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[.frac][TZ]
128 * Where .frac is fractional seconds
129 * TZ is something like +04:00
132 if (s.length() < 19) {
133 throw TimeFormatError (s);
136 /* Date and time with whole seconds */
138 if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
139 throw TimeFormatError(s);
142 _year = lexical_cast<int>(s.substr(0, 4));
143 _month = lexical_cast<int>(s.substr(5, 2));
144 _day = lexical_cast<int>(s.substr(8, 2));
145 _hour = lexical_cast<int>(s.substr(11, 2));
146 _minute = lexical_cast<int>(s.substr(14, 2));
147 _second = lexical_cast<int>(s.substr(17, 2));
151 /* Fractional seconds */
152 if (s.length() > pos && s[pos] == '.') {
153 auto end = s.find('+', pos);
154 if (end == std::string::npos) {
155 end = s.find('-', pos);
157 if (end == std::string::npos) {
160 auto const length = end - pos;
161 _millisecond = lexical_cast<int>(s.substr(pos + 1, std::min(static_cast<size_t>(3), length - 1)));
168 if (pos != s.length()) {
169 if (s[pos] != '+' && s[pos] != '-') {
170 throw TimeFormatError(s);
172 if ((s.length() - pos) != 6) {
173 throw TimeFormatError(s);
176 _offset.set_hour(lexical_cast<int>(s.substr(pos + 1, 2)));
177 _offset.set_minute(lexical_cast<int>(s.substr(pos + 4, 2)));
180 _offset.set_hour(-_offset.hour());
181 _offset.set_minute(-_offset.minute());
185 _offset.set_minute(0);
191 LocalTime::as_string(bool with_millisecond, bool with_timezone) const
195 auto const written = snprintf(
196 buffer, sizeof (buffer),
198 date().c_str(), time_of_day(true, with_millisecond).c_str()
201 DCP_ASSERT(written < 32);
205 buffer + written, sizeof(buffer) - written,
206 "%s%02d:%02d", (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
214 LocalTime::date () const
217 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
223 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
226 DCP_ASSERT(!(with_millisecond && !with_second));
227 if (with_millisecond) {
228 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
229 } else if (with_second) {
230 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
232 snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
239 LocalTime::add_days (int days)
241 using namespace boost;
243 gregorian::date d (_year, _month, _day);
245 d += gregorian::days (days);
247 d -= gregorian::days (-days);
250 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
255 LocalTime::add(boost::posix_time::time_duration duration)
257 using namespace boost;
259 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
266 LocalTime::add_months (int m)
268 using namespace boost;
270 gregorian::date d (_year, _month, _day);
272 d += gregorian::months (m);
274 d -= gregorian::months (-m);
277 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
282 LocalTime::add_minutes (int m)
284 add(boost::posix_time::time_duration(0, m, 0));
289 LocalTime::operator== (LocalTime const & other) const
292 auto b = other.as_utc();
294 return a.year() == b.year() && a.month() == b.month() && a.day() == b.day() &&
295 a.hour() == b.hour() && a.minute() == b.minute() && a.second() == b.second() && a.millisecond() == b.millisecond();
300 LocalTime::operator< (LocalTime const & other) const
303 auto b = other.as_utc();
305 if (a.year() != b.year()) {
306 return a.year() < b.year();
308 if (a.month() != b.month()) {
309 return a.month() < b.month();
311 if (a.day() != b.day()) {
312 return a.day() < b.day();
314 if (a.hour() != b.hour()) {
315 return a.hour() < b.hour();
317 if (a.minute() != b.minute()) {
318 return a.minute() < other.minute();
320 if (a.second() != b.second()) {
321 return a.second() < b.second();
323 return a.millisecond() < b.millisecond();
328 LocalTime::operator<=(LocalTime const& other) const
330 return *this < other || *this == other;
336 LocalTime::operator>(LocalTime const & other) const
339 auto b = other.as_utc();
341 if (a.year() != b.year()) {
342 return a.year() > b.year();
344 if (a.month() != b.month()) {
345 return a.month() > b.month();
347 if (a.day() != b.day()) {
348 return a.day() > b.day();
350 if (a.hour() != b.hour()) {
351 return a.hour() > b.hour();
353 if (a.minute() != b.minute()) {
354 return a.minute() > b.minute();
356 if (a.second() != b.second()) {
357 return a.second() > b.second();
359 return a.millisecond() > b.millisecond();
364 LocalTime::operator>=(LocalTime const& other) const
366 return *this > other || *this == other;
371 LocalTime::operator!= (LocalTime const & other) const
373 return !(*this == other);
378 dcp::operator<< (ostream& s, LocalTime const & t)
386 LocalTime::from_asn1_utc_time (string time)
389 sscanf(time.c_str(), "%2d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
404 LocalTime::from_asn1_generalized_time (string time)
407 sscanf(time.c_str(), "%4d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
417 LocalTime::as_utc() const
420 t.add(boost::posix_time::time_duration(-_offset.hour(), -_offset.minute(), 0));