LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
DateTime.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
25 
26 
39 #include <limits>
40 #include <cmath>
41 
42 #include "lsst/daf/base/DateTime.h"
43 
44 #include "boost/format.hpp"
45 #include "boost/regex.hpp"
46 #include <vector>
47 
48 #include "lsst/pex/exceptions.h"
49 
50 namespace dafBase = lsst::daf::base;
51 namespace pexEx = lsst::pex::exceptions;
52 
53 // Epoch = 1970 JAN 1 00:00:00 = JD 2440587.5 = MJD 40587.0
54 static double const MJD_TO_JD = 2400000.5;
55 static double const EPOCH_IN_MJD = 40587.0;
56 static double const JD2000 = 2451544.50;
57 
59 static double const NSEC_PER_DAY = 86.4e12;
60 
62 static long long const LL_NSEC_PER_SEC = 1000000000LL;
63 // static long long const LL_NSEC_PER_DAY = 86400 * LL_NSEC_PER_SEC;
64 
65 // Maximum number of days expressible as signed 64-bit nanoseconds.
66 // 2^64 / 2 / 1e9 / 86400
67 // NOTE: long long nsecs will wrap:
68 // -- earliest date representable = sep 21, 1677 00:00:00
69 // -- latest date representable = apr 12, 2262 00:00:00
70 static double const MAX_DAYS = 106751.99;
71 
72 
73 #ifdef CAL_TO_JD
74 static double const HOURS_PER_DAY = 24.0;
75 static double const MIN_PER_DAY = 1440.0;
76 static double const SEC_PER_DAY = 86400.0;
77 #endif
78 
79 // Difference between Terrestrial Time and TAI.
80 static double const TT_MINUS_TAI_NSECS = 32184000000LL;
81 
82 /* Leap second table as string.
83  *
84  * Source: http://maia.usno.navy.mil/ser7/tai-utc.dat
85  */
86 static std::string leapString =
87 "\
88 1961 JAN 1 =JD 2437300.5 TAI-UTC= 1.4228180 S + (MJD - 37300.) X 0.001296 S\n\
89 1961 AUG 1 =JD 2437512.5 TAI-UTC= 1.3728180 S + (MJD - 37300.) X 0.001296 S\n\
90 1962 JAN 1 =JD 2437665.5 TAI-UTC= 1.8458580 S + (MJD - 37665.) X 0.0011232S\n\
91 1963 NOV 1 =JD 2438334.5 TAI-UTC= 1.9458580 S + (MJD - 37665.) X 0.0011232S\n\
92 1964 JAN 1 =JD 2438395.5 TAI-UTC= 3.2401300 S + (MJD - 38761.) X 0.001296 S\n\
93 1964 APR 1 =JD 2438486.5 TAI-UTC= 3.3401300 S + (MJD - 38761.) X 0.001296 S\n\
94 1964 SEP 1 =JD 2438639.5 TAI-UTC= 3.4401300 S + (MJD - 38761.) X 0.001296 S\n\
95 1965 JAN 1 =JD 2438761.5 TAI-UTC= 3.5401300 S + (MJD - 38761.) X 0.001296 S\n\
96 1965 MAR 1 =JD 2438820.5 TAI-UTC= 3.6401300 S + (MJD - 38761.) X 0.001296 S\n\
97 1965 JUL 1 =JD 2438942.5 TAI-UTC= 3.7401300 S + (MJD - 38761.) X 0.001296 S\n\
98 1965 SEP 1 =JD 2439004.5 TAI-UTC= 3.8401300 S + (MJD - 38761.) X 0.001296 S\n\
99 1966 JAN 1 =JD 2439126.5 TAI-UTC= 4.3131700 S + (MJD - 39126.) X 0.002592 S\n\
100 1968 FEB 1 =JD 2439887.5 TAI-UTC= 4.2131700 S + (MJD - 39126.) X 0.002592 S\n\
101 1972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0 S + (MJD - 41317.) X 0.0 S\n\
102 1972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0 S + (MJD - 41317.) X 0.0 S\n\
103 1973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0 S + (MJD - 41317.) X 0.0 S\n\
104 1974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0 S + (MJD - 41317.) X 0.0 S\n\
105 1975 JAN 1 =JD 2442413.5 TAI-UTC= 14.0 S + (MJD - 41317.) X 0.0 S\n\
106 1976 JAN 1 =JD 2442778.5 TAI-UTC= 15.0 S + (MJD - 41317.) X 0.0 S\n\
107 1977 JAN 1 =JD 2443144.5 TAI-UTC= 16.0 S + (MJD - 41317.) X 0.0 S\n\
108 1978 JAN 1 =JD 2443509.5 TAI-UTC= 17.0 S + (MJD - 41317.) X 0.0 S\n\
109 1979 JAN 1 =JD 2443874.5 TAI-UTC= 18.0 S + (MJD - 41317.) X 0.0 S\n\
110 1980 JAN 1 =JD 2444239.5 TAI-UTC= 19.0 S + (MJD - 41317.) X 0.0 S\n\
111 1981 JUL 1 =JD 2444786.5 TAI-UTC= 20.0 S + (MJD - 41317.) X 0.0 S\n\
112 1982 JUL 1 =JD 2445151.5 TAI-UTC= 21.0 S + (MJD - 41317.) X 0.0 S\n\
113 1983 JUL 1 =JD 2445516.5 TAI-UTC= 22.0 S + (MJD - 41317.) X 0.0 S\n\
114 1985 JUL 1 =JD 2446247.5 TAI-UTC= 23.0 S + (MJD - 41317.) X 0.0 S\n\
115 1988 JAN 1 =JD 2447161.5 TAI-UTC= 24.0 S + (MJD - 41317.) X 0.0 S\n\
116 1990 JAN 1 =JD 2447892.5 TAI-UTC= 25.0 S + (MJD - 41317.) X 0.0 S\n\
117 1991 JAN 1 =JD 2448257.5 TAI-UTC= 26.0 S + (MJD - 41317.) X 0.0 S\n\
118 1992 JUL 1 =JD 2448804.5 TAI-UTC= 27.0 S + (MJD - 41317.) X 0.0 S\n\
119 1993 JUL 1 =JD 2449169.5 TAI-UTC= 28.0 S + (MJD - 41317.) X 0.0 S\n\
120 1994 JUL 1 =JD 2449534.5 TAI-UTC= 29.0 S + (MJD - 41317.) X 0.0 S\n\
121 1996 JAN 1 =JD 2450083.5 TAI-UTC= 30.0 S + (MJD - 41317.) X 0.0 S\n\
122 1997 JUL 1 =JD 2450630.5 TAI-UTC= 31.0 S + (MJD - 41317.) X 0.0 S\n\
123 1999 JAN 1 =JD 2451179.5 TAI-UTC= 32.0 S + (MJD - 41317.) X 0.0 S\n\
124 2006 JAN 1 =JD 2453736.5 TAI-UTC= 33.0 S + (MJD - 41317.) X 0.0 S\n\
125 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S\n\
126 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S\n\
127 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S\n\
128 ";
129 
130 // Anonymous namespace for structures, classes, and formerly file-static
131 // functions.
132 namespace {
133 
135 struct Leap {
136  long long whenUtc;
137  long long whenTai;
138  double offset;
139  double mjdRef;
140  double drift;
141 };
142 
143 class LeapTable : public std::vector<Leap> {
144 public:
145  LeapTable(void);
146 };
147 
148 LeapTable leapSecTable;
149 
150 LeapTable::LeapTable(void) {
152 }
153 
158 template<typename NsType>
159 NsType utcToTai(NsType nsecs) {
160  size_t i;
161  for (i = 0; i < leapSecTable.size(); ++i) {
162  if (nsecs < leapSecTable[i].whenUtc) break;
163  }
164  if (i == 0) {
165  throw LSST_EXCEPT(
166  lsst::pex::exceptions::DomainError,
167  (boost::format(
168  "DateTime value too early for UTC-TAI conversion: %1%"
169  ) % nsecs).str());
170  }
171  Leap const& l(leapSecTable[i - 1]);
172  double mjd = static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
173  double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
174  NsType leapNSecs = static_cast<NsType>(leapSecs * 1.0e9 + 0.5);
175  return nsecs + leapNSecs;
176 }
177 
182 template<typename NsType>
183 NsType taiToUtc(NsType nsecs) {
184  size_t i;
185  for (i = 0; i < leapSecTable.size(); ++i) {
186  if (nsecs < leapSecTable[i].whenTai) break;
187  }
188  if (i == 0) {
189  throw LSST_EXCEPT(
190  lsst::pex::exceptions::DomainError,
191  (boost::format(
192  "DateTime value too early for TAI-UTC conversion: %1%"
193  ) % nsecs).str());
194  }
195  Leap const& l(leapSecTable[i - 1]);
196  double mjd = static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
197  double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
198  // Correct for TAI MJD vs. UTC MJD.
199  leapSecs /= 1.0 + l.drift * 1.0e9 / NSEC_PER_DAY;
200  NsType leapNSecs = static_cast<NsType>(leapSecs * 1.0e9 + 0.5);
201  return nsecs - leapNSecs;
202 }
203 
204 
205 #ifdef CAL_TO_JD
206 
216 double calendarToJd(int year, int month, int day, int hour, int min, double sec) {
217  if ( month <= 2 ) {
218  year -= 1;
219  month += 12;
220  }
221  int a = int(year/100);
222  int b = 2 - a + int(a/4);
223 
224  int yy = 1582, mm = 10; //, d = 4;
225  if (year < yy || (year == yy && month < mm) || (year == yy && month == mm && day <= 4)) {
226  b = 0;
227  }
228 
229  double jd = static_cast<int>(365.25*(year + 4716)) +
230  static_cast<int>(30.6001*(month + 1)) + day + b - 1524.5;
231  jd += hour/HOURS_PER_DAY + min/MIN_PER_DAY + sec/SEC_PER_DAY;
232 
233  return jd;
234 }
235 
236 #endif // CAL_TO_JD
237 } // end anonymous namespace
238 
239 
247 
248  if (mjd > EPOCH_IN_MJD + MAX_DAYS) {
249  throw LSST_EXCEPT(
250  lsst::pex::exceptions::DomainError,
251  (boost::format("MJD too far in the future: %1%") % mjd).str());
252  }
253  if (mjd < EPOCH_IN_MJD - MAX_DAYS) {
254  throw LSST_EXCEPT(
255  lsst::pex::exceptions::DomainError,
256  (boost::format("MJD too far in the past: %1%") % mjd).str());
257  }
258  _nsecs = static_cast<long long>((mjd - EPOCH_IN_MJD) * NSEC_PER_DAY);
259  if (scale == UTC) {
260  _nsecs = utcToTai(_nsecs);
261  } else if (scale == TT) {
262  _nsecs -= TT_MINUS_TAI_NSECS;
263  }
264 
265 }
266 
273  setNsecsFromMjd(jd - MJD_TO_JD, scale);
274 }
275 
282  setNsecsFromMjd(365.25*(epoch - 2000.0) + JD2000 - MJD_TO_JD, scale);
283 }
284 
285 
286 
291 dafBase::DateTime::DateTime(long long nsecs, Timescale scale) : _nsecs(nsecs) {
292  if (scale == UTC) {
293  _nsecs = utcToTai(_nsecs);
294  } else if (scale == TT) {
295  _nsecs -= TT_MINUS_TAI_NSECS;
296  }
297 }
298 
305  switch (system) {
306  case MJD:
307  setNsecsFromMjd(date, scale);
308  break;
309  case JD:
310  setNsecsFromJd(date, scale);
311  break;
312  case EPOCH:
313  setNsecsFromEpoch(date, scale);
314  break;
315  default:
316  throw LSST_EXCEPT(pexEx::InvalidParameterError, "DateSystem must be MJD, JD, or EPOCH.");
317  break;
318  }
319 }
320 
321 
322 
332 dafBase::DateTime::DateTime(int year, int month, int day,
333  int hr, int min, int sec, Timescale scale) {
334 
335 
336  struct tm tm;
337  tm.tm_year = year - 1900;
338  tm.tm_mon = month - 1;
339  tm.tm_mday = day;
340  tm.tm_hour = hr;
341  tm.tm_min = min;
342  tm.tm_sec = sec;
343  tm.tm_wday = 0;
344  tm.tm_yday = 0;
345  tm.tm_isdst = 0;
346  tm.tm_gmtoff = 0;
347 
348  // Convert to seconds since the epoch, correcting to UTC.
349  // Although timegm() is non-standard, it is a commonly-supported
350  // extension and is much safer/more reliable than mktime(3) in that
351  // it doesn't try to deal with the anomalies of local time zones.
352  time_t secs = timegm(&tm);
353 
354  // long long nsecs will blow out beyond sep 21, 1677 0:00:00, and apr 12 2262 00:00:00
355  // (refering to the values of EPOCH_IN_MJD +/- MAX_DAYS ... exceeds 64 bits.)
356  // However, a tm struct is only 32 bits, and saturates at:
357  // low end - Dec 13 1901, 20:45:52
358  // hi end - Jan 19 2038, 03:14:07
359 
360  if (secs == -1) {
361  throw LSST_EXCEPT(
362  lsst::pex::exceptions::DomainError,
363  (boost::format("Unconvertible date: %04d-%02d-%02dT%02d:%02d:%02d")
364  % year % month % day % hr % min % sec).str());
365  }
366 
367  _nsecs = secs * LL_NSEC_PER_SEC;
368  if (scale == UTC) {
369  _nsecs = utcToTai(_nsecs);
370  } else if (scale == TT) {
371  _nsecs -= TT_MINUS_TAI_NSECS;
372  }
373 
374 }
375 
382 dafBase::DateTime::DateTime(std::string const& iso8601) {
383  boost::regex re("(\\d{4})-?(\\d{2})-?(\\d{2})" "T"
384  "(\\d{2}):?(\\d{2}):?(\\d{2})" "([.,](\\d*))?" "Z");
385  boost::smatch matches;
386  if (!regex_match(iso8601, matches, re)) {
387  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
388  "Not in acceptable ISO8601 format: " + iso8601);
389  }
390  DateTime dt(atoi(matches.str(1).c_str()), atoi(matches.str(2).c_str()),
391  atoi(matches.str(3).c_str()), atoi(matches.str(4).c_str()),
392  atoi(matches.str(5).c_str()), atoi(matches.str(6).c_str()),
393  UTC);
394  _nsecs = dt._nsecs;
395  if (matches[7].matched) {
396  std::string frac = matches.str(8);
397  int places = frac.size();
398  if (places > 9) { // truncate fractional nanosec
399  frac.erase(9);
400  }
401  int value = atoi(frac.c_str());
402  while (places < 9) {
403  value *= 10;
404  ++places;
405  }
406  _nsecs += value;
407  }
408 }
409 
410 
420  switch (system) {
421  case MJD:
422  return _getMjd(scale);
423  break;
424  case JD:
425  return _getJd(scale);
426  break;
427  case EPOCH:
428  return _getEpoch(scale);
429  break;
430  default:
431  throw LSST_EXCEPT(pexEx::InvalidParameterError,
432  "DateSystem must be MJD, JD, or EPOCH.");
433  break;
434  }
435 }
436 
437 
442  if (scale == TAI) {
443  return _nsecs;
444  } else if (scale == TT) {
445  return _nsecs + TT_MINUS_TAI_NSECS;
446  } else {
447  return taiToUtc(_nsecs);
448  }
449 }
450 
451 
457 
458  double nsecs;
459  if (scale == TAI) {
460  nsecs = static_cast<double>(_nsecs);
461  } else if (scale == TT) {
462  nsecs = static_cast<double>(_nsecs) + TT_MINUS_TAI_NSECS;
463  } else {
464  nsecs = static_cast<double>(taiToUtc(_nsecs));
465  }
466  return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
467 }
468 
469 
475  return _getMjd(scale) + MJD_TO_JD;
476 }
477 
483  return 2000.0 + (_getJd(scale) - JD2000)/365.25;
484 }
485 
486 
487 
488 
492 struct tm dafBase::DateTime::gmtime(void) const {
493  struct tm gmt;
494  long long nsecs = taiToUtc(_nsecs);
495  // Round to negative infinity
496  long long frac = nsecs % LL_NSEC_PER_SEC;
497  if (nsecs < 0 && frac < 0) {
498  nsecs -= LL_NSEC_PER_SEC + frac;
499  }
500  else {
501  nsecs -= frac;
502  }
503  time_t secs = static_cast<time_t>(nsecs / LL_NSEC_PER_SEC);
504  gmtime_r(&secs, &gmt);
505  return gmt;
506 }
507 
511 struct timespec dafBase::DateTime::timespec(void) const {
512  struct timespec ts;
513  long long nsecs = taiToUtc(_nsecs);
514  ts.tv_sec = static_cast<time_t>(nsecs / LL_NSEC_PER_SEC);
515  ts.tv_nsec = static_cast<int>(nsecs % LL_NSEC_PER_SEC);
516  return ts;
517 }
518 
522 struct timeval dafBase::DateTime::timeval(void) const {
523  struct timeval tv;
524  long long nsecs = taiToUtc(_nsecs);
525  tv.tv_sec = static_cast<time_t>(nsecs / LL_NSEC_PER_SEC);
526  tv.tv_usec = static_cast<int>((nsecs % LL_NSEC_PER_SEC) / 1000);
527  return tv;
528 }
529 
533 std::string dafBase::DateTime::toString(void) const {
534  struct tm gmt(this->gmtime());
535  long long nsecs = taiToUtc(_nsecs) % LL_NSEC_PER_SEC;
536  if (nsecs < 0) {
537  nsecs += LL_NSEC_PER_SEC;
538  }
539  return (boost::format("%04d-%02d-%02dT%02d:%02d:%02d.%09dZ") %
540  (gmt.tm_year + 1900) % (gmt.tm_mon + 1) % gmt.tm_mday %
541  gmt.tm_hour % gmt.tm_min % gmt.tm_sec % nsecs).str();
542 }
543 
547 bool dafBase::DateTime::operator==(DateTime const& rhs) const {
548  return _nsecs == rhs._nsecs;
549 }
550 
555  struct timeval tv;
556  int ret = gettimeofday(&tv, 0);
557  if (ret != 0) {
558  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
559  "Unable to get current time");
560  }
561  long long nsecs = tv.tv_sec * LL_NSEC_PER_SEC + tv.tv_usec * 1000LL;
562  return DateTime(nsecs, DateTime::UTC);
563 }
564 
568 void dafBase::DateTime::initializeLeapSeconds(std::string const& leapString) {
569  Leap l;
570  leapSecTable.clear();
571  boost::regex re("^\\d{4}.*?=JD\\s*([\\d.]+)\\s+TAI-UTC=\\s+([\\d.]+)\\s+S"
572  " \\+ \\(MJD - ([\\d.]+)\\) X ([\\d.]+)\\s*S$");
573  for (boost::cregex_iterator i = make_regex_iterator(leapString.c_str(), re);
574  i != boost::cregex_iterator(); ++i) {
575  double mjdUtc = strtod((*i)[1].first, 0) - MJD_TO_JD;
576  l.offset = strtod((*i)[2].first, 0);
577  l.mjdRef = strtod((*i)[3].first, 0);
578  l.drift = strtod((*i)[4].first, 0);
579  l.whenUtc = static_cast<long long>(
580  (mjdUtc - EPOCH_IN_MJD) * NSEC_PER_DAY);
581  l.whenTai = l.whenUtc + static_cast<long long>(
582  1.0e9 * (l.offset + (mjdUtc - l.mjdRef) * l.drift));
583  leapSecTable.push_back(l);
584  }
585 }
static DateTime now(void)
Definition: DateTime.cc:554
double _getEpoch(Timescale scale) const
Definition: DateTime.cc:482
void setNsecsFromEpoch(double epoch, Timescale scale)
a function to convert epoch to internal nsecs
Definition: DateTime.cc:281
Class for handling dates/times, including MJD, UTC, and TAI.
Definition: DateTime.h:58
double _getJd(Timescale scale) const
Definition: DateTime.cc:474
bool operator==(DateTime const &rhs) const
Definition: DateTime.cc:547
void setNsecsFromJd(double jd, Timescale scale)
a function to convert JD to internal nsecs
Definition: DateTime.cc:272
Include files required for standard LSST Exception handling.
void setNsecsFromMjd(double mjd, Timescale scale)
a function to convert MJD to interal nsecs
Definition: DateTime.cc:246
static void initializeLeapSeconds(std::string const &leapString)
Definition: DateTime.cc:568
double get(DateSystem system=MJD, Timescale scale=TAI) const
Definition: DateTime.cc:419
long long nsecs(Timescale scale=TAI) const
Definition: DateTime.cc:441
long long _nsecs
Nanoseconds since Unix epoch.
Definition: DateTime.h:83
Interface for DateTime class.
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
double _getMjd(Timescale scale) const
Definition: DateTime.cc:456
struct tm gmtime(void) const
Definition: DateTime.cc:492
afw::table::Key< double > b
struct timespec timespec(void) const
Definition: DateTime.cc:511
std::string toString(void) const
Definition: DateTime.cc:533
DateTime(long long nsecs=0LL, Timescale scale=TAI)
Definition: DateTime.cc:291
struct timeval timeval(void) const
Definition: DateTime.cc:522