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);
222 LocalTime::add_days (int days)
224 using namespace boost;
226 gregorian::date d (_year, _month, _day);
228 d += gregorian::days (days);
230 d -= gregorian::days (-days);
233 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
238 LocalTime::add_months (int m)
240 using namespace boost;
242 gregorian::date d (_year, _month, _day);
244 d += gregorian::months (m);
246 d -= gregorian::months (-m);
249 set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
253 LocalTime::add_minutes (int m)
255 using namespace boost;
257 posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
258 t += posix_time::time_duration(0, m, 0);
263 LocalTime::operator== (LocalTime const & other) const
265 return _year == other._year && _month == other._month && _day == other._day &&
266 _hour == other._hour && _second == other._second && _millisecond == other._millisecond &&
267 _tz_hour == other._tz_hour && _tz_minute == other._tz_minute;
271 LocalTime::operator< (LocalTime const & other) const
273 if (_year != other._year) {
274 return _year < other._year;
276 if (_month != other._month) {
277 return _month < other._month;
279 if (_day != other._day) {
280 return _day < other._day;
282 if (_hour != other._hour) {
283 return _hour < other._hour;
285 if (_second != other._second) {
286 return _second < other._second;
288 return _millisecond < other._millisecond;
292 LocalTime::operator!= (LocalTime const & other) const
294 return !(*this == other);
298 dcp::operator<< (ostream& s, LocalTime const & t)