2 Copyright (C) 2014-2020 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.
34 /** @file src/local_time.cc
35 * @brief LocalTime class.
38 #include "local_time.h"
39 #include "exceptions.h"
40 #include "dcp_assert.h"
41 #include <boost/lexical_cast.hpp>
42 #include <boost/date_time/posix_time/posix_time.hpp>
43 #include <boost/date_time/c_local_time_adjustor.hpp>
44 #include <boost/date_time/gregorian/gregorian.hpp>
49 using boost::lexical_cast;
52 /** Construct a LocalTime from the current time */
53 LocalTime::LocalTime ()
55 time_t now = time (0);
56 struct tm* tm = localtime (&now);
58 set_local_time_zone ();
61 LocalTime::LocalTime (struct tm t)
64 set_local_time_zone ();
68 LocalTime::set (struct tm const * tm)
70 _year = tm->tm_year + 1900;
71 _month = tm->tm_mon + 1;
79 /** Construct a LocalTime from a boost::posix_time::ptime using the local
82 LocalTime::LocalTime (boost::posix_time::ptime t)
85 set_local_time_zone ();
89 LocalTime::set (boost::posix_time::ptime t)
91 _year = t.date().year ();
92 _month = t.date().month ();
93 _day = t.date().day ();
94 _hour = t.time_of_day().hours ();
95 _minute = t.time_of_day().minutes ();
96 _second = t.time_of_day().seconds ();
97 _millisecond = t.time_of_day().fractional_seconds () / 1000;
98 DCP_ASSERT (_millisecond < 1000);
101 /** Construct a LocalTime from a boost::posix_time::ptime and a time zone offset.
102 * @param tz_minute Offset from UTC in minutes; if the timezone is behind UTC this may be negative,
103 * e.g. -04:30 would have tz_hour=-1 and tz_minute=-30.
105 LocalTime::LocalTime (boost::posix_time::ptime t, int tz_hour, int tz_minute)
109 _tz_minute = tz_minute;
112 /** Set our UTC offset to be according to the local time zone */
114 LocalTime::set_local_time_zone ()
116 boost::posix_time::ptime const utc_now = boost::posix_time::second_clock::universal_time ();
117 boost::posix_time::ptime const now = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local (utc_now);
118 boost::posix_time::time_duration offset = now - utc_now;
120 _tz_hour = offset.hours ();
121 _tz_minute = offset.minutes ();
124 /** @param s A string of the form 2013-01-05T18:06:59[.123][+04:00] */
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;
173 _tz_hour = with_tz ? lexical_cast<int> (s.substr (tz_pos + 1, 2)) : 0;
174 _tz_minute = with_tz ? lexical_cast<int> (s.substr (tz_pos + 4, 2)) : 0;
176 if (with_tz && s[tz_pos] == '-') {
177 _tz_hour = -_tz_hour;
178 _tz_minute = -_tz_minute;
182 /** @return A string of the form 2013-01-05T18:06:59+04:00 or 2013-01-05T18:06:59.123+04:00 */
184 LocalTime::as_string (bool with_millisecond) const
188 buffer, sizeof (buffer),
190 date().c_str(), time_of_day(true, with_millisecond).c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), abs(_tz_minute)
195 /** @return The date in the form YYYY-MM-DD */
197 LocalTime::date () const
200 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
204 /** @return The time in the form HH:MM:SS or HH:MM:SS.mmm */
206 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
209 DCP_ASSERT(!(with_millisecond && !with_second));
210 if (with_millisecond) {
211 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
212 } else if (with_second) {
213 snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
215 snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
221 LocalTime::add_months (int m)
223 using namespace boost;
225 gregorian::date d (_year, _month, _day);
227 d += gregorian::months (m);
229 d -= gregorian::months (-m);
232 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
236 LocalTime::add_minutes (int m)
238 using namespace boost;
240 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second));
241 t += posix_time::time_duration(0, m, 0);
246 LocalTime::operator== (LocalTime const & other) const
248 return _year == other._year && _month == other._month && _day == other._day &&
249 _hour == other._hour && _second == other._second && _millisecond == other._millisecond &&
250 _tz_hour == other._tz_hour && _tz_minute == other._tz_minute;
254 LocalTime::operator< (LocalTime const & other) const
256 if (_year != other._year) {
257 return _year < other._year;
259 if (_month != other._month) {
260 return _month < other._month;
262 if (_day != other._day) {
263 return _day < other._day;
265 if (_hour != other._hour) {
266 return _hour < other._hour;
268 if (_second != other._second) {
269 return _second < other._second;
271 return _millisecond < other._millisecond;
275 LocalTime::operator!= (LocalTime const & other) const
277 return !(*this == other);
281 dcp::operator<< (ostream& s, LocalTime const & t)