2 Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 /** @file src/local_time.cc
36 * @brief LocalTime class
40 #include "local_time.h"
41 #include "exceptions.h"
42 #include "dcp_assert.h"
43 #include <boost/lexical_cast.hpp>
44 #include <boost/date_time/posix_time/posix_time.hpp>
45 #include <boost/date_time/c_local_time_adjustor.hpp>
46 #include <boost/date_time/gregorian/gregorian.hpp>
52 using boost::lexical_cast;
56 LocalTime::LocalTime ()
59 auto tm = localtime (&now);
61 set_local_time_zone ();
65 LocalTime::LocalTime (struct tm t)
68 set_local_time_zone ();
73 LocalTime::set (struct tm const * tm)
75 _year = tm->tm_year + 1900;
76 _month = tm->tm_mon + 1;
85 LocalTime::LocalTime (boost::posix_time::ptime t)
88 set_local_time_zone ();
93 LocalTime::set (boost::posix_time::ptime t)
95 _year = t.date().year ();
96 _month = t.date().month ();
97 _day = t.date().day ();
98 _hour = t.time_of_day().hours ();
99 _minute = t.time_of_day().minutes ();
100 _second = t.time_of_day().seconds ();
101 _millisecond = t.time_of_day().fractional_seconds () / 1000;
102 DCP_ASSERT (_millisecond < 1000);
106 LocalTime::LocalTime (boost::posix_time::ptime t, int tz_hour, int tz_minute)
110 _tz_minute = tz_minute;
114 /** Set our UTC offset to be according to the local time zone */
116 LocalTime::set_local_time_zone ()
118 auto const utc_now = boost::posix_time::second_clock::universal_time ();
119 auto const now = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local (utc_now);
120 auto offset = now - utc_now;
122 _tz_hour = offset.hours ();
123 _tz_minute = offset.minutes ();
127 LocalTime::LocalTime (string s)
129 /* 2013-01-05T18:06:59 or 2013-01-05T18:06:59.123 or 2013-01-05T18:06:59+04:00 or 2013-01-05T18:06:59.123+04:00 */
130 /* 0123456789012345678 or 01234567890123456789012 or 0123456789012345678901234 or 01234567890123456789012345678 */
132 if (s.length() < 19) {
133 throw TimeFormatError (s);
136 bool with_millisecond = false;
137 bool with_tz = false;
139 switch (s.length ()) {
143 with_millisecond = true;
149 with_millisecond = with_tz = true;
152 throw TimeFormatError (s);
155 int const tz_pos = with_millisecond ? 23 : 19;
157 /* Check incidental characters */
158 if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
159 throw TimeFormatError (s);
161 if (with_millisecond && s[19] != '.') {
162 throw TimeFormatError (s);
164 if (with_tz && s[tz_pos] != '+' && s[tz_pos] != '-') {
165 throw TimeFormatError (s);
168 _year = lexical_cast<int>(s.substr(0, 4));
169 _month = lexical_cast<int>(s.substr(5, 2));
170 _day = lexical_cast<int>(s.substr(8, 2));
171 _hour = lexical_cast<int>(s.substr(11, 2));
172 _minute = lexical_cast<int>(s.substr(14, 2));
173 _second = lexical_cast<int>(s.substr(17, 2));
174 _millisecond = with_millisecond ? lexical_cast<int>(s.substr(20, 3)) : 0;
175 _tz_hour = with_tz ? lexical_cast<int>(s.substr(tz_pos + 1, 2)) : 0;
176 _tz_minute = with_tz ? lexical_cast<int>(s.substr(tz_pos + 4, 2)) : 0;
178 if (with_tz && s[tz_pos] == '-') {
179 _tz_hour = -_tz_hour;
180 _tz_minute = -_tz_minute;
186 LocalTime::as_string (bool with_millisecond) const
190 buffer, sizeof (buffer),
192 date().c_str(), time_of_day(true, with_millisecond).c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), abs(_tz_minute)
199 LocalTime::date () const
202 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
208 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
211 DCP_ASSERT(!(with_millisecond && !with_second));
212 if (with_millisecond) {
213 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
214 } else if (with_second) {
215 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
217 snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
224 LocalTime::add_days (int days)
226 using namespace boost;
228 gregorian::date d (_year, _month, _day);
230 d += gregorian::days (days);
232 d -= gregorian::days (-days);
235 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
240 LocalTime::add_months (int m)
242 using namespace boost;
244 gregorian::date d (_year, _month, _day);
246 d += gregorian::months (m);
248 d -= gregorian::months (-m);
251 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
256 LocalTime::add_minutes (int m)
258 using namespace boost;
260 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
261 t += posix_time::time_duration(0, m, 0);
267 LocalTime::operator== (LocalTime const & other) const
269 return _year == other._year && _month == other._month && _day == other._day &&
270 _hour == other._hour && _second == other._second && _millisecond == other._millisecond &&
271 _tz_hour == other._tz_hour && _tz_minute == other._tz_minute;
276 LocalTime::operator< (LocalTime const & other) const
278 if (_year != other._year) {
279 return _year < other._year;
281 if (_month != other._month) {
282 return _month < other._month;
284 if (_day != other._day) {
285 return _day < other._day;
287 if (_hour != other._hour) {
288 return _hour < other._hour;
290 if (_second != other._second) {
291 return _second < other._second;
293 return _millisecond < other._millisecond;
298 LocalTime::operator!= (LocalTime const & other) const
300 return !(*this == other);
305 dcp::operator<< (ostream& s, LocalTime const & t)