Time Systems and Conversions¶
Purpose¶
This specification defines the mathematical contract for LuPNT’s time
systems and the conversions between them. It fixes the epoch origin, the
scale definitions, the exact offset/rate/periodic equations, the body and
reference-system each scale is proper to, and the leap-second and
Earth-orientation inputs. Every conversion is tied to the function that
implements it in cpp/lupnt/conversions/time_conversions.{h,cc}; the
Python surface is pnt.convert_time and friends from
python/bindings/py_time_converter.cc. The relativistic content follows
the author’s PhD thesis, Chapter 2.3 (Iiyama), and the Turyshev 2026 (ApJ
997:97) lunar-timescale formulation; equations are cited inline.
Epoch, Argument, and Unit Contract¶
Every scalar time in the API is a coordinate offset in seconds from the J2000 epoch of the corresponding scale, not a calendar date. The J2000 origin is
The Julian/Modified-Julian bridges are exact linear maps
(MjdToTime/TimeToMjd/JdToTime/TimeToJd):
// time_conversions.cc :: MjdToTime / TimeToMjd
Real MjdToTime(Real mjd) { return (mjd - MJD_J2000_TT) * SECS_DAY; }
Real TimeToMjd(Real t) { return t / SECS_DAY + MJD_J2000_TT; }
The Time enum (cpp/lupnt/core/constants.h) enumerates the supported
scales:
Conversion Graph¶
ConvertTime(t, from, to) (time_conversions.cc :: ConvertTime) routes
any pair through the canonical chain, with TAI/TT as the two hubs. The
vectorized overload ConvertTime(VecX, from, to) maps element-wise,
with special handling for the position-dependent lunar scales. The
Python bindings expose exactly this:
import pylupnt as pnt
t_tt = pnt.convert_time(t_tai, pnt.Time.TAI, pnt.Time.TT) # scalar
t = pnt.convert_time(t_vec, pnt.Time.GPS, pnt.Time.TDB) # VecX
Proper vs. Coordinate Time¶
Following the thesis (Ch. 2.3.1), a clock measures proper time
\(\tau\) along its worldline, while a coordinate time \(t\)
labels events in a 4-D relativistic reference system (BCRS, GCRS, LCRS) and
cannot be read off any single physical clock. IsCoordinateTimeScale
partitions the enum: TT, TCG, TDB, TCB, TCL, and LT are coordinate scales;
UT1, UTC, TAI, GPS are not.
// time_conversions.cc :: IsCoordinateTimeScale
case Time::TT: case Time::TCG: case Time::TDB:
case Time::TCB: case Time::TCL: case Time::LT: return true;
ConvertCoordinateTime is the coordinate-only entry point; its
position-aware overloads take a BCRS position \(x_\mathrm{bcrs}\) so
that the position-dependent terms of the TDB/TCB/TCL transforms
(below) can be evaluated at the event location rather than at the
body center.
Atomic and Terrestrial Scales¶
TAI -> TT is a defining constant offset (thesis Eq. 2.38):
// time_conversions.cc :: TaiToTt / TtToTai (TT_TAI_OFFSET = 32.184 s)
Real TaiToTt(Real t_tai) { return t_tai + TT_TAI_OFFSET; }
Real TtToTai(Real t_tt) { return t_tt - TT_TAI_OFFSET; }
TAI -> GPS is the fixed 19-second offset that removes the leap seconds
accumulated at the GPS epoch (thesis Eq. 2.39); GPS time runs without leap
seconds thereafter (TaiToGps/GpsToTai):
Note
Only GPS is implemented among the GNSS scales. The thesis also lists
GLONASS \(= \mathrm{TAI}-34\) s (leap-tracking, offset by UTC),
Galileo \(= \mathrm{TAI}-19\) s, and BeiDou \(= \mathrm{TAI}-33\)
s (Eqs. 2.40-2.42); these are not in the Time enum.
Earth-Rotation and Leap-Second Scales (UTC, UT1)¶
TAI <-> UTC applies the integer leap-second table
\(\Delta_\mathrm{AT}(\mathrm{MJD})\) from
interfaces/tai_utc.h (thesis Eq. 2.43):
UTC <-> UT1 adds the observed Earth-orientation difference
\((\mathrm{UT1}-\mathrm{UTC})\) looked up from the EOP table
(interfaces/eop.h):
// time_conversions.cc :: UtcToUt1
Real mjd_utc = t_utc / SECS_DAY + MJD_J2000_TT;
Real ut1_utc = GetUt1UtcDifference(mjd_utc);
return t_utc + ut1_utc;
UT1 in turn drives Earth rotation: EarthRotationAngle gives the ERA
\(\theta = 2\pi(0.7790572732640 + 1.00273781191135448\, t_\mathrm{UT1}/86400)\),
and GreenwichMeanSiderealTime/GreenwichApparentSiderealTime give GMST/GAST
(the latter adding the equation of the equinoxes). These are the only
rotation quantities keyed on UT1.
Barycentric and Geocentric Coordinate Scales¶
The four Einstein-scaled coordinate times share the reference epoch \(T_0 =\) 1977-01-01 00:00:00 TAI and the defining rates
with \(\mathrm{MJD}_{T_0} = \texttt{MJD\_COORDINATE\_TT\_TCG\_TCB}\).
TCG <-> TT (geocentric rate, thesis Eq. 2.37) removes the geoid potential secular rate:
// time_conversions.cc :: TtToTcg (inverse of Eq. 2.37 in offset form)
Real tt_tcg = -L_G / (1.0 - L_G) * (mjd_tt - MJD_COORDINATE_TT_TCG_TCB) * SECS_DAY;
return t_tt - tt_tcg;
TCB <-> TDB (barycentric rate + offset, thesis Eq. 2.32):
// time_conversions.cc :: TcbToTdb
Real t_tdb = t_tcb - L_B * (mjd_tcb - MJD_COORDINATE_TT_TCG_TCB) * SECS_DAY + TDB_0;
TT <-> TDB has two implementations that intentionally differ:
Forward
TtToTdbuses the analytic Fairhead/Moyer series (thesis Eqs. 2.33-2.34), \(M_E = (357.53 + 0.9856003\,d_\mathrm{J2000})^\circ\),\[\mathrm{TDB} = \mathrm{TT} + 0.001658\sin M_E + 0.000014\sin 2M_E\ \text{s} .\]Inverse
TDBToTt(no position) uses the NAIF single-term expansion \(\mathrm{TT} = \mathrm{TDB} - k\sin E\), with \(k=1.657\times10^{-3}\), eccentric anomaly \(E = M + e_b\sin M\), \(M = 6.239996 + 1.99096871\times10^{-7}\,t_\mathrm{TDB}\).
Note
The scalar TT<->TDB pair is a periodic approximation good to
\(\lesssim 2\) ms (thesis: “deviates by a periodic oscillation of
less than 2 milliseconds”). The position-aware overloads
TtToTdb(t, x_bcrs) / TDBToTt(t, x_bcrs) instead evaluate the full
GCRS<->BCRS 4-D transform (thesis Eq. 2.35, Turyshev Eq. 21/22): a
trapezoidal integral of \(c^{-2}\) and \(c^{-4}\) integrands built
from the Earth’s barycentric velocity and the external Solar-System
potential \(w_\mathrm{ext}=\sum_{B\neq E} GM_B/r_{EB}\), plus a
position term \(-v_E\!\cdot\!r_E/c^2\). TtToTdb(t, x_bcrs) inverts
this by 10-step Newton iteration. See
TdbToTtMinusTdbEq21 / IntegrateTdbToTtTerms.
Lunicentric Coordinate Scales (TCL, LT)¶
The Moon’s analogues follow Turyshev 2026, with LCRS as the lunar 4-D system. TCB -> TCL is the barycentric-to-lunicentric 4-D transform (thesis Eqs. 2.44-2.45; Turyshev Eq. 23/25), structurally identical to the Earth case with the Moon substituted:
where \(r_M = x_\mathrm{bcrs}-x_M\), \(v_M = dx_M/d\mathrm{TCB}\), and
\(w_\mathrm{ext}(x_M)=\sum_{B\neq M} GM_B/|x_M-x_B|\) sums the Sun,
Mercury, Venus, Earth, Mars, and the outer planets
(kTclExternalBodies). Passing no position places the clock at the lunar
center (\(v_M\!\cdot r_M = 0\)). TclToTcb inverts by Newton
iteration.
// time_conversions.cc :: TcbToTcl (Turyshev Eq. 23/25)
auto [i2, i4] = IntegrateTcbToTclTerms(t_tcb);
return t_tcb - i2/(C*C) - i4/(C*C*C*C) + TcbToTclPositionTerm(t_tcb, x_bcrs);
TCL <-> LT is the lunar analogue of TCG->TT, a defining rate on the selenoid (thesis Eqs. 2.48-2.49) with \(L_L = 3.13905\times10^{-11}\):
// time_conversions.cc :: TclToLt (T_L0 assumed = T_0)
Real lt_tcl = -L_L * (mjd_tcl - MJD_COORDINATE_TT_TCG_TCB) * SECS_DAY;
return t_tcl + lt_tcl;
Note
\(L_L\) is not yet internationally standardized; the code uses the Turyshev selenoid value \(3.13905\times10^{-11}\) (potential \(\Phi_L = 2.82123744381\times10^{6}\ \mathrm{m^2/s^2}\)), whereas Kopeikin adopts \(3.14027\times10^{-11}\) (thesis Ch. 2.3.5).
Lunar Time as a Function of TT (direct path)¶
Rather than chaining TT->TDB->TCB->TCL->LT, LuPNT implements the closed-form
\(\mathrm{TL}-\mathrm{TT}(\mathrm{TDB})\) of Turyshev 2026 Eq. 57 (the
TDB-argument form of thesis Eq. 2.50) in TdbToLtMinusTt. The integral is
a trapezoidal sweep at \(dt = 0.01\) day. For repeated queries in a
window, InitLtMinusTtFit fits piecewise Chebyshev segments (degree 12,
1-day segments) over the window in a single forward sweep and caches them in
s_lt_minus_tt_fit; TdbToLtMinusTt then uses the fast path
(ChebyshevFitModel::Eval, numerics/cheby_fit.h). TdbToLt composes
\(\mathrm{TL} = \mathrm{TT}(\mathrm{TDB}) + (\mathrm{TL}-\mathrm{TT})\);
LtToTdb/LtToTt invert by 10-step Newton iteration.
// time_conversions.cc :: TdbToLt / TtToLt
Real TdbToLt(Real t_tdb) { return TDBToTt(t_tdb) + TdbToLtMinusTt(t_tdb); }
Real TtToLt(Real t_tt) { return TdbToLt(TtToTdb(t_tt)); }
A separate lunar-surface proper-time term is provided by
GetProperTimeCorrectionTcl(t_tcg, x_mci), the first-order clock
correction \(-v_{LE}\cdot(r_x - r_{LE})/c^2\) for a clock at MCI
position \(x_\mathrm{mci}\) relative to the Moon’s geocentric motion.
Conversion of Physical Quantities and Constants¶
Because the scaled coordinate times (TT, TDB, LT) run at rates \((1-L_G)\), \((1-L_B)\), \((1-L_L)\) relative to their unscaled partners (TCG, TCB, TCL), spatial coordinates and gravitational parameters must be rescaled to keep \(c\) and the equations of motion invariant (thesis Eqs. 2.52-2.57):
LuPNT encodes these as the CoordinateScale factors
\(1-L_x\) (constants.h :: CoordinateScaleFactor), with
CoordinateScaleRatio(from, to) returning
\((1-L_\mathrm{to})/(1-L_\mathrm{from})\) for the rescale, guarded by
AreCoordinateScalesConvertible (only barycentric<->barycentric,
geocentric<->geocentric, lunicentric<->lunicentric pairs share a constant
factor).
// constants.h :: CoordinateScaleFactor
case CoordinateScale::TDB: return 1.0 - L_B;
case CoordinateScale::TT: return 1.0 - L_G;
case CoordinateScale::TL: return 1.0 - L_L;
Model Boundaries¶
All API times are seconds from the per-scale J2000 origin; calendar I/O goes through
GregorianToTime/TimeToGregorianString.Only GPS is implemented among GNSS scales; GLONASS/Galileo/BeiDou offsets are documented but not exposed.
The scalar TT<->TDB and TDB->TT paths are periodic approximations (\(\lesssim 2\) ms); sub-microsecond and position-dependent work must use the
x_bcrsoverloads viaConvertCoordinateTime.TCL/TDB position-aware transforms integrate from \(T_0\) per call unless a Chebyshev fit window is installed (LT only, via
InitLtMinusFit).\(L_L\) (and hence LT) is provisional pending international standardization.