43#include "boost/format.hpp"
55static double const MJD_TO_JD = 2400000.5;
56static double const EPOCH_IN_MJD = 40587.0;
57static double const JD2000 = 2451545.0;
60static double const NSEC_PER_DAY = 86.4e12;
63static long long const LL_NSEC_PER_SEC = 1000000000LL;
71static double const MAX_DAYS = 106751.99;
74static double const HOURS_PER_DAY = 24.0;
75static double const MIN_PER_DAY = 1440.0;
76static double const SEC_PER_DAY = 86400.0;
80static long long const TT_MINUS_TAI_NSECS = 32184000000LL;
881961 JAN 1 =JD 2437300.5 TAI-UTC= 1.4228180 S + (MJD - 37300.) X 0.001296 S\n\
891961 AUG 1 =JD 2437512.5 TAI-UTC= 1.3728180 S + (MJD - 37300.) X 0.001296 S\n\
901962 JAN 1 =JD 2437665.5 TAI-UTC= 1.8458580 S + (MJD - 37665.) X 0.0011232S\n\
911963 NOV 1 =JD 2438334.5 TAI-UTC= 1.9458580 S + (MJD - 37665.) X 0.0011232S\n\
921964 JAN 1 =JD 2438395.5 TAI-UTC= 3.2401300 S + (MJD - 38761.) X 0.001296 S\n\
931964 APR 1 =JD 2438486.5 TAI-UTC= 3.3401300 S + (MJD - 38761.) X 0.001296 S\n\
941964 SEP 1 =JD 2438639.5 TAI-UTC= 3.4401300 S + (MJD - 38761.) X 0.001296 S\n\
951965 JAN 1 =JD 2438761.5 TAI-UTC= 3.5401300 S + (MJD - 38761.) X 0.001296 S\n\
961965 MAR 1 =JD 2438820.5 TAI-UTC= 3.6401300 S + (MJD - 38761.) X 0.001296 S\n\
971965 JUL 1 =JD 2438942.5 TAI-UTC= 3.7401300 S + (MJD - 38761.) X 0.001296 S\n\
981965 SEP 1 =JD 2439004.5 TAI-UTC= 3.8401300 S + (MJD - 38761.) X 0.001296 S\n\
991966 JAN 1 =JD 2439126.5 TAI-UTC= 4.3131700 S + (MJD - 39126.) X 0.002592 S\n\
1001968 FEB 1 =JD 2439887.5 TAI-UTC= 4.2131700 S + (MJD - 39126.) X 0.002592 S\n\
1011972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0 S + (MJD - 41317.) X 0.0 S\n\
1021972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0 S + (MJD - 41317.) X 0.0 S\n\
1031973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0 S + (MJD - 41317.) X 0.0 S\n\
1041974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0 S + (MJD - 41317.) X 0.0 S\n\
1051975 JAN 1 =JD 2442413.5 TAI-UTC= 14.0 S + (MJD - 41317.) X 0.0 S\n\
1061976 JAN 1 =JD 2442778.5 TAI-UTC= 15.0 S + (MJD - 41317.) X 0.0 S\n\
1071977 JAN 1 =JD 2443144.5 TAI-UTC= 16.0 S + (MJD - 41317.) X 0.0 S\n\
1081978 JAN 1 =JD 2443509.5 TAI-UTC= 17.0 S + (MJD - 41317.) X 0.0 S\n\
1091979 JAN 1 =JD 2443874.5 TAI-UTC= 18.0 S + (MJD - 41317.) X 0.0 S\n\
1101980 JAN 1 =JD 2444239.5 TAI-UTC= 19.0 S + (MJD - 41317.) X 0.0 S\n\
1111981 JUL 1 =JD 2444786.5 TAI-UTC= 20.0 S + (MJD - 41317.) X 0.0 S\n\
1121982 JUL 1 =JD 2445151.5 TAI-UTC= 21.0 S + (MJD - 41317.) X 0.0 S\n\
1131983 JUL 1 =JD 2445516.5 TAI-UTC= 22.0 S + (MJD - 41317.) X 0.0 S\n\
1141985 JUL 1 =JD 2446247.5 TAI-UTC= 23.0 S + (MJD - 41317.) X 0.0 S\n\
1151988 JAN 1 =JD 2447161.5 TAI-UTC= 24.0 S + (MJD - 41317.) X 0.0 S\n\
1161990 JAN 1 =JD 2447892.5 TAI-UTC= 25.0 S + (MJD - 41317.) X 0.0 S\n\
1171991 JAN 1 =JD 2448257.5 TAI-UTC= 26.0 S + (MJD - 41317.) X 0.0 S\n\
1181992 JUL 1 =JD 2448804.5 TAI-UTC= 27.0 S + (MJD - 41317.) X 0.0 S\n\
1191993 JUL 1 =JD 2449169.5 TAI-UTC= 28.0 S + (MJD - 41317.) X 0.0 S\n\
1201994 JUL 1 =JD 2449534.5 TAI-UTC= 29.0 S + (MJD - 41317.) X 0.0 S\n\
1211996 JAN 1 =JD 2450083.5 TAI-UTC= 30.0 S + (MJD - 41317.) X 0.0 S\n\
1221997 JUL 1 =JD 2450630.5 TAI-UTC= 31.0 S + (MJD - 41317.) X 0.0 S\n\
1231999 JAN 1 =JD 2451179.5 TAI-UTC= 32.0 S + (MJD - 41317.) X 0.0 S\n\
1242006 JAN 1 =JD 2453736.5 TAI-UTC= 33.0 S + (MJD - 41317.) X 0.0 S\n\
1252009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S\n\
1262012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S\n\
1272015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S\n\
1282017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S\n\
149LeapTable leapSecTable;
159template <
typename NsType>
160NsType utcToTai(NsType nsecs) {
162 for (i = 0; i < leapSecTable.size(); ++i) {
163 if (nsecs < leapSecTable[i].whenUtc)
break;
168 (boost::format(
"DateTime value too early for UTC-TAI conversion: %1%") % nsecs).
str());
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;
183template <
typename NsType>
184NsType taiToUtc(NsType nsecs) {
186 for (i = 0; i < leapSecTable.size(); ++i) {
187 if (nsecs < leapSecTable[i].whenTai)
break;
192 (boost::format(
"DateTime value too early for TAI-UTC conversion: %1%") % nsecs).
str());
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;
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;
215 return nsecs - TT_MINUS_TAI_NSECS;
217 return utcToTai(nsecs);
220 os <<
"Unsupported scale " << scale;
236 return nsecs + TT_MINUS_TAI_NSECS;
238 return taiToUtc(nsecs);
241 os <<
"Unsupported scale " << scale;
257double calendarToJd(
int year,
int month,
int day,
int hour,
int min,
double sec) {
262 int a = int(year / 100);
263 int b = 2 - a + int(a / 4);
265 int yy = 1582, mm = 10;
266 if (year < yy || (year == yy && month < mm) || (year == yy && month == mm && day <= 4)) {
270 double jd =
static_cast<int>(365.25 * (year + 4716)) +
static_cast<int>(30.6001 * (month + 1)) + day + b -
272 jd += hour / HOURS_PER_DAY +
min / MIN_PER_DAY + sec / SEC_PER_DAY;
284void DateTime::setNsecsFromMjd(
double mjd, Timescale scale) {
285 if (mjd > EPOCH_IN_MJD + MAX_DAYS) {
287 (boost::format(
"MJD too far in the future: %1%") % mjd).
str());
289 if (mjd < EPOCH_IN_MJD - MAX_DAYS) {
291 (boost::format(
"MJD too far in the past: %1%") % mjd).
str());
293 _nsecs = nsecAnyToTai(
static_cast<long long>((mjd - EPOCH_IN_MJD) * NSEC_PER_DAY), scale);
296void DateTime::setNsecsFromJd(
double jd, Timescale scale) { setNsecsFromMjd(jd - MJD_TO_JD, scale); }
303void DateTime::setNsecsFromEpoch(
double epoch, Timescale scale) {
304 setNsecsFromMjd(365.25 * (epoch - 2000.0) + JD2000 - MJD_TO_JD, scale);
314 setNsecsFromMjd(date, scale);
317 setNsecsFromJd(date, scale);
320 setNsecsFromEpoch(date, scale);
329 int const minYear = 1902;
330 int const maxYear = 2261;
331 if ((year < minYear) || (year > maxYear)) {
334 (boost::format(
"Year = %d out of range [%04d, %04d]") % year % minYear % maxYear).
str());
338 tm.tm_year = year - 1900;
339 tm.tm_mon = month - 1;
353 time_t secs = timegm(&tm);
371 if (timegm(&tm) != -1) {
377 (boost::format(
"Unconvertible date: %04d-%02d-%02dT%02d:%02d:%02d") % year %
378 month % day % hr % min % sec)
383 _nsecs = nsecAnyToTai(secs * LL_NSEC_PER_SEC, scale);
391 "(\\d{4})-?(\\d{2})-?(\\d{2})"
393 "(\\d{2}):?(\\d{2}):?(\\d{2})"
399 "(\\d{4})-?(\\d{2})-?(\\d{2})"
401 "(\\d{2}):?(\\d{2}):?(\\d{2})"
405 if (!regex_match(iso8601, matches, re)) {
410 DateTime dt(atoi(matches.
str(1).c_str()), atoi(matches.
str(2).c_str()), atoi(matches.
str(3).c_str()),
411 atoi(matches.
str(4).c_str()), atoi(matches.
str(5).c_str()), atoi(matches.
str(6).c_str()),
415 if (matches[7].matched) {
417 int places = frac.
size();
421 int value = atoi(frac.
c_str());
434 return _getMjd(scale);
437 return _getJd(scale);
440 return _getEpoch(scale);
453 return nsecTaiToAny(_nsecs, scale);
456double DateTime::_getMjd(Timescale scale)
const {
458 double nsecs = nsecTaiToAny(_nsecs, scale);
459 return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
462double DateTime::_getJd(Timescale scale)
const {
return _getMjd(scale) + MJD_TO_JD; }
464double DateTime::_getEpoch(Timescale scale)
const {
return 2000.0 + (_getJd(scale) - JD2000) / 365.25; }
469 long long nsecs = nsecTaiToAny(_nsecs, scale);
471 long long frac = nsecs % LL_NSEC_PER_SEC;
472 if (nsecs < 0 && frac < 0) {
473 nsecs -= LL_NSEC_PER_SEC + frac;
477 time_t secs =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
478 gmtime_r(&secs, &gmt);
482struct timespec
DateTime::timespec(Timescale scale) const {
485 long long nsecs = nsecTaiToAny(_nsecs, scale);
486 ts.tv_sec =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
487 ts.tv_nsec =
static_cast<int>(nsecs % LL_NSEC_PER_SEC);
491struct timeval
DateTime::timeval(Timescale scale) const {
494 long long nsecs = nsecTaiToAny(_nsecs, scale);
495 tv.tv_sec =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
496 tv.tv_usec =
static_cast<int>((nsecs % LL_NSEC_PER_SEC) / 1000);
502 struct tm gmt(this->
gmtime(scale));
504 long long fracnsecs = nsecTaiToAny(_nsecs, scale) % LL_NSEC_PER_SEC;
506 fracnsecs += LL_NSEC_PER_SEC;
508 auto fmtStr = scale ==
UTC ?
"%04d-%02d-%02dT%02d:%02d:%02d.%09dZ" :
"%04d-%02d-%02dT%02d:%02d:%02d.%09d";
509 return (boost::format(fmtStr) % (gmt.tm_year + 1900) % (gmt.tm_mon + 1) % gmt.tm_mday % gmt.tm_hour %
510 gmt.tm_min % gmt.tm_sec % fracnsecs)
520 int ret = gettimeofday(&tv, 0);
524 long long nsecs = tv.tv_sec * LL_NSEC_PER_SEC + tv.tv_usec * 1000LL;
530 leapSecTable.clear();
532 "\\d{4}.*?=JD\\s*([\\d.]+)\\s+TAI-UTC=\\s+([\\d.]+)\\s+S"
533 " \\+ \\(MJD - ([\\d.]+)\\) X ([\\d.]+)\\s*S");
538 double mjdUtc =
std::stod((*i)[1]) - MJD_TO_JD;
542 l.whenUtc =
static_cast<long long>((mjdUtc - EPOCH_IN_MJD) * NSEC_PER_DAY);
543 l.whenTai = l.whenUtc +
static_cast<long long>(1.0e9 * (l.offset + (mjdUtc - l.mjdRef) * l.drift));
544 leapSecTable.push_back(l);
Interface for DateTime class.
#define LSST_EXCEPT(type,...)
Class for handling dates/times, including MJD, UTC, and TAI.
DateTime()
Default constructor: construct an invalid DateTime.
std::size_t hash_value() const noexcept
Return a hash of this object.
std::string toString(Timescale scale) const
Get date as an ISO8601-formatted string.
bool isValid() const
Is this date valid?
static DateTime now(void)
Return current time as a DateTime.
double get(DateSystem system=MJD, Timescale scale=TAI) const
Get date as a double in a specified representation, such as MJD.
struct timeval timeval(Timescale scale) const
Get date as a timeval struct, with time in seconds and microseconds.
struct tm gmtime(Timescale scale) const
Get date as a tm struct, with truncated fractional seconds.
static void initializeLeapSeconds(std::string const &leapString)
Initialize the leap second table from USNO.
bool operator==(DateTime const &rhs) const
static constexpr long long invalid_nsecs
long long nsecs(Timescale scale=TAI) const
Get date as nanoseconds since the unix epoch.