44 #include "boost/format.hpp"
45 #include "boost/regex.hpp"
50 namespace dafBase = lsst::daf::base;
51 namespace pexEx = lsst::pex::exceptions;
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;
59 static double const NSEC_PER_DAY = 86.4e12;
62 static long long const LL_NSEC_PER_SEC = 1000000000
LL;
70 static double const MAX_DAYS = 106751.99;
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;
80 static double const TT_MINUS_TAI_NSECS = 32184000000
LL;
86 static std::string leapString =
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\
143 class LeapTable :
public std::vector<Leap> {
148 LeapTable leapSecTable;
150 LeapTable::LeapTable(
void) {
158 template<
typename NsType>
159 NsType utcToTai(NsType nsecs) {
161 for (i = 0; i < leapSecTable.size(); ++i) {
162 if (nsecs < leapSecTable[i].whenUtc)
break;
166 lsst::pex::exceptions::DomainError,
168 "DateTime value too early for UTC-TAI conversion: %1%"
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;
182 template<
typename NsType>
183 NsType taiToUtc(NsType nsecs) {
185 for (i = 0; i < leapSecTable.size(); ++i) {
186 if (nsecs < leapSecTable[i].whenTai)
break;
190 lsst::pex::exceptions::DomainError,
192 "DateTime value too early for TAI-UTC conversion: %1%"
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;
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;
216 double calendarToJd(
int year,
int month,
int day,
int hour,
int min,
double sec) {
221 int a = int(year/100);
222 int b = 2 - a + int(a/4);
224 int yy = 1582, mm = 10;
225 if (year < yy || (year == yy && month < mm) || (year == yy && month == mm && day <= 4)) {
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;
248 if (mjd > EPOCH_IN_MJD + MAX_DAYS) {
250 lsst::pex::exceptions::DomainError,
251 (
boost::format(
"MJD too far in the future: %1%") % mjd).str());
253 if (mjd < EPOCH_IN_MJD - MAX_DAYS) {
255 lsst::pex::exceptions::DomainError,
256 (
boost::format(
"MJD too far in the past: %1%") % mjd).str());
258 _nsecs =
static_cast<long long>((mjd - EPOCH_IN_MJD) * NSEC_PER_DAY);
261 }
else if (scale ==
TT) {
262 _nsecs -= TT_MINUS_TAI_NSECS;
273 setNsecsFromMjd(jd - MJD_TO_JD, scale);
282 setNsecsFromMjd(365.25*(epoch - 2000.0) + JD2000 - MJD_TO_JD, scale);
294 }
else if (scale ==
TT) {
295 _nsecs -= TT_MINUS_TAI_NSECS;
307 setNsecsFromMjd(date, scale);
310 setNsecsFromJd(date, scale);
313 setNsecsFromEpoch(date, scale);
316 throw LSST_EXCEPT(pexEx::InvalidParameterError,
"DateSystem must be MJD, JD, or EPOCH.");
337 tm.tm_year = year - 1900;
338 tm.tm_mon = month - 1;
352 time_t secs = timegm(&tm);
362 lsst::pex::exceptions::DomainError,
363 (
boost::format(
"Unconvertible date: %04d-%02d-%02dT%02d:%02d:%02d")
364 % year % month % day % hr % min % sec).str());
367 _nsecs = secs * LL_NSEC_PER_SEC;
369 _nsecs = utcToTai(_nsecs);
370 }
else if (scale == TT) {
371 _nsecs -= TT_MINUS_TAI_NSECS;
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);
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()),
395 if (matches[7].matched) {
396 std::string frac = matches.str(8);
397 int places = frac.size();
401 int value = atoi(frac.c_str());
422 return _getMjd(scale);
425 return _getJd(scale);
428 return _getEpoch(scale);
432 "DateSystem must be MJD, JD, or EPOCH.");
444 }
else if (scale == TT) {
445 return _nsecs + TT_MINUS_TAI_NSECS;
447 return taiToUtc(_nsecs);
460 nsecs =
static_cast<double>(_nsecs);
461 }
else if (scale == TT) {
462 nsecs =
static_cast<double>(_nsecs) + TT_MINUS_TAI_NSECS;
464 nsecs =
static_cast<double>(taiToUtc(_nsecs));
466 return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
475 return _getMjd(scale) + MJD_TO_JD;
483 return 2000.0 + (_getJd(scale) - JD2000)/365.25;
494 long long nsecs = taiToUtc(_nsecs);
496 long long frac = nsecs % LL_NSEC_PER_SEC;
497 if (nsecs < 0 && frac < 0) {
498 nsecs -= LL_NSEC_PER_SEC + frac;
503 time_t secs =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
504 gmtime_r(&secs, &gmt);
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);
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);
534 struct tm gmt(this->gmtime());
535 long long nsecs = taiToUtc(_nsecs) % LL_NSEC_PER_SEC;
537 nsecs += LL_NSEC_PER_SEC;
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();
548 return _nsecs == rhs.
_nsecs;
556 int ret = gettimeofday(&tv, 0);
558 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
559 "Unable to get current time");
561 long long nsecs = tv.tv_sec * LL_NSEC_PER_SEC + tv.tv_usec * 1000
LL;
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);
static DateTime now(void)
double _getEpoch(Timescale scale) const
void setNsecsFromEpoch(double epoch, Timescale scale)
a function to convert epoch to internal nsecs
Class for handling dates/times, including MJD, UTC, and TAI.
double _getJd(Timescale scale) const
bool operator==(DateTime const &rhs) const
void setNsecsFromJd(double jd, Timescale scale)
a function to convert JD to internal nsecs
void setNsecsFromMjd(double mjd, Timescale scale)
a function to convert MJD to interal nsecs
static void initializeLeapSeconds(std::string const &leapString)
double get(DateSystem system=MJD, Timescale scale=TAI) const
long long nsecs(Timescale scale=TAI) const
long long _nsecs
Nanoseconds since Unix epoch.
Interface for DateTime class.
#define LSST_EXCEPT(type,...)
double _getMjd(Timescale scale) const
struct tm gmtime(void) const
afw::table::Key< double > b
struct timespec timespec(void) const
std::string toString(void) const
DateTime(long long nsecs=0LL, Timescale scale=TAI)
Include files required for standard LSST Exception handling.
struct timeval timeval(void) const