2 Copyright (C) 2014-2019 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/c_local_time_adjustor.hpp>
47 using boost::lexical_cast;
50 /** Construct a LocalTime from the current time */
51 LocalTime::LocalTime ()
53 time_t now = time (0);
54 struct tm* tm = localtime (&now);
58 LocalTime::LocalTime (struct tm t)
64 LocalTime::set (struct tm const * tm)
66 _year = tm->tm_year + 1900;
67 _month = tm->tm_mon + 1;
74 set_local_time_zone ();
77 /** Construct a LocalTime from a boost::posix_time::ptime using the local
80 LocalTime::LocalTime (boost::posix_time::ptime t)
82 _year = t.date().year ();
83 _month = t.date().month ();
84 _day = t.date().day ();
85 _hour = t.time_of_day().hours ();
86 _minute = t.time_of_day().minutes ();
87 _second = t.time_of_day().seconds ();
88 _millisecond = t.time_of_day().fractional_seconds () / 1000;
89 DCP_ASSERT (_millisecond < 1000);
91 set_local_time_zone ();
94 /** Construct a LocalTime from a boost::posix_time::ptime and a time zone offset.
95 * @param tz_minute Offset from UTC in minutes; if the timezone is behind UTC this may be negative,
96 * e.g. -04:30 would have tz_hour=-1 and tz_minute=-30.
98 LocalTime::LocalTime (boost::posix_time::ptime t, int tz_hour, int tz_minute)
100 _year = t.date().year ();
101 _month = t.date().month ();
102 _day = t.date().day ();
103 _hour = t.time_of_day().hours ();
104 _minute = t.time_of_day().minutes ();
105 _second = t.time_of_day().seconds ();
106 _millisecond = t.time_of_day().fractional_seconds () / 1000;
107 DCP_ASSERT (_millisecond < 1000);
110 _tz_minute = tz_minute;
113 /** Set our UTC offset to be according to the local time zone */
115 LocalTime::set_local_time_zone ()
117 boost::posix_time::ptime const utc_now = boost::posix_time::second_clock::universal_time ();
118 boost::posix_time::ptime const now = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local (utc_now);
119 boost::posix_time::time_duration offset = now - utc_now;
121 _tz_hour = offset.hours ();
122 _tz_minute = offset.minutes ();
125 /** @param s A string of the form 2013-01-05T18:06:59[.123][+04:00] */
126 LocalTime::LocalTime (string s)
128 /* 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 */
129 /* 0123456789012345678 or 01234567890123456789012 or 0123456789012345678901234 or 01234567890123456789012345678 */
131 if (s.length() < 19) {
132 throw TimeFormatError (s);
135 bool with_millisecond = false;
136 bool with_tz = false;
138 switch (s.length ()) {
142 with_millisecond = true;
148 with_millisecond = with_tz = true;
151 throw TimeFormatError (s);
154 int const tz_pos = with_millisecond ? 23 : 19;
156 /* Check incidental characters */
157 if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
158 throw TimeFormatError (s);
160 if (with_millisecond && s[19] != '.') {
161 throw TimeFormatError (s);
163 if (with_tz && s[tz_pos] != '+' && s[tz_pos] != '-') {
164 throw TimeFormatError (s);
167 _year = lexical_cast<int> (s.substr (0, 4));
168 _month = lexical_cast<int> (s.substr (5, 2));
169 _day = lexical_cast<int> (s.substr (8, 2));
170 _hour = lexical_cast<int> (s.substr (11, 2));
171 _minute = lexical_cast<int> (s.substr (14, 2));
172 _second = lexical_cast<int> (s.substr (17, 2));
173 _millisecond = with_millisecond ? lexical_cast<int> (s.substr (20, 3)) : 0;
174 _tz_hour = with_tz ? lexical_cast<int> (s.substr (tz_pos + 1, 2)) : 0;
175 _tz_minute = with_tz ? lexical_cast<int> (s.substr (tz_pos + 4, 2)) : 0;
177 if (with_tz && s[tz_pos] == '-') {
178 _tz_hour = -_tz_hour;
179 _tz_minute = -_tz_minute;
183 /** @return A string of the form 2013-01-05T18:06:59+04:00 or 2013-01-05T18:06:59.123+04:00 */
185 LocalTime::as_string (bool with_millisecond) const
189 buffer, sizeof (buffer),
191 date().c_str(), time_of_day(true, with_millisecond).c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), abs(_tz_minute)
196 /** @return The date in the form YYYY-MM-DD */
198 LocalTime::date () const
201 snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
205 /** @return The time in the form HH:MM:SS or HH:MM:SS.mmm */
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);
222 LocalTime::add_months (int m)
231 while (_month > 11) {
238 LocalTime::operator== (LocalTime const & other) const
240 return _year == other._year && _month == other._month && _day == other._day &&
241 _hour == other._hour && _second == other._second && _millisecond == other._millisecond &&
242 _tz_hour == other._tz_hour && _tz_minute == other._tz_minute;
246 LocalTime::operator< (LocalTime const & other) const
248 if (_year != other._year) {
249 return _year < other._year;
251 if (_month != other._month) {
252 return _month < other._month;
254 if (_day != other._day) {
255 return _day < other._day;
257 if (_hour != other._hour) {
258 return _hour < other._hour;
260 if (_second != other._second) {
261 return _second < other._second;
263 return _millisecond < other._millisecond;
267 LocalTime::operator!= (LocalTime const & other) const
269 return !(*this == other);
273 dcp::operator<< (ostream& s, LocalTime const & t)