@tubular/time
Version:
Date/time, IANA timezones, leap seconds, TAI/UTC conversions, calendar with settable Julian/Gregorian switchover
216 lines • 11 kB
JavaScript
import { ceil, floor, max, round } from '@tubular/math';
import { clone } from '@tubular/util';
import { DAY_MSEC, DAY_SEC, DELTA_TDT_DAYS, DELTA_TDT_MSEC, JD_J2000, UNIX_TIME_ZERO_AS_JULIAN_DAY, setDeltaTUpdater } from './common';
import { Timezone } from './timezone';
import { getDateFromDayNumber_SGC, getDayNumber_SGC } from './calendar';
/* eslint-disable @typescript-eslint/indent, comma-spacing, space-infix-ops */
const baseHistoricDeltaT = [
// Values to smooth transition from polynomial used for earlier years.
// 1580-1599
130.8, 129.7, 128.6, 127.5, 126.4, 125.4, 124.3, 123.2, 122.1, 121.0,
119.9, 118.8, 117.7, 116.6, 115.5, 114.5, 113.4, 112.3, 111.2, 110.1,
// From http://astro.ukho.gov.uk/nao/lvm/, with interpolated between-decade values.
// 1600-1649
109, 107, 106, 104, 103, 101, 99.7, 98.2, 96.8, 95.4,
94.0, 92.6, 91.2, 89.8, 88.4, 87.0, 85.6, 84.2, 82.8, 81.4,
80.0, 78.6, 77.2, 75.7, 74.3, 72.9, 71.5, 70.1, 68.7, 67.3,
66.0, 64.7, 63.4, 62.2, 60.9, 59.7, 58.5, 57.4, 56.2, 55.1,
54.0, 52.9, 51.9, 50.8, 49.8, 48.8, 47.8, 46.9, 45.9, 45.0,
// 1650-1699
44.0, 43.1, 42.1, 41.2, 40.2, 39.3, 38.4, 37.5, 36.7, 35.8,
35.0, 34.2, 33.5, 32.7, 32.0, 31.3, 30.6, 30.0, 29.3, 28.6,
28.0, 27.4, 26.7, 26.1, 25.5, 24.9, 24.3, 23.7, 23.1, 22.6,
22.0, 21.4, 20.9, 20.4, 19.8, 19.3, 18.8, 18.3, 17.9, 17.4,
17.0, 16.6, 16.3, 15.9, 15.6, 15.3, 15.0, 14.8, 14.5, 14.3,
// 1700-1749
14.0, 13.8, 13.5, 13.3, 13.0, 12.8, 12.6, 12.4, 12.3, 12.1,
12.0, 11.9, 11.9, 11.8, 11.8, 11.8, 11.8, 11.9, 11.9, 11.9,
12.0, 12.1, 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7, 12.8,
13.0, 13.2, 13.3, 13.5, 13.7, 13.9, 14.1, 14.4, 14.6, 14.8,
15.0, 15.2, 15.4, 15.6, 15.8, 16.0, 16.2, 16.4, 16.6, 16.8,
// 1750-1799
17.0, 17.2, 17.4, 17.6, 17.8, 18.0, 18.2, 18.4, 18.6, 18.8,
19.0, 19.2, 19.5, 19.7, 19.9, 20.1, 20.4, 20.6, 20.7, 20.9,
21.0, 21.1, 21.1, 21.1, 21.1, 21.1, 21.1, 21.1, 21.0, 21.0,
21.0, 21.0, 21.0, 21.1, 21.1, 21.2, 21.2, 21.2, 21.2, 21.1,
21.0, 20.9, 20.7, 20.5, 20.2, 19.9, 19.7, 19.3, 19.0, 18.7,
// From http://astro.ukho.gov.uk/nao/lvm/
// 1800-1849
18.4, 18.0, 17.6, 17.3, 16.9, 16.6, 16.3, 16.0, 15.8, 15.7,
15.7, 15.7, 15.8, 16.0, 16.2, 16.4, 16.5, 16.7, 16.7, 16.7,
16.5, 16.2, 15.8, 15.3, 14.8, 14.1, 13.5, 12.8, 12.1, 11.4,
10.8, 10.2, 9.7, 9.3, 8.9, 8.5, 8.2, 8.0, 7.8, 7.7,
7.6, 7.6, 7.7, 7.7, 7.9, 8.0, 8.2, 8.5, 8.7, 9.0,
// 1850-1899
9.3, 9.67, 9.98, 10.23, 10.37, 10.36, 10.18, 9.88, 9.54, 9.24,
9.04, 8.99, 9.01, 8.97, 8.76, 8.25, 7.38, 6.22, 4.92, 3.59,
2.37, 1.36, 0.56, -0.10, -0.65, -1.13, -1.58, -2.01, -2.43, -2.83,
-3.21, -3.58, -3.91, -4.17, -4.34, -4.39, -4.31, -4.14, -3.97, -3.86,
-3.88, -4.07, -4.37, -4.69, -4.93, -5.02, -4.87, -4.48, -3.86, -3.02,
// 1900-1949
-1.98, -0.75, 0.62, 2.06, 3.51, 4.92, 6.24, 7.49, 8.70, 9.90,
11.14, 12.43, 13.75, 15.06, 16.32, 17.48, 18.52, 19.44, 20.25, 20.98,
21.62, 22.19, 22.69, 23.12, 23.49, 23.79, 24.02, 24.20, 24.32, 24.39,
24.42, 24.41, 24.38, 24.32, 24.25, 24.16, 24.08, 24.04, 24.06, 24.17,
24.43, 24.83, 25.35, 25.92, 26.51, 27.05, 27.51, 27.89, 28.24, 28.58,
// From http://astro.ukho.gov.uk/nao/lvm/, mid-year values omitted
// 1950-1991
28.93, 29.32, 29.70, 30.00, 30.20, 30.41, 30.76, 31.34, 32.03, 32.65,
33.07, 33.36, 33.62, 33.96, 34.44, 35.09, 35.95, 36.93, 37.96, 38.95,
39.93, 40.95, 42.04, 43.15, 44.24, 45.28, 46.28, 47.29, 48.33, 49.37,
50.36, 51.28, 52.13, 52.94, 53.70, 54.39, 54.98, 55.46, 55.89, 56.37,
56.99, 57.70,
// Derived from https://datacenter.iers.org/data/latestVersion/finals.data.iau2000.txt
// 1992-1999
58.31, 59.12, 59.98, 60.79, 61.63, 62.30, 62.97, 63.47,
// 2000-2019
63.83, 64.09, 64.30, 64.47, 64.57, 64.69, 64.85, 65.15, 65.46, 65.78,
66.07, 66.32, 66.60, 66.91, 67.28, 67.64, 68.10, 68.59, 68.97, 69.22
// From 2020 onward, data from timezone files, via updateDeltaTs().
// Additional data from https://datacenter.iers.org/data/latestVersion/finals.data.iau2000.txt,
// as linked to from https://www.iers.org/IERS/EN/DataProducts/EarthOrientationData/eop.html.
// ΔT = 32.184† + (TAI - UTC)‡ - (UT1 - UTC)§
// † TT - TAI (Terrestrial Time minus International Atomic Time), a constant value.
// ‡ 37 seconds as of 2021-11-21, as it will likely remain for some time.
// § From finals.data, numeric value starting at 59th character column.
];
let historicDeltaT = clone(baseHistoricDeltaT);
let calibration = 0;
let lastTableYear = -1;
const preKnownLeapSeconds = getDayNumber_SGC(1958, 1, 1) + UNIX_TIME_ZERO_AS_JULIAN_DAY;
let safeTaiLow = Number.MIN_SAFE_INTEGER, safeTaiHigh = Number.MAX_SAFE_INTEGER;
let safeUtcLow = Number.MIN_SAFE_INTEGER, safeUtcHigh = Number.MAX_SAFE_INTEGER;
let postKnownLeapSeconds;
updateDeltaTs();
export function updateDeltaTs(post2019values, lastKnownLeapSecond) {
if (!post2019values)
post2019values = [69.36, 69.36];
historicDeltaT = clone(baseHistoricDeltaT);
historicDeltaT.push(...post2019values);
calibration = 0;
lastTableYear = -1;
let lastDay = Date.now() / DAY_MSEC;
if (lastKnownLeapSecond)
lastDay = max(lastDay, getDayNumber_SGC(lastKnownLeapSecond));
const lastYMD = getDateFromDayNumber_SGC(lastDay);
const afterLeaps = getDayNumber_SGC({ y: lastYMD.y + 1, m: lastYMD.m < 7 ? 1 : 7, d: 1 });
postKnownLeapSeconds = afterLeaps + UNIX_TIME_ZERO_AS_JULIAN_DAY;
safeUtcLow = ceil((preKnownLeapSeconds + 1 - UNIX_TIME_ZERO_AS_JULIAN_DAY) * DAY_MSEC);
safeUtcHigh = floor((afterLeaps - 1) * DAY_MSEC);
safeTaiLow = utToTaiMillis(safeUtcLow);
safeTaiHigh = utToTaiMillis(safeUtcHigh);
}
setDeltaTUpdater(updateDeltaTs);
export function isSafeTaiMillis(tai) {
return safeTaiLow < tai && tai < safeTaiHigh;
}
export function isSafeUtcMillis(utc) {
return safeUtcLow < utc && utc < safeUtcHigh;
}
export function getDeltaTAtJulianDate(timeJDE) {
const year = (timeJDE - JD_J2000) / 365.25 + 2000.0;
// Do a three-point interpolation from either the table or the computed values.
const tableMidYear = floor(year);
const dt1 = deltaTAtStartOfYear(tableMidYear - 1);
const dt2 = deltaTAtStartOfYear(tableMidYear);
const dt3 = deltaTAtStartOfYear(tableMidYear + 1);
const a = dt2 - dt1;
const b = dt3 - dt2;
const c = b - a;
const n = year - tableMidYear;
return dt2 + n * (a + b + n * c) / 2.0;
}
export function getDeltaTAtTaiMillis(millis) {
return getDeltaTAtJulianDate(millis / DAY_MSEC + UNIX_TIME_ZERO_AS_JULIAN_DAY);
}
export function utToTdt(timeJDU) {
let timeJDE = timeJDU;
for (let i = 0; i < 5; ++i)
timeJDE = timeJDU + getDeltaTAtJulianDate(timeJDE) / DAY_SEC;
return timeJDE;
}
export function utToTai(timeJDU, asUtc = false) {
var _a, _b;
let utcMillis;
let deltaTai;
if (asUtc && preKnownLeapSeconds - 365 <= timeJDU && timeJDU <= postKnownLeapSeconds + 365) {
utcMillis = round((timeJDU - UNIX_TIME_ZERO_AS_JULIAN_DAY) * DAY_MSEC);
deltaTai = (_b = (_a = Timezone.findDeltaTaiFromUtc(utcMillis)) === null || _a === void 0 ? void 0 : _a.deltaTai) !== null && _b !== void 0 ? _b : 0;
}
if (asUtc && preKnownLeapSeconds <= timeJDU && timeJDU <= postKnownLeapSeconds)
return timeJDU + deltaTai / DAY_SEC;
const tai = utToTdt(timeJDU) - DELTA_TDT_DAYS;
if (!asUtc || timeJDU < preKnownLeapSeconds - 365 || timeJDU > postKnownLeapSeconds + 365)
return tai;
const weight = (timeJDU <= preKnownLeapSeconds ? preKnownLeapSeconds - timeJDU : timeJDU - postKnownLeapSeconds);
return ((timeJDU + deltaTai / DAY_SEC) * (365 - weight) + tai * weight) / 365;
}
export function utToTaiMillis(millis, asUtc = false) {
if (isSafeUtcMillis(millis))
return round((utToTai(millis / DAY_MSEC + UNIX_TIME_ZERO_AS_JULIAN_DAY, asUtc) -
UNIX_TIME_ZERO_AS_JULIAN_DAY) * DAY_MSEC);
else
return (utToTai(millis / DAY_MSEC + UNIX_TIME_ZERO_AS_JULIAN_DAY, asUtc) -
UNIX_TIME_ZERO_AS_JULIAN_DAY) * DAY_MSEC;
}
export function tdtToUt(timeJDE) {
return timeJDE - getDeltaTAtJulianDate(timeJDE) / DAY_SEC;
}
export function tdtDaysToTaiMillis(timeJDE) {
return (timeJDE - UNIX_TIME_ZERO_AS_JULIAN_DAY) * DAY_MSEC - DELTA_TDT_MSEC;
}
export function taiDaysToUt(timeJDE) {
return tdtToUt(timeJDE + DELTA_TDT_DAYS);
}
export function taiMillisToTdt(millis) {
return (millis + DELTA_TDT_MSEC) / DAY_MSEC + UNIX_TIME_ZERO_AS_JULIAN_DAY;
}
export function taiToUtMillis(millis, forUtc = false) {
var _a, _b;
const tdt = millis / DAY_MSEC + UNIX_TIME_ZERO_AS_JULIAN_DAY + DELTA_TDT_DAYS;
const timeJDU = tdtToUt(tdt);
const jduMillis = (timeJDU - UNIX_TIME_ZERO_AS_JULIAN_DAY) * DAY_MSEC;
if (!forUtc || timeJDU < preKnownLeapSeconds - 365 || timeJDU > postKnownLeapSeconds + 365)
return jduMillis;
const deltaTai = (_b = (_a = Timezone.findDeltaTaiFromTai(millis)) === null || _a === void 0 ? void 0 : _a.deltaTai) !== null && _b !== void 0 ? _b : 0;
let utMillis = millis - deltaTai * 1000;
if (preKnownLeapSeconds > timeJDU || timeJDU > postKnownLeapSeconds) {
const weight = (timeJDU <= preKnownLeapSeconds ? preKnownLeapSeconds - timeJDU : timeJDU - postKnownLeapSeconds);
utMillis = round((utMillis * (365 - weight) + jduMillis * weight) / 365);
}
return isSafeUtcMillis(utMillis) ? round(utMillis) : utMillis;
}
function deltaTAtStartOfYear(year) {
// Make the post-table approximations line up with the last tabular delta T.
if (lastTableYear < 0) {
lastTableYear = historicDeltaT.length + 1578; // Temporarily 1 less than it should be
calibration = historicDeltaT[historicDeltaT.length - 1] - deltaTAtStartOfYear(lastTableYear + 1);
++lastTableYear;
}
// Polynomial expressions from http://eclipsewise.com/help/deltatpoly2014.html
let t, u;
if (year < -500) {
u = (year - 1820) / 100;
return -20 + 32 * u ** 2;
}
else if (year < 500) {
u = year / 100.0;
return 10583.6 - 1014.41 * u + 33.78311 * u ** 2 - 5.952053 * u ** 3
- 0.1798452 * u ** 4 + 0.022174192 * u ** 5 + 0.0090316521 * u ** 6;
}
else if (year <= 1580) {
u = (year - 1000.0) / 100.0;
return 1574.2 - 556.01 * u + 71.23472 * u ** 2 + 0.319781 * u ** 3
- 0.8503463 * u ** 4 - 0.005050998 * u ** 5 + 0.0083572073 * u ** 6;
}
else if (year <= lastTableYear)
return historicDeltaT[year - 1580];
else if (year < 3000) {
t = year - 2015;
return calibration + 67.69 + 0.3645 * t + 0.0039755 * t ** 2;
}
u = (year - 1820.0) / 100.0;
// Changed -20 in original expression to -171.82, so result matches above formula at year 3000
return calibration - 171.82 + 32 * u ** 2;
}
//# sourceMappingURL=ut-converter.js.map