41 #include "boost/format.hpp"
42 #include "boost/regex.hpp"
47 namespace dafBase = lsst::daf::base;
48 namespace pexEx = lsst::pex::exceptions;
51 static double const MJD_TO_JD = 2400000.5;
52 static double const EPOCH_IN_MJD = 40587.0;
53 static double const JD2000 = 2451544.50;
56 static double const NSEC_PER_DAY = 86.4e12;
59 static long long const LL_NSEC_PER_SEC = 1000000000
LL;
67 static double const MAX_DAYS = 106751.99;
71 static double const HOURS_PER_DAY = 24.0;
72 static double const MIN_PER_DAY = 1440.0;
73 static double const SEC_PER_DAY = 86400.0;
77 static long long const TT_MINUS_TAI_NSECS = 32184000000
LL;
83 static std::string leapString =
85 1961 JAN 1 =JD 2437300.5 TAI-UTC= 1.4228180 S + (MJD - 37300.) X 0.001296 S\n\
86 1961 AUG 1 =JD 2437512.5 TAI-UTC= 1.3728180 S + (MJD - 37300.) X 0.001296 S\n\
87 1962 JAN 1 =JD 2437665.5 TAI-UTC= 1.8458580 S + (MJD - 37665.) X 0.0011232S\n\
88 1963 NOV 1 =JD 2438334.5 TAI-UTC= 1.9458580 S + (MJD - 37665.) X 0.0011232S\n\
89 1964 JAN 1 =JD 2438395.5 TAI-UTC= 3.2401300 S + (MJD - 38761.) X 0.001296 S\n\
90 1964 APR 1 =JD 2438486.5 TAI-UTC= 3.3401300 S + (MJD - 38761.) X 0.001296 S\n\
91 1964 SEP 1 =JD 2438639.5 TAI-UTC= 3.4401300 S + (MJD - 38761.) X 0.001296 S\n\
92 1965 JAN 1 =JD 2438761.5 TAI-UTC= 3.5401300 S + (MJD - 38761.) X 0.001296 S\n\
93 1965 MAR 1 =JD 2438820.5 TAI-UTC= 3.6401300 S + (MJD - 38761.) X 0.001296 S\n\
94 1965 JUL 1 =JD 2438942.5 TAI-UTC= 3.7401300 S + (MJD - 38761.) X 0.001296 S\n\
95 1965 SEP 1 =JD 2439004.5 TAI-UTC= 3.8401300 S + (MJD - 38761.) X 0.001296 S\n\
96 1966 JAN 1 =JD 2439126.5 TAI-UTC= 4.3131700 S + (MJD - 39126.) X 0.002592 S\n\
97 1968 FEB 1 =JD 2439887.5 TAI-UTC= 4.2131700 S + (MJD - 39126.) X 0.002592 S\n\
98 1972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0 S + (MJD - 41317.) X 0.0 S\n\
99 1972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0 S + (MJD - 41317.) X 0.0 S\n\
100 1973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0 S + (MJD - 41317.) X 0.0 S\n\
101 1974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0 S + (MJD - 41317.) X 0.0 S\n\
102 1975 JAN 1 =JD 2442413.5 TAI-UTC= 14.0 S + (MJD - 41317.) X 0.0 S\n\
103 1976 JAN 1 =JD 2442778.5 TAI-UTC= 15.0 S + (MJD - 41317.) X 0.0 S\n\
104 1977 JAN 1 =JD 2443144.5 TAI-UTC= 16.0 S + (MJD - 41317.) X 0.0 S\n\
105 1978 JAN 1 =JD 2443509.5 TAI-UTC= 17.0 S + (MJD - 41317.) X 0.0 S\n\
106 1979 JAN 1 =JD 2443874.5 TAI-UTC= 18.0 S + (MJD - 41317.) X 0.0 S\n\
107 1980 JAN 1 =JD 2444239.5 TAI-UTC= 19.0 S + (MJD - 41317.) X 0.0 S\n\
108 1981 JUL 1 =JD 2444786.5 TAI-UTC= 20.0 S + (MJD - 41317.) X 0.0 S\n\
109 1982 JUL 1 =JD 2445151.5 TAI-UTC= 21.0 S + (MJD - 41317.) X 0.0 S\n\
110 1983 JUL 1 =JD 2445516.5 TAI-UTC= 22.0 S + (MJD - 41317.) X 0.0 S\n\
111 1985 JUL 1 =JD 2446247.5 TAI-UTC= 23.0 S + (MJD - 41317.) X 0.0 S\n\
112 1988 JAN 1 =JD 2447161.5 TAI-UTC= 24.0 S + (MJD - 41317.) X 0.0 S\n\
113 1990 JAN 1 =JD 2447892.5 TAI-UTC= 25.0 S + (MJD - 41317.) X 0.0 S\n\
114 1991 JAN 1 =JD 2448257.5 TAI-UTC= 26.0 S + (MJD - 41317.) X 0.0 S\n\
115 1992 JUL 1 =JD 2448804.5 TAI-UTC= 27.0 S + (MJD - 41317.) X 0.0 S\n\
116 1993 JUL 1 =JD 2449169.5 TAI-UTC= 28.0 S + (MJD - 41317.) X 0.0 S\n\
117 1994 JUL 1 =JD 2449534.5 TAI-UTC= 29.0 S + (MJD - 41317.) X 0.0 S\n\
118 1996 JAN 1 =JD 2450083.5 TAI-UTC= 30.0 S + (MJD - 41317.) X 0.0 S\n\
119 1997 JUL 1 =JD 2450630.5 TAI-UTC= 31.0 S + (MJD - 41317.) X 0.0 S\n\
120 1999 JAN 1 =JD 2451179.5 TAI-UTC= 32.0 S + (MJD - 41317.) X 0.0 S\n\
121 2006 JAN 1 =JD 2453736.5 TAI-UTC= 33.0 S + (MJD - 41317.) X 0.0 S\n\
122 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S\n\
123 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S\n\
124 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S\n\
125 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S\n\
141 class LeapTable :
public std::vector<Leap> {
146 LeapTable leapSecTable;
148 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;
184 template<
typename NsType>
185 NsType taiToUtc(NsType nsecs) {
187 for (i = 0; i < leapSecTable.size(); ++i) {
188 if (nsecs < leapSecTable[i].whenTai)
break;
192 lsst::pex::exceptions::DomainError,
194 "DateTime value too early for TAI-UTC conversion: %1%"
197 Leap
const& l(leapSecTable[i - 1]);
198 double mjd =
static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
199 double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
201 leapSecs /= 1.0 + l.drift * 1.0e9 / NSEC_PER_DAY;
202 NsType leapNSecs = static_cast<NsType>(leapSecs * 1.0e9 + 0.5);
203 return nsecs - leapNSecs;
218 return nsecs - TT_MINUS_TAI_NSECS;
220 return utcToTai(nsecs);
222 std::ostringstream os;
223 os <<
"Unsupported scale " <<
scale;
224 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError, os.str());
239 return nsecs + TT_MINUS_TAI_NSECS;
241 return taiToUtc(nsecs);
243 std::ostringstream os;
244 os <<
"Unsupported scale " <<
scale;
245 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError, os.str());
261 double calendarToJd(
int year,
int month,
int day,
int hour,
int min,
double sec) {
266 int a = int(year/100);
267 int b = 2 - a + int(a/4);
269 int yy = 1582, mm = 10;
270 if (year < yy || (year == yy && month < mm) || (year == yy && month == mm && day <= 4)) {
274 double jd =
static_cast<int>(365.25*(year + 4716)) +
275 static_cast<int>(30.6001*(month + 1)) + day + b - 1524.5;
276 jd += hour/HOURS_PER_DAY + min/MIN_PER_DAY + sec/SEC_PER_DAY;
291 if (mjd > EPOCH_IN_MJD + MAX_DAYS) {
293 lsst::pex::exceptions::DomainError,
294 (
boost::format(
"MJD too far in the future: %1%") % mjd).str());
296 if (mjd < EPOCH_IN_MJD - MAX_DAYS) {
298 lsst::pex::exceptions::DomainError,
299 (
boost::format(
"MJD too far in the past: %1%") % mjd).str());
301 _nsecs = nsecAnyToTai(static_cast<long long>((mjd - EPOCH_IN_MJD) * NSEC_PER_DAY), scale);
322 _nsecs(nsecAnyToTai(nsecs, scale))
337 throw LSST_EXCEPT(pexEx::InvalidParameterError,
"DateSystem must be MJD, JD, or EPOCH");
343 int const minYear = 1902;
344 int const maxYear = 2261;
345 if ((year < minYear) || (year > maxYear)) {
347 lsst::pex::exceptions::DomainError,
348 (
boost::format(
"Year = %d out of range [%04d, %04d]") % year % minYear % maxYear).str());
352 tm.tm_year = year - 1900;
353 tm.tm_mon = month - 1;
367 time_t secs = timegm(&tm);
385 if (timegm(&tm) != -1) {
391 lsst::pex::exceptions::DomainError,
392 (
boost::format(
"Unconvertible date: %04d-%02d-%02dT%02d:%02d:%02d")
393 % year % month % day % hr % min % sec).str());
397 _nsecs = nsecAnyToTai(secs * LL_NSEC_PER_SEC, scale);
404 re = boost::regex(
"(\\d{4})-?(\\d{2})-?(\\d{2})" "T"
405 "(\\d{2}):?(\\d{2}):?(\\d{2})" "([.,](\\d*))?" "Z");
408 re = boost::regex(
"(\\d{4})-?(\\d{2})-?(\\d{2})" "T"
409 "(\\d{2}):?(\\d{2}):?(\\d{2})" "([.,](\\d*))?");
411 boost::smatch matches;
412 if (!regex_match(iso8601, matches, re)) {
413 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
414 "Not in acceptable ISO8601 format: " + iso8601);
418 DateTime dt(atoi(matches.str(1).c_str()), atoi(matches.str(2).c_str()),
419 atoi(matches.str(3).c_str()), atoi(matches.str(4).c_str()),
420 atoi(matches.str(5).c_str()), atoi(matches.str(6).c_str()),
424 if (matches[7].matched) {
425 std::string frac = matches.str(8);
426 int places = frac.size();
430 int value = atoi(frac.c_str());
452 throw LSST_EXCEPT(pexEx::InvalidParameterError,
"DateSystem must be MJD, JD, or EPOCH");
462 return nsecTaiToAny(
_nsecs, scale);
467 double nsecs = nsecTaiToAny(
_nsecs, scale);
468 return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
472 return _getMjd(scale) + MJD_TO_JD;
476 return 2000.0 + (
_getJd(scale) - JD2000)/365.25;
479 struct tm
DateTime::gmtime(Timescale scale) const {
482 long long nsecs = nsecTaiToAny(_nsecs, scale);
484 long long frac = nsecs % LL_NSEC_PER_SEC;
485 if (nsecs < 0 && frac < 0) {
486 nsecs -= LL_NSEC_PER_SEC + frac;
491 time_t secs =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
492 gmtime_r(&secs, &gmt);
496 struct timespec
DateTime::timespec(Timescale scale) const {
499 long long nsecs = nsecTaiToAny(_nsecs, scale);
500 ts.tv_sec =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
501 ts.tv_nsec =
static_cast<int>(nsecs % LL_NSEC_PER_SEC);
505 struct timeval
DateTime::timeval(Timescale scale) const {
508 long long nsecs = nsecTaiToAny(_nsecs, scale);
509 tv.tv_sec =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
510 tv.tv_usec =
static_cast<int>((nsecs % LL_NSEC_PER_SEC) / 1000);
516 struct tm gmt(this->gmtime(scale));
518 long long fracnsecs = nsecTaiToAny(
_nsecs, scale) % LL_NSEC_PER_SEC;
520 fracnsecs += LL_NSEC_PER_SEC;
522 auto fmtStr = scale ==
UTC ?
"%04d-%02d-%02dT%02d:%02d:%02d.%09dZ"
523 :
"%04d-%02d-%02dT%02d:%02d:%02d.%09d";
525 (gmt.tm_year + 1900) % (gmt.tm_mon + 1) % gmt.tm_mday %
526 gmt.tm_hour % gmt.tm_min % gmt.tm_sec % fracnsecs).str();
535 int ret = gettimeofday(&tv, 0);
537 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
538 "Unable to get current time");
540 long long nsecs = tv.tv_sec * LL_NSEC_PER_SEC + tv.tv_usec * 1000
LL;
546 leapSecTable.clear();
547 boost::regex re(
"^\\d{4}.*?=JD\\s*([\\d.]+)\\s+TAI-UTC=\\s+([\\d.]+)\\s+S"
548 " \\+ \\(MJD - ([\\d.]+)\\) X ([\\d.]+)\\s*S$");
549 for (boost::cregex_iterator i = make_regex_iterator(leapString.c_str(), re);
550 i != boost::cregex_iterator(); ++i) {
551 double mjdUtc = strtod((*i)[1].first, 0) - MJD_TO_JD;
552 l.offset = strtod((*i)[2].first, 0);
553 l.mjdRef = strtod((*i)[3].first, 0);
554 l.drift = strtod((*i)[4].first, 0);
555 l.whenUtc =
static_cast<long long>(
556 (mjdUtc - EPOCH_IN_MJD) * NSEC_PER_DAY);
557 l.whenTai = l.whenUtc +
static_cast<long long>(
558 1.0e9 * (l.offset + (mjdUtc - l.mjdRef) * l.drift));
559 leapSecTable.push_back(l);
void setNsecsFromMjd(double mjd, Timescale scale)
Set internal nanoseconds from Modified Julian Date in the specified time scale.
void _assertValid() const
Raise RuntimeError if DateTime is not valid.
void setNsecsFromJd(double jd, Timescale scale)
Set internal nanoseconds from Julian Days in the specified time scale.
struct timeval timeval(Timescale scale) const
Get date as a timeval struct, with time in seconds and microseconds.
Class for handling dates/times, including MJD, UTC, and TAI.
double _getEpoch(Timescale scale) const
Get date as an epoch (year) in the specified time scale.
Include files required for standard LSST Exception handling.
double _getJd(Timescale scale) const
Get date as Julian Days in the specified time scale.
long long _nsecs
TAI nanoseconds since Unix epoch.
double get(DateSystem system=MJD, Timescale scale=TAI) const
Get date as a double in a specified representation, such as MJD.
static constexpr long long invalid_nsecs
bool isValid() const
Is this date valid?
static void initializeLeapSeconds(std::string const &leapString)
Initialize the leap second table from USNO.
long long nsecs(Timescale scale=TAI) const
Get date as nanoseconds since the unix epoch.
std::string toString(Timescale scale) const
Get date as an ISO8601-formatted string.
Interface for DateTime class.
bool operator==(DateTime const &rhs) const
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
void setNsecsFromEpoch(double epoch, Timescale scale)
Set internal nanoseconds from an epoch (year) in the specified time scale.
double _getMjd(Timescale scale) const
Get date as Modified Julian Days in the specified time scale.
afw::table::Key< double > b
DateTime()
Default constructor: construct an invalid DateTime.
static DateTime now(void)
Return current time as a DateTime.