22
#include <fmt/format.h>
26
#if defined(__GNUC__) && !defined(_WIN32)
27
#include <fmt/chrono.h>
32
#include <celutil/gettext.h>
33
#include <celutil/winutil.h>
36
using namespace std::string_view_literals;
38
namespace celestia::astro
46
constexpr double dTA = 32.184;
51
constexpr std::array<LeapSecondRecord, 28> LeapSeconds
53
LeapSecondRecord{ 10, 2441317.5 },
54
LeapSecondRecord{ 11, 2441499.5 },
55
LeapSecondRecord{ 12, 2441683.5 },
56
LeapSecondRecord{ 13, 2442048.5 },
57
LeapSecondRecord{ 14, 2442413.5 },
58
LeapSecondRecord{ 15, 2442778.5 },
59
LeapSecondRecord{ 16, 2443144.5 },
60
LeapSecondRecord{ 17, 2443509.5 },
61
LeapSecondRecord{ 18, 2443874.5 },
62
LeapSecondRecord{ 19, 2444239.5 },
63
LeapSecondRecord{ 20, 2444786.5 },
64
LeapSecondRecord{ 21, 2445151.5 },
65
LeapSecondRecord{ 22, 2445516.5 },
66
LeapSecondRecord{ 23, 2446247.5 },
67
LeapSecondRecord{ 24, 2447161.5 },
68
LeapSecondRecord{ 25, 2447892.5 },
69
LeapSecondRecord{ 26, 2448257.5 },
70
LeapSecondRecord{ 27, 2448804.5 },
71
LeapSecondRecord{ 28, 2449169.5 },
72
LeapSecondRecord{ 29, 2449534.5 },
73
LeapSecondRecord{ 30, 2450083.5 },
74
LeapSecondRecord{ 31, 2450630.5 },
75
LeapSecondRecord{ 32, 2451179.5 },
76
LeapSecondRecord{ 33, 2453736.5 },
77
LeapSecondRecord{ 34, 2454832.5 },
78
LeapSecondRecord{ 35, 2456109.5 },
79
LeapSecondRecord{ 36, 2457204.5 },
80
LeapSecondRecord{ 37, 2457754.5 },
83
celestia::util::array_view<LeapSecondRecord> g_leapSeconds = LeapSeconds;
85
#if !(defined(__GNUC__) && !defined(_WIN32))
86
class MonthAbbreviations
89
explicit MonthAbbreviations(const std::locale& loc);
90
std::string_view operator[](int i) const { return abbreviations[static_cast<std::size_t>(i)]; }
93
std::array<std::string, 12> abbreviations;
96
MonthAbbreviations::MonthAbbreviations(const std::locale& loc)
98
for (int i = 0; i < 12; ++i)
102
std::wostringstream stream;
104
stream << std::put_time(&tm, L"%b");
105
abbreviations[i] = util::WideToUTF8(stream.str());
111
timeToLocal(const std::time_t& time, std::tm& localt)
114
return localtime_s(&localt, &time) == 0;
116
return localtime_r(&time, &localt) != nullptr;
121
timeToUTC(const std::time_t& time, std::tm& utct)
124
return gmtime_s(&utct, &time) == 0;
126
return gmtime_r(&time, &utct) != nullptr;
133
setLeapSeconds(celestia::util::array_view<LeapSecondRecord> leapSeconds)
135
g_leapSeconds = leapSeconds;
138
Date::Date(int Y, int M, int D) :
147
auto a = (std::int64_t) std::floor(jd + 0.5);
152
c = (double) (a + 1524);
156
auto b = (double) ((std::int64_t) std::floor((a - 1867216.25) / 36524.25));
157
c = a + b - (std::int64_t) std::floor(b / 4) + 1525;
160
auto d = (std::int64_t) std::floor((c - 122.1) / 365.25);
161
auto e = (std::int64_t) std::floor(365.25 * d);
162
auto f = (std::int64_t) std::floor((c - e) / 30.6001);
164
double dday = c - e - (std::int64_t) std::floor(30.6001 * f) + ((jd + 0.5) - a);
168
month = (int) (f - 1 - 12 * (std::int64_t) (f / 14));
169
year = (int) (d - 4715 - (std::int64_t) ((7.0 + month) / 10.0));
172
double dhour = (dday - day) * 24;
175
double dminute = (dhour - hour) * 60;
176
minute = (int) dminute;
178
seconds = (dminute - minute) * 60;
184
Date::toString(const std::locale& loc, Format format) const
186
if (format == ISO8601)
188
return fmt::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:08.5f}Z"sv,
189
year, month, day, hour, minute, seconds);
194
#if defined(__GNUC__) && !defined(_WIN32)
195
struct tm cal_time {};
196
cal_time.tm_year = year-1900;
197
cal_time.tm_mon = month-1;
198
cal_time.tm_mday = day;
199
cal_time.tm_hour = hour;
200
cal_time.tm_min = minute;
201
cal_time.tm_sec = (int)seconds;
202
cal_time.tm_wday = wday;
203
cal_time.tm_gmtoff = utc_offset;
204
#if defined(__APPLE__) || defined(__FreeBSD__)
206
cal_time.tm_zone = const_cast<char*>(tzname.c_str());
208
cal_time.tm_zone = tzname.c_str();
214
return fmt::format(loc, "{:%c}"sv, cal_time);
216
return fmt::format(loc, "{:%Y %b %d %H:%M:%S %Z}"sv, cal_time);
218
return fmt::format(loc, "{:%Y %b %d %H:%M:%S %z}"sv, cal_time);
221
static const MonthAbbreviations* monthAbbreviations = nullptr;
222
if (monthAbbreviations == nullptr)
223
monthAbbreviations = std::make_unique<MonthAbbreviations>(loc).release();
228
return fmt::format("{:04} {} {:02} {:02}:{:02}:{:02} {}"sv,
229
year, (*monthAbbreviations)[month - 1], day,
230
hour, minute, static_cast<int>(seconds), tzname);
233
char sign = utc_offset < 0 ? '-' : '+';
234
auto offsets = std::div(std::abs(utc_offset), 3600);
235
return fmt::format("{:04} {} {:02} {:02}:{:02}:{:02} {}{:02}{:02}"sv,
236
year, (*monthAbbreviations)[month - 1], day,
237
hour, minute, static_cast<int>(seconds),
238
sign, offsets.quot, offsets.rem / 60);
245
Date::operator double() const
247
int y = year, m = month;
257
if (year > 1582 || (year == 1582 && (month > 10 || (month == 10 && day >= 15))))
259
B = y / 400 - y / 100;
262
return (std::floor(365.25 * y) +
263
std::floor(30.6001 * (m + 1)) + B + 1720996.5 +
264
day + hour / HOURS_PER_DAY + minute / MINUTES_PER_DAY + seconds / SECONDS_PER_DAY);
270
time_t t = time(nullptr);
274
if (timeToUTC(t, gmt))
276
d.year = gmt.tm_year + 1900;
277
d.month = gmt.tm_mon + 1;
279
d.hour = gmt.tm_hour;
280
d.minute = gmt.tm_min;
281
d.seconds = gmt.tm_sec;
289
parseDate(const std::string& s, Date& date)
292
unsigned int month = 1;
293
unsigned int day = 1;
294
unsigned int hour = 0;
295
unsigned int minute = 0;
298
if (std::sscanf(s.c_str(), "%d-%u-%uT%u:%u:%lf",
299
&year, &month, &day, &hour, &minute, &second) != 6 &&
300
std::sscanf(s.c_str(), " %d %u %u %u:%u:%lf ",
301
&year, &month, &day, &hour, &minute, &second) != 6 &&
302
std::sscanf(s.c_str(), " %d %u %u %u:%u ",
303
&year, &month, &day, &hour, &minute) != 5 &&
304
std::sscanf(s.c_str(), " %d %u %u ", &year, &month, &day) != 3)
309
if (month < 1 || month > 12)
311
if (hour > 23 || minute > 59 || second >= 60.0 || second < 0.0)
315
int maxDay = 31 - ((0xa50 >> month) & 0x1);
319
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
324
if (day > (unsigned int) maxDay || day < 1)
331
date.minute = minute;
332
date.seconds = second;
343
int dAT = g_leapSeconds[0].seconds;
346
for (std::size_t i = g_leapSeconds.size() - 1; i > 0; i--)
348
if (tai - secsToDays(g_leapSeconds[i].seconds) >= g_leapSeconds[i].t)
350
dAT = g_leapSeconds[i].seconds;
353
if (tai - secsToDays(g_leapSeconds[i - 1].seconds) >= g_leapSeconds[i].t)
355
dAT = g_leapSeconds[i].seconds;
356
extraSecs = g_leapSeconds[i].seconds - g_leapSeconds[i - 1].seconds;
361
Date utcDate(tai - secsToDays(dAT));
362
utcDate.seconds += extraSecs;
369
UTCtoTAI(const Date& utc)
371
double dAT = g_leapSeconds[0].seconds;
372
auto utcjd = (double) Date(utc.year, utc.month, utc.day);
374
for (std::size_t i = g_leapSeconds.size() - 1; i > 0; i--)
376
if (utcjd >= g_leapSeconds[i].t)
378
dAT = g_leapSeconds[i].seconds;
383
double tai = utcjd + secsToDays(utc.hour * 3600.0 + utc.minute * 60.0 + utc.seconds + dAT);
392
return tt - secsToDays(dTA);
399
return tai + secsToDays(dTA);
404
TDBcorrection(double tdb)
409
constexpr double K = 1.657e-3;
410
constexpr double EB = 1.671e-2;
411
constexpr double M0 = 6.239996;
412
constexpr double M1 = 1.99096871e-7;
415
double t = daysToSecs(tdb - J2000);
418
double M = M0 + M1 * t;
421
double E = M + EB * std::sin(M);
423
return K * std::sin(E);
430
return tt + secsToDays(TDBcorrection(tt));
437
return tdb - secsToDays(TDBcorrection(tdb));
444
return TAItoUTC(TTtoTAI(TDBtoTT(tdb)));
450
TDBtoLocal(double tdb)
452
double tai = TTtoTAI(TDBtoTT(tdb));
454
double jdutc = TAItoJDUTC(tai);
455
if (jdutc <= 2415733 || jdutc >= 2465442)
456
return TDBtoUTC(tdb);
458
auto time = static_cast<time_t>(julianDateToSeconds(jdutc - 2440587.5));
461
if (!timeToLocal(time, localt))
462
return TDBtoUTC(tdb);
465
d.year = localt.tm_year + 1900;
466
d.month = localt.tm_mon + 1;
467
d.day = localt.tm_mday;
468
d.hour = localt.tm_hour;
469
d.minute = localt.tm_min;
470
d.seconds = localt.tm_sec;
471
d.wday = localt.tm_wday;
472
#if defined(__GNUC__) && !defined(_WIN32)
473
d.utc_offset = static_cast<int>(localt.tm_gmtoff);
474
d.tzname = localt.tm_zone;
476
Date utcDate = TAItoUTC(tai);
477
int daydiff = d.day - utcDate.day;
481
else if (daydiff > 1 || daydiff == -1)
485
d.utc_offset = (hourdiff + d.hour - utcDate.hour) * 3600
486
+ (d.minute - utcDate.minute) * 60;
487
d.tzname = localt.tm_isdst ? _("DST"): _("STD");
494
UTCtoTDB(const Date& utc)
496
return TTtoTDB(TAItoTT(UTCtoTAI(utc)));
503
JDUTCtoTAI(double utc)
505
double dAT = g_leapSeconds[0].seconds;
507
for (std::size_t i = g_leapSeconds.size() - 1; i > 0; i--)
509
if (utc > g_leapSeconds[i].t)
511
dAT = g_leapSeconds[i].seconds;
516
return utc + secsToDays(dAT);
521
TAItoJDUTC(double tai)
523
double dAT = g_leapSeconds[0].seconds;
525
for (std::size_t i = g_leapSeconds.size() - 1; i > 0; i--)
527
if (tai - secsToDays(g_leapSeconds[i - 1].seconds) > g_leapSeconds[i].t)
529
dAT = g_leapSeconds[i].seconds;
534
return tai - secsToDays(dAT);