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, UTCOffset offset)
107 {
108  set (t);
109  _offset = offset;
110 }
111 
112 
114 void
116 {
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;
120 
121  _offset = { static_cast<int>(offset.hours()), static_cast<int>(offset.minutes()) };
122 }
123 
124 
126 {
127  /* 2013-01-05T18:06:59[.frac][TZ]
128  * Where .frac is fractional seconds
129  * TZ is something like +04:00
130  */
131 
132  if (s.length() < 19) {
133  throw TimeFormatError (s);
134  }
135 
136  /* Date and time with whole seconds */
137 
138  if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
139  throw TimeFormatError(s);
140  }
141 
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));
148 
149  size_t pos = 19;
150 
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);
156  }
157  if (end == std::string::npos) {
158  end = s.length();
159  }
160  auto const length = end - pos;
161  _millisecond = lexical_cast<int>(s.substr(pos + 1, std::min(static_cast<size_t>(3), length - 1)));
162  pos = end;
163  } else {
164  _millisecond = 0;
165  }
166 
167  /* Timezone */
168  if (pos != s.length() && s[pos] != 'Z') {
169  if (s[pos] != '+' && s[pos] != '-') {
170  throw TimeFormatError(s);
171  }
172  if ((s.length() - pos) != 6) {
173  throw TimeFormatError(s);
174  }
175 
176  _offset.set_hour(lexical_cast<int>(s.substr(pos + 1, 2)));
177  _offset.set_minute(lexical_cast<int>(s.substr(pos + 4, 2)));
178 
179  if (s[pos] == '-') {
180  _offset.set_hour(-_offset.hour());
181  _offset.set_minute(-_offset.minute());
182  }
183  }
184 }
185 
186 
187 string
188 LocalTime::as_string(bool with_millisecond, bool with_timezone) const
189 {
190  char buffer[32];
191 
192  auto const written = snprintf(
193  buffer, sizeof (buffer),
194  "%sT%s",
195  date().c_str(), time_of_day(true, with_millisecond).c_str()
196  );
197 
198  DCP_ASSERT(written < 32);
199 
200  if (with_timezone) {
201  snprintf(
202  buffer + written, sizeof(buffer) - written,
203  "%s%02d:%02d", (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
204  );
205  }
206  return buffer;
207 }
208 
209 
210 string
212 {
213  char buffer[32];
214  snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
215  return buffer;
216 }
217 
218 
219 string
220 LocalTime::time_of_day (bool with_second, bool with_millisecond) const
221 {
222  char buffer[32];
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);
228  } else {
229  snprintf (buffer, sizeof (buffer), "%02d:%02d", _hour, _minute);
230  }
231  return buffer;
232 }
233 
234 
235 void
236 LocalTime::add_days (int days)
237 {
238  using namespace boost;
239 
240  gregorian::date d (_year, _month, _day);
241  if (days > 0) {
242  d += gregorian::days (days);
243  } else {
244  d -= gregorian::days (-days);
245  }
246 
247  set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
248 }
249 
250 
251 void
252 LocalTime::add(boost::posix_time::time_duration duration)
253 {
254  using namespace boost;
255 
256  posix_time::ptime t(gregorian::date(_year, _month, _day), posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000));
257  t += duration;
258  set (t);
259 }
260 
261 
262 void
263 LocalTime::add_months (int m)
264 {
265  using namespace boost;
266 
267  gregorian::date d (_year, _month, _day);
268  if (m > 0) {
269  d += gregorian::months (m);
270  } else {
271  d -= gregorian::months (-m);
272  }
273 
274  set (posix_time::ptime(d, posix_time::time_duration(_hour, _minute, _second, _millisecond * 1000)));
275 }
276 
277 
278 void
279 LocalTime::add_minutes (int m)
280 {
281  add(boost::posix_time::time_duration(0, m, 0));
282 }
283 
284 
285 bool
286 LocalTime::operator== (LocalTime const & other) const
287 {
288  auto a = as_utc();
289  auto b = other.as_utc();
290 
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();
293 }
294 
295 
296 bool
297 LocalTime::operator< (LocalTime const & other) const
298 {
299  auto a = as_utc();
300  auto b = other.as_utc();
301 
302  if (a.year() != b.year()) {
303  return a.year() < b.year();
304  }
305  if (a.month() != b.month()) {
306  return a.month() < b.month();
307  }
308  if (a.day() != b.day()) {
309  return a.day() < b.day();
310  }
311  if (a.hour() != b.hour()) {
312  return a.hour() < b.hour();
313  }
314  if (a.minute() != b.minute()) {
315  return a.minute() < other.minute();
316  }
317  if (a.second() != b.second()) {
318  return a.second() < b.second();
319  }
320  return a.millisecond() < b.millisecond();
321 }
322 
323 
324 bool
325 LocalTime::operator<=(LocalTime const& other) const
326 {
327  return *this < other || *this == other;
328 }
329 
330 
331 
332 bool
333 LocalTime::operator>(LocalTime const & other) const
334 {
335  auto a = as_utc();
336  auto b = other.as_utc();
337 
338  if (a.year() != b.year()) {
339  return a.year() > b.year();
340  }
341  if (a.month() != b.month()) {
342  return a.month() > b.month();
343  }
344  if (a.day() != b.day()) {
345  return a.day() > b.day();
346  }
347  if (a.hour() != b.hour()) {
348  return a.hour() > b.hour();
349  }
350  if (a.minute() != b.minute()) {
351  return a.minute() > b.minute();
352  }
353  if (a.second() != b.second()) {
354  return a.second() > b.second();
355  }
356  return a.millisecond() > b.millisecond();
357 }
358 
359 
360 bool
361 LocalTime::operator>=(LocalTime const& other) const
362 {
363  return *this > other || *this == other;
364 }
365 
366 
367 bool
368 LocalTime::operator!= (LocalTime const & other) const
369 {
370  return !(*this == other);
371 }
372 
373 
374 ostream&
375 dcp::operator<< (ostream& s, LocalTime const & t)
376 {
377  s << t.as_string ();
378  return s;
379 }
380 
381 
382 LocalTime
383 LocalTime::from_asn1_utc_time (string time)
384 {
385  LocalTime t;
386  sscanf(time.c_str(), "%2d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
387 
388  if (t._year < 70) {
389  t._year += 100;
390  }
391  t._year += 1900;
392 
393  t._millisecond = 0;
394  t._offset = {};
395 
396  return t;
397 }
398 
399 
400 LocalTime
401 LocalTime::from_asn1_generalized_time (string time)
402 {
403  LocalTime t;
404  sscanf(time.c_str(), "%4d%2d%2d%2d%2d%2d", &t._year, &t._month, &t._day, &t._hour, &t._minute, &t._second);
405 
406  t._millisecond = 0;
407  t._offset = {};
408 
409  return t;
410 }
411 
412 
413 LocalTime
414 LocalTime::as_utc() const
415 {
416  auto t = *this;
417  t.add(boost::posix_time::time_duration(-_offset.hour(), -_offset.minute(), 0));
418  return t;
419 }
420 
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
Definition: local_time.h:68
std::string date() const
Definition: local_time.cc:211
int _day
day number of the month (1-31)
Definition: local_time.h:177
int _hour
hour number of the day (0-23)
Definition: local_time.h:178
int _millisecond
millisecond number of the second (0-999)
Definition: local_time.h:181
std::string time_of_day(bool with_second, bool with_millisecond) const
Definition: local_time.cc:220
std::string as_string(bool with_millisecond=false, bool with_timezone=true) const
Definition: local_time.cc:188
void set_local_time_zone()
Definition: local_time.cc:115
int _second
second number of the minute (0-59)
Definition: local_time.h:180
int _month
month number of the year (1-12)
Definition: local_time.h:176
int _year
year
Definition: local_time.h:175
int _minute
minute number of the hour (0-59)
Definition: local_time.h:179
A an error with a string passed to LocalTime.
Definition: exceptions.h:213
DCP_ASSERT macro.
Exceptions thrown by libdcp.
LocalTime class.
Namespace for everything in libdcp.
Definition: array_data.h:50