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, UTCOffset offset)
113 /** Set our UTC offset to be according to the local time zone */
115 LocalTime::set_local_time_zone ()
117 auto const utc_now = boost::posix_time::second_clock::universal_time ();
118 auto const now = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local (utc_now);
119 auto offset = now - utc_now;
121 _offset = { static_cast<int>(offset.hours()), static_cast<int>(offset.minutes()) };
125 LocalTime::LocalTime (string s)
127 /* 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 */
128 /* 0123456789012345678 or 01234567890123456789012 or 0123456789012345678901234 or 01234567890123456789012345678 */
130 if (s.length() < 19) {
131 throw TimeFormatError (s);
134 bool with_millisecond = false;
135 bool with_tz = false;
137 switch (s.length ()) {
141 with_millisecond = true;
147 with_millisecond = with_tz = true;
150 throw TimeFormatError (s);
153 int const tz_pos = with_millisecond ? 23 : 19;
155 /* Check incidental characters */
156 if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
157 throw TimeFormatError (s);
159 if (with_millisecond && s[19] != '.') {
160 throw TimeFormatError (s);
162 if (with_tz && s[tz_pos] != '+' && s[tz_pos] != '-') {
163 throw TimeFormatError (s);
166 _year = lexical_cast<int>(s.substr(0, 4));
167 _month = lexical_cast<int>(s.substr(5, 2));
168 _day = lexical_cast<int>(s.substr(8, 2));
169 _hour = lexical_cast<int>(s.substr(11, 2));
170 _minute = lexical_cast<int>(s.substr(14, 2));
171 _second = lexical_cast<int>(s.substr(17, 2));
172 _millisecond = with_millisecond ? lexical_cast<int>(s.substr(20, 3)) : 0;
174 _offset.set_hour(with_tz ? lexical_cast<int>(s.substr(tz_pos + 1, 2)) : 0);
175 _offset.set_minute(with_tz ? lexical_cast<int>(s.substr(tz_pos + 4, 2)) : 0);
177 if (with_tz && s[tz_pos] == '-') {
178 _offset.set_hour(-_offset.hour());
179 _offset.set_minute(-_offset.minute());
185 LocalTime::as_string (bool with_millisecond) const
189 buffer, sizeof (buffer),
191 date().c_str(), time_of_day(true, with_millisecond).c_str(), (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
198 LocalTime::date () const
201 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
207 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
210 DCP_ASSERT(!(with_millisecond && !with_second));
211 if (with_millisecond) {
212 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
213 } else if (with_second) {
214 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
216 snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
223 LocalTime::add_days (int days)
225 using namespace boost;
227 gregorian::date d (_year, _month, _day);
229 d += gregorian::days (days);
231 d -= gregorian::days (-days);
234 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
239 LocalTime::add(boost::posix_time::time_duration duration)
241 using namespace boost;
243 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
250 LocalTime::add_months (int m)
252 using namespace boost;
254 gregorian::date d (_year, _month, _day);
256 d += gregorian::months (m);
258 d -= gregorian::months (-m);
261 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
266 LocalTime::add_minutes (int m)
268 add(boost::posix_time::time_duration(0, m, 0));
273 LocalTime::operator== (LocalTime const & other) const
276 auto b = other.as_utc();
278 return a.year() == b.year() && a.month() == b.month() && a.day() == b.day() &&
279 a.hour() == b.hour() && a.minute() == b.minute() && a.second() == b.second() && a.millisecond() == b.millisecond();
284 LocalTime::operator< (LocalTime const & other) const
287 auto b = other.as_utc();
289 if (a.year() != b.year()) {
290 return a.year() < b.year();
292 if (a.month() != b.month()) {
293 return a.month() < b.month();
295 if (a.day() != b.day()) {
296 return a.day() < b.day();
298 if (a.hour() != b.hour()) {
299 return a.hour() < b.hour();
301 if (a.minute() != b.minute()) {
302 return a.minute() < other.minute();
304 if (a.second() != b.second()) {
305 return a.second() < b.second();
307 return a.millisecond() < b.millisecond();
312 LocalTime::operator>(LocalTime const & other) const
315 auto b = other.as_utc();
317 if (a.year() != b.year()) {
318 return a.year() > b.year();
320 if (a.month() != b.month()) {
321 return a.month() > b.month();
323 if (a.day() != b.day()) {
324 return a.day() > b.day();
326 if (a.hour() != b.hour()) {
327 return a.hour() > b.hour();
329 if (a.minute() != b.minute()) {
330 return a.minute() > b.minute();
332 if (a.second() != b.second()) {
333 return a.second() > b.second();
335 return a.millisecond() > b.millisecond();
340 LocalTime::operator!= (LocalTime const & other) const
342 return !(*this == other);
347 dcp::operator<< (ostream& s, LocalTime const & t)
355 LocalTime::from_asn1_utc_time (string time)
358 sscanf(time.c_str(), "%2d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
373 LocalTime::from_asn1_generalized_time (string time)
376 sscanf(time.c_str(), "%4d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
386 LocalTime::as_utc() const
389 t.add(boost::posix_time::time_duration(-_offset.hour(), -_offset.minute(), 0));