Skip to main content

tempoch_core/
julian_date_ext.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! Julian Date (`Time<JD>`) specific extensions.
5
6use qtty::*;
7use std::ops::Add;
8
9use super::instant::Time;
10use super::scales::{JD, MJD};
11
12impl Time<JD> {
13    /// J2000.0 epoch: 2000-01-01T12:00:00 TT  (JD 2 451 545.0).
14    pub const J2000: Self = Self::new(2_451_545.0);
15
16    /// One Julian year expressed in days.
17    pub const JULIAN_YEAR: Days = Days::new(365.25);
18
19    /// One Julian century expressed in days.
20    pub const JULIAN_CENTURY: Days = Days::new(36_525.0);
21
22    /// One Julian millennium expressed in days.
23    pub const JULIAN_MILLENNIUM: Days = Days::new(365_250.0);
24
25    /// Julian millennia since J2000.0 (used by VSOP87).
26    #[inline]
27    pub fn julian_millennias(&self) -> Millennia {
28        Millennia::new(
29            ((*self - Self::J2000) / Self::JULIAN_MILLENNIUM)
30                .simplify()
31                .value(),
32        )
33    }
34
35    /// Julian centuries since J2000.0 (used by nutation, precession, sidereal time).
36    #[inline]
37    pub fn julian_centuries(&self) -> Centuries {
38        Centuries::new(
39            ((*self - Self::J2000) / Self::JULIAN_CENTURY)
40                .simplify()
41                .value(),
42        )
43    }
44
45    /// Julian years since J2000.0.
46    #[inline]
47    pub fn julian_years(&self) -> JulianYears {
48        JulianYears::new(
49            ((*self - Self::J2000) / Self::JULIAN_YEAR)
50                .simplify()
51                .value(),
52        )
53    }
54
55    /// Converts JD(TT) → JD(TDB) using the Fairhead & Bretagnon (1990)
56    /// expression for `TDB − TT`.
57    ///
58    /// The dominant term has an amplitude of ≈1.658 ms. This implementation
59    /// includes the four largest periodic terms plus a secular component,
60    /// matching the formula recommended by USNO Circular 179 (Kaplan 2005)
61    /// and consistent with IAU 2006 Resolution B3.
62    ///
63    /// Accuracy: better than 30 μs for dates within ±10 000 years of J2000.
64    ///
65    /// ## References
66    /// * Fairhead & Bretagnon (1990), A&A 229, 240
67    /// * USNO Circular 179, eq. 2.6
68    /// * SOFA `iauDtdb` (full implementation has hundreds of terms)
69    pub fn tt_to_tdb(jd_tt: Self) -> Self {
70        jd_tt + super::scales::tdb_minus_tt_days(jd_tt.quantity())
71    }
72
73    /// Convenience: MJD value corresponding to this JD.
74    ///
75    /// Kept as a convenience wrapper for `self.to::<MJD>()`.
76    #[inline]
77    pub fn to_mjd(&self) -> Time<MJD> {
78        self.to::<MJD>()
79    }
80}
81
82// ── From / Into conversions for qtty time quantities (on JD only) ────────
83
84impl Add<Years> for Time<JD> {
85    type Output = Self;
86    fn add(self, years: Years) -> Self {
87        // Treat `Years` here as Julian years for JD arithmetic stability.
88        self + Days::new(years.value() * Self::JULIAN_YEAR.value())
89    }
90}
91
92impl From<JulianYears> for Time<JD> {
93    fn from(years: JulianYears) -> Self {
94        Self::J2000 + years.to::<Day>()
95    }
96}
97
98impl From<Time<JD>> for JulianYears {
99    fn from(jd: Time<JD>) -> Self {
100        jd.julian_years()
101    }
102}
103
104impl From<Centuries> for Time<JD> {
105    fn from(centuries: Centuries) -> Self {
106        // `Centuries` are interpreted as Julian centuries relative to J2000.
107        Self::J2000 + Days::new(centuries.value() * Self::JULIAN_CENTURY.value())
108    }
109}
110
111impl From<Time<JD>> for Centuries {
112    fn from(jd: Time<JD>) -> Self {
113        jd.julian_centuries()
114    }
115}
116
117impl From<Millennia> for Time<JD> {
118    fn from(millennia: Millennia) -> Self {
119        // `Millennia` are interpreted as Julian millennia relative to J2000.
120        Self::J2000 + Days::new(millennia.value() * Self::JULIAN_MILLENNIUM.value())
121    }
122}
123
124impl From<Time<JD>> for Millennia {
125    fn from(jd: Time<JD>) -> Self {
126        jd.julian_millennias()
127    }
128}