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, bool with_timezone) const
189 auto const written = snprintf(
190 buffer, sizeof (buffer),
192 date().c_str(), time_of_day(true, with_millisecond).c_str()
195 DCP_ASSERT(written < 32);
199 buffer + written, sizeof(buffer) - written,
200 "%s%02d:%02d", (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
208 LocalTime::date () const
211 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
217 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
220 DCP_ASSERT(!(with_millisecond && !with_second));
221 if (with_millisecond) {
222 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
223 } else if (with_second) {
224 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
226 snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
233 LocalTime::add_days (int days)
235 using namespace boost;
237 gregorian::date d (_year, _month, _day);
239 d += gregorian::days (days);
241 d -= gregorian::days (-days);
244 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
249 LocalTime::add(boost::posix_time::time_duration duration)
251 using namespace boost;
253 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
260 LocalTime::add_months (int m)
262 using namespace boost;
264 gregorian::date d (_year, _month, _day);
266 d += gregorian::months (m);
268 d -= gregorian::months (-m);
271 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
276 LocalTime::add_minutes (int m)
278 add(boost::posix_time::time_duration(0, m, 0));
283 LocalTime::operator== (LocalTime const & other) const
286 auto b = other.as_utc();
288 return a.year() == b.year() && a.month() == b.month() && a.day() == b.day() &&
289 a.hour() == b.hour() && a.minute() == b.minute() && a.second() == b.second() && a.millisecond() == b.millisecond();
294 LocalTime::operator< (LocalTime const & other) const
297 auto b = other.as_utc();
299 if (a.year() != b.year()) {
300 return a.year() < b.year();
302 if (a.month() != b.month()) {
303 return a.month() < b.month();
305 if (a.day() != b.day()) {
306 return a.day() < b.day();
308 if (a.hour() != b.hour()) {
309 return a.hour() < b.hour();
311 if (a.minute() != b.minute()) {
312 return a.minute() < other.minute();
314 if (a.second() != b.second()) {
315 return a.second() < b.second();
317 return a.millisecond() < b.millisecond();
322 LocalTime::operator<=(LocalTime const& other) const
324 return *this < other || *this == other;
330 LocalTime::operator>(LocalTime const & other) const
333 auto b = other.as_utc();
335 if (a.year() != b.year()) {
336 return a.year() > b.year();
338 if (a.month() != b.month()) {
339 return a.month() > b.month();
341 if (a.day() != b.day()) {
342 return a.day() > b.day();
344 if (a.hour() != b.hour()) {
345 return a.hour() > b.hour();
347 if (a.minute() != b.minute()) {
348 return a.minute() > b.minute();
350 if (a.second() != b.second()) {
351 return a.second() > b.second();
353 return a.millisecond() > b.millisecond();
358 LocalTime::operator>=(LocalTime const& other) const
360 return *this > other || *this == other;
365 LocalTime::operator!= (LocalTime const & other) const
367 return !(*this == other);
372 dcp::operator<< (ostream& s, LocalTime const & t)
380 LocalTime::from_asn1_utc_time (string time)
383 sscanf(time.c_str(), "%2d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
398 LocalTime::from_asn1_generalized_time (string time)
401 sscanf(time.c_str(), "%4d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
411 LocalTime::as_utc() const
414 t.add(boost::posix_time::time_duration(-_offset.hour(), -_offset.minute(), 0));