libdcp
local_time.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
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.
10 
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.
15 
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/>.
18 
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
23  including the two.
24 
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.
32 */
33 
34 
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>
47 #include <cstdio>
48 
49 
50 using std::string;
51 using std::ostream;
52 using boost::lexical_cast;
53 using namespace dcp;
54 
55 
57 {
58  auto now = time (0);
59  auto tm = localtime (&now);
60  set (tm);
62 }
63 
64 
65 LocalTime::LocalTime (struct tm t)
66 {
67  set (&t);
69 }
70 
71 
72 void
73 LocalTime::set (struct tm const * tm)
74 {
75  _year = tm->tm_year + 1900;
76  _month = tm->tm_mon + 1;
77  _day = tm->tm_mday;
78  _hour = tm->tm_hour;
79  _minute = tm->tm_min;
80  _second = tm->tm_sec;
81  _millisecond = 0;
82 }
83 
84 
85 LocalTime::LocalTime (boost::posix_time::ptime t)
86 {
87  set (t);
89 }
90 
91 
92 void
93 LocalTime::set (boost::posix_time::ptime t)
94 {
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);
103 }
104 
105 
106 LocalTime::LocalTime (boost::posix_time::ptime t, int tz_hour, int tz_minute)
107 {
108  set (t);
109  _tz_hour = tz_hour;
110  _tz_minute = tz_minute;
111 }
112 
113 
115 void
117 {
118  auto const utc_now = boost::posix_time::second_clock::universal_time ();
119  auto const now = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local (utc_now);
120  auto offset = now - utc_now;
121 
122  _tz_hour = offset.hours ();
123  _tz_minute = offset.minutes ();
124 }
125 
126 
128 {
129  /* 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 */
130  /* 0123456789012345678 or 01234567890123456789012 or 0123456789012345678901234 or 01234567890123456789012345678 */
131 
132  if (s.length() < 19) {
133  throw TimeFormatError (s);
134  }
135 
136  bool with_millisecond = false;
137  bool with_tz = false;
138 
139  switch (s.length ()) {
140  case 19:
141  break;
142  case 23:
143  with_millisecond = true;
144  break;
145  case 25:
146  with_tz = true;
147  break;
148  case 29:
149  with_millisecond = with_tz = true;
150  break;
151  default:
152  throw TimeFormatError (s);
153  }
154 
155  int const tz_pos = with_millisecond ? 23 : 19;
156 
157  /* Check incidental characters */
158  if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
159  throw TimeFormatError (s);
160  }
161  if (with_millisecond && s[19] != '.') {
162  throw TimeFormatError (s);
163  }
164  if (with_tz && s[tz_pos] != '+' && s[tz_pos] != '-') {
165  throw TimeFormatError (s);
166  }
167 
168  _year = lexical_cast<int>(s.substr(0, 4));
169  _month = lexical_cast<int>(s.substr(5, 2));
170  _day = lexical_cast<int>(s.substr(8, 2));
171  _hour = lexical_cast<int>(s.substr(11, 2));
172  _minute = lexical_cast<int>(s.substr(14, 2));
173  _second = lexical_cast<int>(s.substr(17, 2));
174  _millisecond = with_millisecond ? lexical_cast<int>(s.substr(20, 3)) : 0;
175  _tz_hour = with_tz ? lexical_cast<int>(s.substr(tz_pos + 1, 2)) : 0;
176  _tz_minute = with_tz ? lexical_cast<int>(s.substr(tz_pos + 4, 2)) : 0;
177 
178  if (with_tz && s[tz_pos] == '-') {
179  _tz_hour = -_tz_hour;
181  }
182 }
183 
184 
185 string
186 LocalTime::as_string (bool with_millisecond) const
187 {
188  char buffer[32];
189  snprintf (
190  buffer, sizeof (buffer),
191  "%sT%s%s%02d:%02d",
192  date().c_str(), time_of_day(true, with_millisecond).c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), abs(_tz_minute)
193  );
194  return buffer;
195 }
196 
197 
198 string
200 {
201  char buffer[32];
202  snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
203  return buffer;
204 }
205 
206 
207 string
208 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
209 {
210  char buffer[32];
211  DCP_ASSERT(!(with_millisecond && !with_second));
212  if (with_millisecond) {
213  snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d.%03d", _hour, _minute, _second, _millisecond);
214  } else if (with_second) {
215  snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
216  } else {
217  snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
218  }
219  return buffer;
220 }
221 
222 
223 void
224 LocalTime::add_days (int days)
225 {
226  using namespace boost;
227 
228  gregorian::date d (_year, _month, _day);
229  if (days > 0) {
230  d += gregorian::days (days);
231  } else {
232  d -= gregorian::days (-days);
233  }
234 
235  set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
236 }
237 
238 
239 void
240 LocalTime::add_months (int m)
241 {
242  using namespace boost;
243 
244  gregorian::date d (_year, _month, _day);
245  if (m > 0) {
246  d += gregorian::months (m);
247  } else {
248  d -= gregorian::months (-m);
249  }
250 
251  set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
252 }
253 
254 
255 void
256 LocalTime::add_minutes (int m)
257 {
258  using namespace boost;
259 
260  posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
261  t += posix_time::time_duration(0, m, 0);
262  set (t);
263 }
264 
265 
266 bool
267 LocalTime::operator== (LocalTime const & other) const
268 {
269  return _year == other._year && _month == other._month && _day == other._day &&
270  _hour == other._hour && _second == other._second && _millisecond == other._millisecond &&
271  _tz_hour == other._tz_hour && _tz_minute == other._tz_minute;
272 }
273 
274 
275 bool
276 LocalTime::operator< (LocalTime const & other) const
277 {
278  if (_year != other._year) {
279  return _year < other._year;
280  }
281  if (_month != other._month) {
282  return _month < other._month;
283  }
284  if (_day != other._day) {
285  return _day < other._day;
286  }
287  if (_hour != other._hour) {
288  return _hour < other._hour;
289  }
290  if (_second != other._second) {
291  return _second < other._second;
292  }
293  return _millisecond < other._millisecond;
294 }
295 
296 
297 bool
298 LocalTime::operator!= (LocalTime const & other) const
299 {
300  return !(*this == other);
301 }
302 
303 
304 ostream&
305 dcp::operator<< (ostream& s, LocalTime const & t)
306 {
307  s << t.as_string ();
308  return s;
309 }
310 
311 
312 LocalTime
313 LocalTime::from_asn1_utc_time (string time)
314 {
315  LocalTime t;
316  sscanf(time.c_str(), "%2d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
317 
318  if (t._year < 70) {
319  t._year += 100;
320  }
321  t._year += 1900;
322 
323  t._tz_hour = t._tz_minute = t._millisecond = 0;
324 
325  return t;
326 }
327 
328 
329 LocalTime
330 LocalTime::from_asn1_generalized_time (string time)
331 {
332  LocalTime t;
333  sscanf(time.c_str(), "%4d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
334 
335  t._tz_hour = t._tz_minute = t._millisecond = 0;
336 
337  return t;
338 }
339 
340 
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
Definition: local_time.h:64
std::string as_string(bool with_millisecond=false) const
Definition: local_time.cc:186
std::string date() const
Definition: local_time.cc:199
int _day
day number of the month (1-31)
Definition: local_time.h:143
int _hour
hour number of the day (0-23)
Definition: local_time.h:144
int _tz_hour
hours by which this time is offset from UTC; can be negative
Definition: local_time.h:149
int _millisecond
millisecond number of the second (0-999)
Definition: local_time.h:147
std::string time_of_day(bool with_second, bool with_millisecond) const
Definition: local_time.cc:208
void set_local_time_zone()
Definition: local_time.cc:116
int _second
second number of the minute (0-59)
Definition: local_time.h:146
int _month
month number of the year (1-12)
Definition: local_time.h:142
int _year
year
Definition: local_time.h:141
int _minute
minute number of the hour (0-59)
Definition: local_time.h:145
A an error with a string passed to LocalTime.
Definition: exceptions.h:186
DCP_ASSERT macro.
Exceptions thrown by libdcp.
LocalTime class.
Namespace for everything in libdcp.
Definition: array_data.h:50