LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
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 ";
128 
129 // Anonymous namespace for structures, classes, and formerly file-static
130 // functions.
131 namespace {
132 
134 struct Leap {
135  long long whenUtc;
136  long long whenTai;
137  double offset;
138  double mjdRef;
139  double drift;
140 };
141 
142 class LeapTable : public std::vector<Leap> {
143 public:
144  LeapTable(void);
145 };
146 
147 LeapTable leapSecTable;
148 
149 LeapTable::LeapTable(void) {
151 }
152 
157 template<typename NsType>
158 NsType utcToTai(NsType nsecs) {
159  size_t i;
160  for (i = 0; i < leapSecTable.size(); ++i) {
161  if (nsecs < leapSecTable[i].whenUtc) break;
162  }
163  if (i == 0) {
164  throw LSST_EXCEPT(
165  lsst::pex::exceptions::DomainError,
166  (boost::format(
167  "DateTime value too early for UTC-TAI conversion: %1%"
168  ) % nsecs).str());
169  }
170  Leap const& l(leapSecTable[i - 1]);
171  double mjd = static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
172  double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
173  NsType leapNSecs = static_cast<NsType>(leapSecs * 1.0e9 + 0.5);
174  return nsecs + leapNSecs;
175 }
176 
181 template<typename NsType>
182 NsType taiToUtc(NsType nsecs) {
183  size_t i;
184  for (i = 0; i < leapSecTable.size(); ++i) {
185  if (nsecs < leapSecTable[i].whenTai) break;
186  }
187  if (i == 0) {
188  throw LSST_EXCEPT(
189  lsst::pex::exceptions::DomainError,
190  (boost::format(
191  "DateTime value too early for TAI-UTC conversion: %1%"
192  ) % nsecs).str());
193  }
194  Leap const& l(leapSecTable[i - 1]);
195  double mjd = static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
196  double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
197  // Correct for TAI MJD vs. UTC MJD.
198  leapSecs /= 1.0 + l.drift * 1.0e9 / NSEC_PER_DAY;
199  NsType leapNSecs = static_cast<NsType>(leapSecs * 1.0e9 + 0.5);
200  return nsecs - leapNSecs;
201 }
202 
203 
204 #ifdef CAL_TO_JD
205 
215 double calendarToJd(int year, int month, int day, int hour, int min, double sec) {
216  if ( month <= 2 ) {
217  year -= 1;
218  month += 12;
219  }
220  int a = int(year/100);
221  int b = 2 - a + int(a/4);
222 
223  int yy = 1582, mm = 10; //, d = 4;
224  if (year < yy || (year == yy && month < mm) || (year == yy && month == mm && day <= 4)) {
225  b = 0;
226  }
227 
228  double jd = static_cast<int>(365.25*(year + 4716)) +
229  static_cast<int>(30.6001*(month + 1)) + day + b - 1524.5;
230  jd += hour/HOURS_PER_DAY + min/MIN_PER_DAY + sec/SEC_PER_DAY;
231 
232  return jd;
233 }
234 
235 #endif // CAL_TO_JD
236 } // end anonymous namespace
237 
238 
246 
247  if (mjd > EPOCH_IN_MJD + MAX_DAYS) {
248  throw LSST_EXCEPT(
249  lsst::pex::exceptions::DomainError,
250  (boost::format("MJD too far in the future: %1%") % mjd).str());
251  }
252  if (mjd < EPOCH_IN_MJD - MAX_DAYS) {
253  throw LSST_EXCEPT(
254  lsst::pex::exceptions::DomainError,
255  (boost::format("MJD too far in the past: %1%") % mjd).str());
256  }
257  _nsecs = static_cast<long long>((mjd - EPOCH_IN_MJD) * NSEC_PER_DAY);
258  if (scale == UTC) {
259  _nsecs = utcToTai(_nsecs);
260  } else if (scale == TT) {
261  _nsecs -= TT_MINUS_TAI_NSECS;
262  }
263 
264 }
265 
272  setNsecsFromMjd(jd - MJD_TO_JD, scale);
273 }
274 
281  setNsecsFromMjd(365.25*(epoch - 2000.0) + JD2000 - MJD_TO_JD, scale);
282 }
283 
284 
285 
290 dafBase::DateTime::DateTime(long long nsecs, Timescale scale) : _nsecs(nsecs) {
291  if (scale == UTC) {
292  _nsecs = utcToTai(_nsecs);
293  } else if (scale == TT) {
294  _nsecs -= TT_MINUS_TAI_NSECS;
295  }
296 }
297 
304  switch (system) {
305  case MJD:
306  setNsecsFromMjd(date, scale);
307  break;
308  case JD:
309  setNsecsFromJd(date, scale);
310  break;
311  case EPOCH:
312  setNsecsFromEpoch(date, scale);
313  break;
314  default:
315  throw LSST_EXCEPT(pexEx::InvalidParameterError, "DateSystem must be MJD, JD, or EPOCH.");
316  break;
317  }
318 }
319 
320 
321 
331 dafBase::DateTime::DateTime(int year, int month, int day,
332  int hr, int min, int sec, Timescale scale) {
333 
334 
335  struct tm tm;
336  tm.tm_year = year - 1900;
337  tm.tm_mon = month - 1;
338  tm.tm_mday = day;
339  tm.tm_hour = hr;
340  tm.tm_min = min;
341  tm.tm_sec = sec;
342  tm.tm_wday = 0;
343  tm.tm_yday = 0;
344  tm.tm_isdst = 0;
345  tm.tm_gmtoff = 0;
346 
347  // Convert to seconds since the epoch, correcting to UTC.
348  // Although timegm() is non-standard, it is a commonly-supported
349  // extension and is much safer/more reliable than mktime(3) in that
350  // it doesn't try to deal with the anomalies of local time zones.
351  time_t secs = timegm(&tm);
352 
353  // long long nsecs will blow out beyond sep 21, 1677 0:00:00, and apr 12 2262 00:00:00
354  // (refering to the values of EPOCH_IN_MJD +/- MAX_DAYS ... exceeds 64 bits.)
355  // However, a tm struct is only 32 bits, and saturates at:
356  // low end - Dec 13 1901, 20:45:52
357  // hi end - Jan 19 2038, 03:14:07
358 
359  if (secs == -1) {
360  throw LSST_EXCEPT(
361  lsst::pex::exceptions::DomainError,
362  (boost::format("Unconvertible date: %04d-%02d-%02dT%02d:%02d:%02d")
363  % year % month % day % hr % min % sec).str());
364  }
365 
366  _nsecs = secs * LL_NSEC_PER_SEC;
367  if (scale == UTC) {
368  _nsecs = utcToTai(_nsecs);
369  } else if (scale == TT) {
370  _nsecs -= TT_MINUS_TAI_NSECS;
371  }
372 
373 }
374 
381 dafBase::DateTime::DateTime(std::string const& iso8601) {
382  boost::regex re("(\\d{4})-?(\\d{2})-?(\\d{2})" "T"
383  "(\\d{2}):?(\\d{2}):?(\\d{2})" "([.,](\\d*))?" "Z");
384  boost::smatch matches;
385  if (!regex_match(iso8601, matches, re)) {
386  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
387  "Not in acceptable ISO8601 format: " + iso8601);
388  }
389  DateTime dt(atoi(matches.str(1).c_str()), atoi(matches.str(2).c_str()),
390  atoi(matches.str(3).c_str()), atoi(matches.str(4).c_str()),
391  atoi(matches.str(5).c_str()), atoi(matches.str(6).c_str()),
392  UTC);
393  _nsecs = dt._nsecs;
394  if (matches[7].matched) {
395  std::string frac = matches.str(8);
396  int places = frac.size();
397  if (places > 9) { // truncate fractional nanosec
398  frac.erase(9);
399  }
400  int value = atoi(frac.c_str());
401  while (places < 9) {
402  value *= 10;
403  ++places;
404  }
405  _nsecs += value;
406  }
407 }
408 
409 
419  switch (system) {
420  case MJD:
421  return _getMjd(scale);
422  break;
423  case JD:
424  return _getJd(scale);
425  break;
426  case EPOCH:
427  return _getEpoch(scale);
428  break;
429  default:
430  throw LSST_EXCEPT(pexEx::InvalidParameterError,
431  "DateSystem must be MJD, JD, or EPOCH.");
432  break;
433  }
434 }
435 
436 
441  if (scale == TAI) {
442  return _nsecs;
443  } else if (scale == TT) {
444  return _nsecs + TT_MINUS_TAI_NSECS;
445  } else {
446  return taiToUtc(_nsecs);
447  }
448 }
449 
450 
456 
457  double nsecs;
458  if (scale == TAI) {
459  nsecs = static_cast<double>(_nsecs);
460  } else if (scale == TT) {
461  nsecs = static_cast<double>(_nsecs) + TT_MINUS_TAI_NSECS;
462  } else {
463  nsecs = static_cast<double>(taiToUtc(_nsecs));
464  }
465  return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
466 }
467 
468 
474  return _getMjd(scale) + MJD_TO_JD;
475 }
476 
482  return 2000.0 + (_getJd(scale) - JD2000)/365.25;
483 }
484 
485 
486 
487 
491 struct tm dafBase::DateTime::gmtime(void) const {
492  struct tm gmt;
493  long long nsecs = taiToUtc(_nsecs);
494  // Round to negative infinity
495  long long frac = nsecs % LL_NSEC_PER_SEC;
496  if (nsecs < 0 && frac < 0) {
497  nsecs -= LL_NSEC_PER_SEC + frac;
498  }
499  else {
500  nsecs -= frac;
501  }
502  time_t secs = static_cast<time_t>(nsecs / LL_NSEC_PER_SEC);
503  gmtime_r(&secs, &gmt);
504  return gmt;
505 }
506 
510 struct timespec dafBase::DateTime::timespec(void) const {
511  struct timespec ts;
512  long long nsecs = taiToUtc(_nsecs);
513  ts.tv_sec = static_cast<time_t>(nsecs / LL_NSEC_PER_SEC);
514  ts.tv_nsec = static_cast<int>(nsecs % LL_NSEC_PER_SEC);
515  return ts;
516 }
517 
521 struct timeval dafBase::DateTime::timeval(void) const {
522  struct timeval tv;
523  long long nsecs = taiToUtc(_nsecs);
524  tv.tv_sec = static_cast<time_t>(nsecs / LL_NSEC_PER_SEC);
525  tv.tv_usec = static_cast<int>((nsecs % LL_NSEC_PER_SEC) / 1000);
526  return tv;
527 }
528 
532 std::string dafBase::DateTime::toString(void) const {
533  struct tm gmt(this->gmtime());
534  long long nsecs = taiToUtc(_nsecs) % LL_NSEC_PER_SEC;
535  if (nsecs < 0) {
536  nsecs += LL_NSEC_PER_SEC;
537  }
538  return (boost::format("%04d-%02d-%02dT%02d:%02d:%02d.%09dZ") %
539  (gmt.tm_year + 1900) % (gmt.tm_mon + 1) % gmt.tm_mday %
540  gmt.tm_hour % gmt.tm_min % gmt.tm_sec % nsecs).str();
541 }
542 
546 bool dafBase::DateTime::operator==(DateTime const& rhs) const {
547  return _nsecs == rhs._nsecs;
548 }
549 
554  struct timeval tv;
555  int ret = gettimeofday(&tv, 0);
556  if (ret != 0) {
557  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
558  "Unable to get current time");
559  }
560  long long nsecs = tv.tv_sec * LL_NSEC_PER_SEC + tv.tv_usec * 1000LL;
561  return DateTime(nsecs, DateTime::UTC);
562 }
563 
567 void dafBase::DateTime::initializeLeapSeconds(std::string const& leapString) {
568  Leap l;
569  leapSecTable.clear();
570  boost::regex re("^\\d{4}.*?=JD\\s*([\\d.]+)\\s+TAI-UTC=\\s+([\\d.]+)\\s+S"
571  " \\+ \\(MJD - ([\\d.]+)\\) X ([\\d.]+)\\s*S$");
572  for (boost::cregex_iterator i = make_regex_iterator(leapString.c_str(), re);
573  i != boost::cregex_iterator(); ++i) {
574  double mjdUtc = strtod((*i)[1].first, 0) - MJD_TO_JD;
575  l.offset = strtod((*i)[2].first, 0);
576  l.mjdRef = strtod((*i)[3].first, 0);
577  l.drift = strtod((*i)[4].first, 0);
578  l.whenUtc = static_cast<long long>(
579  (mjdUtc - EPOCH_IN_MJD) * NSEC_PER_DAY);
580  l.whenTai = l.whenUtc + static_cast<long long>(
581  1.0e9 * (l.offset + (mjdUtc - l.mjdRef) * l.drift));
582  leapSecTable.push_back(l);
583  }
584 }
Class for handling dates/times, including MJD, UTC, and TAI.
Definition: DateTime.h:58
struct timeval timeval(void) const
Definition: DateTime.cc:521
long long _nsecs
Nanoseconds since Unix epoch.
Definition: DateTime.h:86
static DateTime now(void)
Definition: DateTime.cc:553
static void initializeLeapSeconds(std::string const &leapString)
Definition: DateTime.cc:567
double min
Definition: attributes.cc:216
void setNsecsFromJd(double jd, Timescale scale)
a function to convert JD to internal nsecs
Definition: DateTime.cc:271
void setNsecsFromEpoch(double epoch, Timescale scale)
a function to convert epoch to internal nsecs
Definition: DateTime.cc:280
std::string toString(void) const
Definition: DateTime.cc:532
double get(DateSystem system=MJD, Timescale scale=TAI) const
Definition: DateTime.cc:418
DateTime(long long nsecs=0LL, Timescale scale=TAI)
Definition: DateTime.cc:290
double _getJd(Timescale scale) const
Definition: DateTime.cc:473
long long nsecs(Timescale scale=TAI) const
Definition: DateTime.cc:440
Interface for DateTime class.
struct tm gmtime(void) const
Definition: DateTime.cc:491
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
double _getMjd(Timescale scale) const
Definition: DateTime.cc:455
struct timespec timespec(void) const
Definition: DateTime.cc:510
bool operator==(DateTime const &rhs) const
Definition: DateTime.cc:546
afw::table::Key< double > b
void setNsecsFromMjd(double mjd, Timescale scale)
a function to convert MJD to interal nsecs
Definition: DateTime.cc:245
double _getEpoch(Timescale scale) const
Definition: DateTime.cc:481
Include files required for standard LSST Exception handling.