43 #include "boost/format.hpp"
55 static double const MJD_TO_JD = 2400000.5;
56 static double const EPOCH_IN_MJD = 40587.0;
57 static double const JD2000 = 2451545.0;
60 static double const NSEC_PER_DAY = 86.4e12;
63 static long long const LL_NSEC_PER_SEC = 1000000000LL;
71 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 long long const TT_MINUS_TAI_NSECS = 32184000000LL;
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 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S\n\
149 LeapTable leapSecTable;
159 template <
typename NsType>
160 NsType 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;
183 template <
typename NsType>
184 NsType 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;
257 double 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;
284 void 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);
296 void DateTime::setNsecsFromJd(
double jd, Timescale
scale) { setNsecsFromMjd(jd - MJD_TO_JD,
scale); }
303 void 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);
456 double DateTime::_getMjd(Timescale
scale)
const {
459 return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
462 double DateTime::_getJd(Timescale
scale)
const {
return _getMjd(
scale) + MJD_TO_JD; }
466 struct tm
DateTime::gmtime(Timescale scale) const {
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);
482 struct 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);
491 struct 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);
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,...)
Create an exception with a given 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
constexpr static long long invalid_nsecs
long long nsecs(Timescale scale=TAI) const
Get date as nanoseconds since the unix epoch.
Reports arguments outside the domain of an operation.
Reports invalid arguments.
Reports errors that are due to events beyond the control of the program.
def scale(algorithm, min, max=None, frame=None)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
A base class for image defects.