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() && s[pos] != 'Z') {
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());
188 LocalTime::as_string(bool with_millisecond, bool with_timezone) const
192 auto const written = snprintf(
193 buffer, sizeof (buffer),
195 date().c_str(), time_of_day(true, with_millisecond).c_str()
198 DCP_ASSERT(written < 32);
202 buffer + written, sizeof(buffer) - written,
203 "%s%02d:%02d", (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
211 LocalTime::date () const
214 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
220 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
223 DCP_ASSERT(!(with_millisecond && !with_second));
224 if (with_millisecond) {
225 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
226 } else if (with_second) {
227 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
229 snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
236 LocalTime::add_days (int days)
238 using namespace boost;
240 gregorian::date d (_year, _month, _day);
242 d += gregorian::days (days);
244 d -= gregorian::days (-days);
247 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
252 LocalTime::add(boost::posix_time::time_duration duration)
254 using namespace boost;
256 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
263 LocalTime::add_months (int m)
265 using namespace boost;
267 gregorian::date d (_year, _month, _day);
269 d += gregorian::months (m);
271 d -= gregorian::months (-m);
274 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
279 LocalTime::add_minutes (int m)
281 add(boost::posix_time::time_duration(0, m, 0));
286 LocalTime::operator== (LocalTime const & other) const
289 auto b = other.as_utc();
291 return a.year() == b.year() && a.month() == b.month() && a.day() == b.day() &&
292 a.hour() == b.hour() && a.minute() == b.minute() && a.second() == b.second() && a.millisecond() == b.millisecond();
297 LocalTime::operator< (LocalTime const & other) const
300 auto b = other.as_utc();
302 if (a.year() != b.year()) {
303 return a.year() < b.year();
305 if (a.month() != b.month()) {
306 return a.month() < b.month();
308 if (a.day() != b.day()) {
309 return a.day() < b.day();
311 if (a.hour() != b.hour()) {
312 return a.hour() < b.hour();
314 if (a.minute() != b.minute()) {
315 return a.minute() < other.minute();
317 if (a.second() != b.second()) {
318 return a.second() < b.second();
320 return a.millisecond() < b.millisecond();
325 LocalTime::operator<=(LocalTime const& other) const
327 return *this < other || *this == other;
333 LocalTime::operator>(LocalTime const & other) const
336 auto b = other.as_utc();
338 if (a.year() != b.year()) {
339 return a.year() > b.year();
341 if (a.month() != b.month()) {
342 return a.month() > b.month();
344 if (a.day() != b.day()) {
345 return a.day() > b.day();
347 if (a.hour() != b.hour()) {
348 return a.hour() > b.hour();
350 if (a.minute() != b.minute()) {
351 return a.minute() > b.minute();
353 if (a.second() != b.second()) {
354 return a.second() > b.second();
356 return a.millisecond() > b.millisecond();
361 LocalTime::operator>=(LocalTime const& other) const
363 return *this > other || *this == other;
368 LocalTime::operator!= (LocalTime const & other) const
370 return !(*this == other);
375 dcp::operator<< (ostream& s, LocalTime const & t)
383 LocalTime::from_asn1_utc_time (string time)
386 sscanf(time.c_str(), "%2d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
401 LocalTime::from_asn1_generalized_time (string time)
404 sscanf(time.c_str(), "%4d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
414 LocalTime::as_utc() const
417 t.add(boost::posix_time::time_duration(-_offset.hour(), -_offset.minute(), 0));