41 #include "boost/format.hpp"
42 #include "boost/regex.hpp"
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 = 1000000000LL;
70 static double const MAX_DAYS = 106751.99;
73 static double const HOURS_PER_DAY = 24.0;
74 static double const MIN_PER_DAY = 1440.0;
75 static double const SEC_PER_DAY = 86400.0;
79 static long long const TT_MINUS_TAI_NSECS = 32184000000LL;
87 1961 JAN 1 =JD 2437300.5 TAI-UTC= 1.4228180 S + (MJD - 37300.) X 0.001296 S\n\
88 1961 AUG 1 =JD 2437512.5 TAI-UTC= 1.3728180 S + (MJD - 37300.) X 0.001296 S\n\
89 1962 JAN 1 =JD 2437665.5 TAI-UTC= 1.8458580 S + (MJD - 37665.) X 0.0011232S\n\
90 1963 NOV 1 =JD 2438334.5 TAI-UTC= 1.9458580 S + (MJD - 37665.) X 0.0011232S\n\
91 1964 JAN 1 =JD 2438395.5 TAI-UTC= 3.2401300 S + (MJD - 38761.) X 0.001296 S\n\
92 1964 APR 1 =JD 2438486.5 TAI-UTC= 3.3401300 S + (MJD - 38761.) X 0.001296 S\n\
93 1964 SEP 1 =JD 2438639.5 TAI-UTC= 3.4401300 S + (MJD - 38761.) X 0.001296 S\n\
94 1965 JAN 1 =JD 2438761.5 TAI-UTC= 3.5401300 S + (MJD - 38761.) X 0.001296 S\n\
95 1965 MAR 1 =JD 2438820.5 TAI-UTC= 3.6401300 S + (MJD - 38761.) X 0.001296 S\n\
96 1965 JUL 1 =JD 2438942.5 TAI-UTC= 3.7401300 S + (MJD - 38761.) X 0.001296 S\n\
97 1965 SEP 1 =JD 2439004.5 TAI-UTC= 3.8401300 S + (MJD - 38761.) X 0.001296 S\n\
98 1966 JAN 1 =JD 2439126.5 TAI-UTC= 4.3131700 S + (MJD - 39126.) X 0.002592 S\n\
99 1968 FEB 1 =JD 2439887.5 TAI-UTC= 4.2131700 S + (MJD - 39126.) X 0.002592 S\n\
100 1972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0 S + (MJD - 41317.) X 0.0 S\n\
101 1972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0 S + (MJD - 41317.) X 0.0 S\n\
102 1973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0 S + (MJD - 41317.) X 0.0 S\n\
103 1974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0 S + (MJD - 41317.) X 0.0 S\n\
104 1975 JAN 1 =JD 2442413.5 TAI-UTC= 14.0 S + (MJD - 41317.) X 0.0 S\n\
105 1976 JAN 1 =JD 2442778.5 TAI-UTC= 15.0 S + (MJD - 41317.) X 0.0 S\n\
106 1977 JAN 1 =JD 2443144.5 TAI-UTC= 16.0 S + (MJD - 41317.) X 0.0 S\n\
107 1978 JAN 1 =JD 2443509.5 TAI-UTC= 17.0 S + (MJD - 41317.) X 0.0 S\n\
108 1979 JAN 1 =JD 2443874.5 TAI-UTC= 18.0 S + (MJD - 41317.) X 0.0 S\n\
109 1980 JAN 1 =JD 2444239.5 TAI-UTC= 19.0 S + (MJD - 41317.) X 0.0 S\n\
110 1981 JUL 1 =JD 2444786.5 TAI-UTC= 20.0 S + (MJD - 41317.) X 0.0 S\n\
111 1982 JUL 1 =JD 2445151.5 TAI-UTC= 21.0 S + (MJD - 41317.) X 0.0 S\n\
112 1983 JUL 1 =JD 2445516.5 TAI-UTC= 22.0 S + (MJD - 41317.) X 0.0 S\n\
113 1985 JUL 1 =JD 2446247.5 TAI-UTC= 23.0 S + (MJD - 41317.) X 0.0 S\n\
114 1988 JAN 1 =JD 2447161.5 TAI-UTC= 24.0 S + (MJD - 41317.) X 0.0 S\n\
115 1990 JAN 1 =JD 2447892.5 TAI-UTC= 25.0 S + (MJD - 41317.) X 0.0 S\n\
116 1991 JAN 1 =JD 2448257.5 TAI-UTC= 26.0 S + (MJD - 41317.) X 0.0 S\n\
117 1992 JUL 1 =JD 2448804.5 TAI-UTC= 27.0 S + (MJD - 41317.) X 0.0 S\n\
118 1993 JUL 1 =JD 2449169.5 TAI-UTC= 28.0 S + (MJD - 41317.) X 0.0 S\n\
119 1994 JUL 1 =JD 2449534.5 TAI-UTC= 29.0 S + (MJD - 41317.) X 0.0 S\n\
120 1996 JAN 1 =JD 2450083.5 TAI-UTC= 30.0 S + (MJD - 41317.) X 0.0 S\n\
121 1997 JUL 1 =JD 2450630.5 TAI-UTC= 31.0 S + (MJD - 41317.) X 0.0 S\n\
122 1999 JAN 1 =JD 2451179.5 TAI-UTC= 32.0 S + (MJD - 41317.) X 0.0 S\n\
123 2006 JAN 1 =JD 2453736.5 TAI-UTC= 33.0 S + (MJD - 41317.) X 0.0 S\n\
124 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S\n\
125 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S\n\
126 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S\n\
127 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S\n\
148 LeapTable leapSecTable;
158 template <
typename NsType>
159 NsType utcToTai(NsType nsecs) {
161 for (i = 0; i < leapSecTable.size(); ++i) {
162 if (nsecs < leapSecTable[i].whenUtc)
break;
167 (
boost::format(
"DateTime value too early for UTC-TAI conversion: %1%") % nsecs).str());
169 Leap
const& l(leapSecTable[i - 1]);
170 double mjd =
static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
171 double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
172 NsType leapNSecs =
static_cast<NsType
>(leapSecs * 1.0e9 + 0.5);
173 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;
191 (
boost::format(
"DateTime value too early for TAI-UTC conversion: %1%") % nsecs).str());
193 Leap
const& l(leapSecTable[i - 1]);
194 double mjd =
static_cast<double>(nsecs) / NSEC_PER_DAY + EPOCH_IN_MJD;
195 double leapSecs = l.offset + (mjd - l.mjdRef) * l.drift;
197 leapSecs /= 1.0 + l.drift * 1.0e9 / NSEC_PER_DAY;
198 NsType leapNSecs =
static_cast<NsType
>(leapSecs * 1.0e9 + 0.5);
199 return nsecs - leapNSecs;
214 return nsecs - TT_MINUS_TAI_NSECS;
216 return utcToTai(nsecs);
219 os <<
"Unsupported scale " <<
scale;
235 return nsecs + TT_MINUS_TAI_NSECS;
237 return taiToUtc(nsecs);
240 os <<
"Unsupported scale " <<
scale;
256 double calendarToJd(
int year,
int month,
int day,
int hour,
int min,
double sec) {
261 int a = int(year / 100);
262 int b = 2 -
a + int(
a / 4);
264 int yy = 1582, mm = 10;
265 if (year < yy || (year == yy && month < mm) || (year == yy && month == mm && day <= 4)) {
269 double jd =
static_cast<int>(365.25 * (year + 4716)) +
static_cast<int>(30.6001 * (month + 1)) + day +
b -
271 jd += hour / HOURS_PER_DAY +
min / MIN_PER_DAY + sec / SEC_PER_DAY;
283 void DateTime::setNsecsFromMjd(
double mjd, Timescale
scale) {
284 if (mjd > EPOCH_IN_MJD + MAX_DAYS) {
286 (
boost::format(
"MJD too far in the future: %1%") % mjd).str());
288 if (mjd < EPOCH_IN_MJD - MAX_DAYS) {
290 (
boost::format(
"MJD too far in the past: %1%") % mjd).str());
292 _nsecs = nsecAnyToTai(
static_cast<long long>((mjd - EPOCH_IN_MJD) * NSEC_PER_DAY),
scale);
295 void DateTime::setNsecsFromJd(
double jd, Timescale
scale) { setNsecsFromMjd(jd - MJD_TO_JD,
scale); }
302 void DateTime::setNsecsFromEpoch(
double epoch, Timescale
scale) {
303 setNsecsFromMjd(365.25 * (epoch - 2000.0) + JD2000 - MJD_TO_JD,
scale);
313 setNsecsFromMjd(date,
scale);
316 setNsecsFromJd(date,
scale);
319 setNsecsFromEpoch(date,
scale);
328 int const minYear = 1902;
329 int const maxYear = 2261;
330 if ((year < minYear) || (year > maxYear)) {
333 (
boost::format(
"Year = %d out of range [%04d, %04d]") % year % minYear % maxYear).str());
337 tm.tm_year = year - 1900;
338 tm.tm_mon = month - 1;
352 time_t secs = timegm(&tm);
370 if (timegm(&tm) != -1) {
376 (
boost::format(
"Unconvertible date: %04d-%02d-%02dT%02d:%02d:%02d") % year %
377 month % day % hr %
min % sec)
382 _nsecs = nsecAnyToTai(secs * LL_NSEC_PER_SEC,
scale);
390 "(\\d{4})-?(\\d{2})-?(\\d{2})"
392 "(\\d{2}):?(\\d{2}):?(\\d{2})"
398 "(\\d{4})-?(\\d{2})-?(\\d{2})"
400 "(\\d{2}):?(\\d{2}):?(\\d{2})"
403 boost::smatch matches;
404 if (!regex_match(iso8601, matches, re)) {
409 DateTime dt(atoi(matches.str(1).c_str()), atoi(matches.str(2).c_str()), atoi(matches.str(3).c_str()),
410 atoi(matches.str(4).c_str()), atoi(matches.str(5).c_str()), atoi(matches.str(6).c_str()),
414 if (matches[7].matched) {
416 int places = frac.
size();
420 int value = atoi(frac.
c_str());
433 return _getMjd(
scale);
436 return _getJd(
scale);
439 return _getEpoch(
scale);
452 return nsecTaiToAny(_nsecs,
scale);
455 double DateTime::_getMjd(Timescale
scale)
const {
458 return nsecs / NSEC_PER_DAY + EPOCH_IN_MJD;
461 double DateTime::_getJd(Timescale
scale)
const {
return _getMjd(
scale) + MJD_TO_JD; }
465 struct tm
DateTime::gmtime(Timescale scale) const {
468 long long nsecs = nsecTaiToAny(_nsecs,
scale);
470 long long frac = nsecs % LL_NSEC_PER_SEC;
471 if (nsecs < 0 && frac < 0) {
472 nsecs -= LL_NSEC_PER_SEC + frac;
476 time_t secs =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
477 gmtime_r(&secs, &gmt);
481 struct timespec DateTime::timespec(Timescale scale) const {
484 long long nsecs = nsecTaiToAny(_nsecs,
scale);
485 ts.tv_sec =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
486 ts.tv_nsec =
static_cast<int>(nsecs % LL_NSEC_PER_SEC);
490 struct timeval DateTime::timeval(Timescale scale) const {
493 long long nsecs = nsecTaiToAny(_nsecs,
scale);
494 tv.tv_sec =
static_cast<time_t
>(nsecs / LL_NSEC_PER_SEC);
495 tv.tv_usec =
static_cast<int>((nsecs % LL_NSEC_PER_SEC) / 1000);
503 long long fracnsecs = nsecTaiToAny(_nsecs,
scale) % LL_NSEC_PER_SEC;
505 fracnsecs += LL_NSEC_PER_SEC;
507 auto fmtStr =
scale ==
UTC ?
"%04d-%02d-%02dT%02d:%02d:%02d.%09dZ" :
"%04d-%02d-%02dT%02d:%02d:%02d.%09d";
508 return (
boost::format(fmtStr) % (gmt.tm_year + 1900) % (gmt.tm_mon + 1) % gmt.tm_mday % gmt.tm_hour %
509 gmt.tm_min % gmt.tm_sec % fracnsecs)
519 int ret = gettimeofday(&tv, 0);
523 long long nsecs = tv.tv_sec * LL_NSEC_PER_SEC + tv.tv_usec * 1000LL;
529 leapSecTable.clear();
531 "^\\d{4}.*?=JD\\s*([\\d.]+)\\s+TAI-UTC=\\s+([\\d.]+)\\s+S"
532 " \\+ \\(MJD - ([\\d.]+)\\) X ([\\d.]+)\\s*S$");
533 for (boost::cregex_iterator i = make_regex_iterator(leapString.
c_str(), re);
534 i != boost::cregex_iterator(); ++i) {
535 double mjdUtc = strtod((*i)[1].
first, 0) - MJD_TO_JD;
536 l.offset = strtod((*i)[2].
first, 0);
537 l.mjdRef = strtod((*i)[3].
first, 0);
538 l.drift = strtod((*i)[4].
first, 0);
539 l.whenUtc =
static_cast<long long>((mjdUtc - EPOCH_IN_MJD) * NSEC_PER_DAY);
540 l.whenTai = l.whenUtc +
static_cast<long long>(1.0e9 * (l.offset + (mjdUtc - l.mjdRef) * l.drift));
541 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.