Skip to main content

deep_time/utc/
historical.rs

1//! Pre-1972 TAI−UTC historical offsets and linear drift rates
2//! from the official USNO `tai-utc.dat` (used by IAU SOFA).
3//!
4//! Provides the piecewise-linear formula for UTC instants before 1972-01-01.
5
6use crate::{Dt, Real};
7
8/// Holds info about a pre-1972 historical UTC offset interval. Used by
9/// [UTC_HIST_TABLE](constant.UTC_HIST_TABLE.html).
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct UtcHistRow {
12    /// Year of the effective UTC date
13    pub yr: i32,
14    /// Month (1-12) of the effective UTC date
15    pub mo: u8,
16    /// Day of the effective UTC date
17    pub day: u8,
18    /// Julian Date (JD) at 0h UT on the effective date (start of interval)
19    pub jd: Real,
20    /// Reference MJD used in the linear drift formula: offset + (MJD - mjd_ref) * drift
21    pub mjd_ref: Real,
22    /// Constant offset (seconds) in the TAI−UTC formula
23    pub offset: Real,
24    /// Drift rate (seconds per day) — this is the historical frequency offset effect
25    pub drift: Real,
26    // jd for the date, includes computed and added sofa offset
27    // pub jd_added: Real,
28    // jd for the date, includes computed and subtracted sofa offset
29    // pub jd_subbed: Real,
30    // /// tai mjd for the date, includes calculated sofa offset
31    // pub tai_mjd: Real,
32}
33
34/// Authoritative pre-1972 TAI−UTC entries from the official USNO `tai-utc.dat`
35/// [file](https://maia.usno.navy.mil/ser7/tai-utc.dat).  
36/// These 13 intervals contain the frequency offsets and linear drifts used by the IAU SOFA library
37/// and all other high-precision astronomy/time-conversion software before UTC switched to pure leap-second mode.
38pub const UTC_HIST_TABLE: &[UtcHistRow] = &[
39    UtcHistRow {
40        yr: 1961,
41        mo: 1,
42        day: 1,
43        jd: 2437300.5,
44        mjd_ref: 37300.0,
45        offset: 1.4228180,
46        drift: 0.001296,
47        // jd_added: 2437300.5000164676,
48        // jd_subbed: 2437300.4999835324,
49        // tai_mjd: 37300.0000164678,
50    },
51    UtcHistRow {
52        yr: 1961,
53        mo: 8,
54        day: 1,
55        jd: 2437512.5,
56        mjd_ref: 37300.0,
57        offset: 1.3728180,
58        drift: 0.001296,
59        // jd_added: 2437512.5000190693,
60        // jd_subbed: 2437512.4999809307,
61        // tai_mjd: 37512.0000190691,
62    },
63    UtcHistRow {
64        yr: 1962,
65        mo: 1,
66        day: 1,
67        jd: 2437665.5,
68        mjd_ref: 37665.0,
69        offset: 1.8458580,
70        drift: 0.0011232,
71        // jd_added: 2437665.500021364,
72        // jd_subbed: 2437665.499978636,
73        // tai_mjd: 37665.000021364096,
74    },
75    UtcHistRow {
76        yr: 1963,
77        mo: 11,
78        day: 1,
79        jd: 2438334.5,
80        mjd_ref: 37665.0,
81        offset: 1.9458580,
82        drift: 0.0011232,
83        // jd_added: 2438334.5000312184,
84        // jd_subbed: 2438334.4999687816,
85        // tai_mjd: 38334.00003121851,
86    },
87    UtcHistRow {
88        yr: 1964,
89        mo: 1,
90        day: 1,
91        jd: 2438395.5,
92        mjd_ref: 38761.0,
93        offset: 3.2401300,
94        drift: 0.001296,
95        // jd_added: 2438395.5000320114,
96        // jd_subbed: 2438395.4999679886,
97        // tai_mjd: 38395.00003201151,
98    },
99    UtcHistRow {
100        yr: 1964,
101        mo: 4,
102        day: 1,
103        jd: 2438486.5,
104        mjd_ref: 38761.0,
105        offset: 3.3401300,
106        drift: 0.001296,
107        // jd_added: 2438486.500034534,
108        // jd_subbed: 2438486.499965466,
109        // tai_mjd: 38486.000034533914,
110    },
111    UtcHistRow {
112        yr: 1964,
113        mo: 9,
114        day: 1,
115        jd: 2438639.5,
116        mjd_ref: 38761.0,
117        offset: 3.4401300,
118        drift: 0.001296,
119        // jd_added: 2438639.5000379863,
120        // jd_subbed: 2438639.4999620137,
121        // tai_mjd: 38639.00003798632,
122    },
123    UtcHistRow {
124        yr: 1965,
125        mo: 1,
126        day: 1,
127        jd: 2438761.5,
128        mjd_ref: 38761.0,
129        offset: 3.5401300,
130        drift: 0.001296,
131        // jd_added: 2438761.5000409735,
132        // jd_subbed: 2438761.4999590265,
133        // tai_mjd: 38761.000040973726,
134    },
135    UtcHistRow {
136        yr: 1965,
137        mo: 3,
138        day: 1,
139        jd: 2438820.5,
140        mjd_ref: 38761.0,
141        offset: 3.6401300,
142        drift: 0.001296,
143        // jd_added: 2438820.500043016,
144        // jd_subbed: 2438820.499956984,
145        // tai_mjd: 38820.00004301613,
146    },
147    UtcHistRow {
148        yr: 1965,
149        mo: 7,
150        day: 1,
151        jd: 2438942.5,
152        mjd_ref: 38761.0,
153        offset: 3.7401300,
154        drift: 0.001296,
155        // jd_added: 2438942.5000460036,
156        // jd_subbed: 2438942.4999539964,
157        // tai_mjd: 38942.000046003544,
158    },
159    UtcHistRow {
160        yr: 1965,
161        mo: 9,
162        day: 1,
163        jd: 2439004.5,
164        mjd_ref: 38761.0,
165        offset: 3.8401300,
166        drift: 0.001296,
167        // jd_added: 2439004.500048091,
168        // jd_subbed: 2439004.499951909,
169        // tai_mjd: 39004.00004809095,
170    },
171    UtcHistRow {
172        yr: 1966,
173        mo: 1,
174        day: 1,
175        jd: 2439126.5,
176        mjd_ref: 39126.0,
177        offset: 4.3131700,
178        drift: 0.002592,
179        // jd_added: 2439126.5000499208,
180        // jd_subbed: 2439126.4999500792,
181        // tai_mjd: 39126.00004992095,
182    },
183    UtcHistRow {
184        yr: 1968,
185        mo: 2,
186        day: 1,
187        jd: 2439887.5,
188        mjd_ref: 39126.0,
189        offset: 4.2131700,
190        drift: 0.002592,
191        // jd_added: 2439887.5000715936,
192        // jd_subbed: 2439887.4999284064,
193        // tai_mjd: 39887.00007159354,
194    },
195];
196
197impl Dt {
198    /// Returns the SOFA historical TAI−UTC offset (in seconds)
199    /// for an **un-adjusted instant**.
200    ///
201    /// - Only for instants that have **not** already been offset
202    ///   by a historical UTC offset value.
203    /// - Not as accurate as ERFA / Astropy.
204    /// - **Do not use this for round tripping.**
205    /// - Unlike ERFA it does **not** support dates between 1960
206    ///   and 1961.
207    pub const fn historical_utc_offset(&self) -> Option<Real> {
208        // < 1961-1-1 midnight, or >= 1972-1-1 midnight
209        if self.to_attos() < -1230724800000000000000000000
210        // tai attos for 1972 (10 leap seconds added)
211        || self.to_attos() >= -883655990000000000000000000
212        {
213            return None;
214        }
215        // if self.to_attos() >= -883656000000000000000000000 {
216        //     return None;
217        // }
218        let jd = self.to_jd_f_raw();
219        let mjd = self.to_mjd_f_raw();
220        let len = UTC_HIST_TABLE.len();
221        let mut i = len;
222        while i > 0 {
223            i -= 1;
224            let entry = &UTC_HIST_TABLE[i];
225            if jd >= entry.jd {
226                let offset = entry.offset + (mjd - entry.mjd_ref) * entry.drift;
227                return Some(offset);
228            }
229        }
230
231        None
232    }
233}