Skip to main content

deep_time/
historical_sofa.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#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct TaiUtcPre1972 {
10    /// Year of the effective UTC date
11    pub yr: i32,
12    /// Month (1-12) of the effective UTC date
13    pub mo: u8,
14    /// Day of the effective UTC date
15    pub day: u8,
16    /// Julian Date (JD) at 0h UT on the effective date (start of interval)
17    pub jd: Real,
18    /// Reference MJD used in the linear drift formula: offset + (MJD - mjd_ref) * drift
19    pub mjd_ref: Real,
20    /// Constant offset (seconds) in the TAI−UTC formula
21    pub offset: Real,
22    /// Drift rate (seconds per day) — this is the historical frequency offset effect
23    pub drift: Real,
24    // jd for the date, includes computed and added sofa offset
25    // pub jd_added: Real,
26    // jd for the date, includes computed and subtracted sofa offset
27    // pub jd_subbed: Real,
28    // /// tai mjd for the date, includes calculated sofa offset
29    // pub tai_mjd: Real,
30}
31
32/// Authoritative pre-1972 TAI−UTC entries from the official USNO `tai-utc.dat`
33/// [file](https://maia.usno.navy.mil/ser7/tai-utc.dat).  
34/// These 13 intervals contain the frequency offsets and linear drifts used by the IAU SOFA library
35/// and all other high-precision astronomy/time-conversion software before UTC switched to pure leap-second mode.
36pub const SOFA_TAI_UTC_PRE_1972: &[TaiUtcPre1972] = &[
37    TaiUtcPre1972 {
38        yr: 1961,
39        mo: 1,
40        day: 1,
41        jd: 2437300.5,
42        mjd_ref: 37300.0,
43        offset: 1.4228180,
44        drift: 0.001296,
45        // jd_added: 2437300.5000164676,
46        // jd_subbed: 2437300.4999835324,
47        // tai_mjd: 37300.0000164678,
48    },
49    TaiUtcPre1972 {
50        yr: 1961,
51        mo: 8,
52        day: 1,
53        jd: 2437512.5,
54        mjd_ref: 37300.0,
55        offset: 1.3728180,
56        drift: 0.001296,
57        // jd_added: 2437512.5000190693,
58        // jd_subbed: 2437512.4999809307,
59        // tai_mjd: 37512.0000190691,
60    },
61    TaiUtcPre1972 {
62        yr: 1962,
63        mo: 1,
64        day: 1,
65        jd: 2437665.5,
66        mjd_ref: 37665.0,
67        offset: 1.8458580,
68        drift: 0.0011232,
69        // jd_added: 2437665.500021364,
70        // jd_subbed: 2437665.499978636,
71        // tai_mjd: 37665.000021364096,
72    },
73    TaiUtcPre1972 {
74        yr: 1963,
75        mo: 11,
76        day: 1,
77        jd: 2438334.5,
78        mjd_ref: 37665.0,
79        offset: 1.9458580,
80        drift: 0.0011232,
81        // jd_added: 2438334.5000312184,
82        // jd_subbed: 2438334.4999687816,
83        // tai_mjd: 38334.00003121851,
84    },
85    TaiUtcPre1972 {
86        yr: 1964,
87        mo: 1,
88        day: 1,
89        jd: 2438395.5,
90        mjd_ref: 38761.0,
91        offset: 3.2401300,
92        drift: 0.001296,
93        // jd_added: 2438395.5000320114,
94        // jd_subbed: 2438395.4999679886,
95        // tai_mjd: 38395.00003201151,
96    },
97    TaiUtcPre1972 {
98        yr: 1964,
99        mo: 4,
100        day: 1,
101        jd: 2438486.5,
102        mjd_ref: 38761.0,
103        offset: 3.3401300,
104        drift: 0.001296,
105        // jd_added: 2438486.500034534,
106        // jd_subbed: 2438486.499965466,
107        // tai_mjd: 38486.000034533914,
108    },
109    TaiUtcPre1972 {
110        yr: 1964,
111        mo: 9,
112        day: 1,
113        jd: 2438639.5,
114        mjd_ref: 38761.0,
115        offset: 3.4401300,
116        drift: 0.001296,
117        // jd_added: 2438639.5000379863,
118        // jd_subbed: 2438639.4999620137,
119        // tai_mjd: 38639.00003798632,
120    },
121    TaiUtcPre1972 {
122        yr: 1965,
123        mo: 1,
124        day: 1,
125        jd: 2438761.5,
126        mjd_ref: 38761.0,
127        offset: 3.5401300,
128        drift: 0.001296,
129        // jd_added: 2438761.5000409735,
130        // jd_subbed: 2438761.4999590265,
131        // tai_mjd: 38761.000040973726,
132    },
133    TaiUtcPre1972 {
134        yr: 1965,
135        mo: 3,
136        day: 1,
137        jd: 2438820.5,
138        mjd_ref: 38761.0,
139        offset: 3.6401300,
140        drift: 0.001296,
141        // jd_added: 2438820.500043016,
142        // jd_subbed: 2438820.499956984,
143        // tai_mjd: 38820.00004301613,
144    },
145    TaiUtcPre1972 {
146        yr: 1965,
147        mo: 7,
148        day: 1,
149        jd: 2438942.5,
150        mjd_ref: 38761.0,
151        offset: 3.7401300,
152        drift: 0.001296,
153        // jd_added: 2438942.5000460036,
154        // jd_subbed: 2438942.4999539964,
155        // tai_mjd: 38942.000046003544,
156    },
157    TaiUtcPre1972 {
158        yr: 1965,
159        mo: 9,
160        day: 1,
161        jd: 2439004.5,
162        mjd_ref: 38761.0,
163        offset: 3.8401300,
164        drift: 0.001296,
165        // jd_added: 2439004.500048091,
166        // jd_subbed: 2439004.499951909,
167        // tai_mjd: 39004.00004809095,
168    },
169    TaiUtcPre1972 {
170        yr: 1966,
171        mo: 1,
172        day: 1,
173        jd: 2439126.5,
174        mjd_ref: 39126.0,
175        offset: 4.3131700,
176        drift: 0.002592,
177        // jd_added: 2439126.5000499208,
178        // jd_subbed: 2439126.4999500792,
179        // tai_mjd: 39126.00004992095,
180    },
181    TaiUtcPre1972 {
182        yr: 1968,
183        mo: 2,
184        day: 1,
185        jd: 2439887.5,
186        mjd_ref: 39126.0,
187        offset: 4.2131700,
188        drift: 0.002592,
189        // jd_added: 2439887.5000715936,
190        // jd_subbed: 2439887.4999284064,
191        // tai_mjd: 39887.00007159354,
192    },
193];
194
195/// Returns the SOFA historical TAI−UTC offset (in seconds) for a given UTC instant.
196///
197/// Only for use with [`Dt`]s that are not already offset by a historical sofa offset.
198///
199/// **Do not use this for round tripping.**
200pub const fn historical_sofa_offset_for_non_adjusted(dt: &Dt) -> Option<Real> {
201    // < 1961-1-1 midnight, or >= 1972-1-1 midnight
202    if dt.sec < -1230724800 || dt.sec >= -883656990 {
203        return None;
204    }
205    let jd = dt.to_jd_f();
206    let mjd = dt.to_mjd_f();
207    let len = SOFA_TAI_UTC_PRE_1972.len();
208    let mut i = len;
209    while i > 0 {
210        i -= 1;
211        let entry = &SOFA_TAI_UTC_PRE_1972[i];
212
213        if jd >= entry.jd {
214            let offset = entry.offset + (mjd - entry.mjd_ref) * entry.drift;
215            return Some(offset);
216        }
217    }
218
219    None
220}
221
222// Returns the SOFA historical TAI−UTC offset (in seconds) for a given TAI instant.
223//
224// This is designed to go from a TAI time that was created using
225// `historical_sofa_offset_for_non_adjusted` where the offset was added,
226// back to a historical UTC SOFA time.
227//
228// The offset is computed using the same piecewise linear formula as the forward direction:
229// `offset = entry.offset + (MJD − entry.mjd_ref) × entry.drift`
230//
231// The correct usage for the returned offset is to subtract from an existing TAI time.
232// pub const fn historical_sofa_offset_for_already_added(tai: &Dt) -> Option<Real> {
233//     // < 1961-01-01 after SOFA offset applied, or >= tai 1972-1-1 midnight
234//     if (tai.sec < -1230724800 || (tai.sec == -1230724800 && tai.attos < 422817999999999936))
235//         || tai.sec >= -883655990
236//     {
237//         return None;
238//     }
239
240//     let jd = tai.to_jd(Scale::TAI);
241//     let mjd = tai.to_mjd(Scale::TAI);
242
243//     let len = SOFA_TAI_UTC_PRE_1972.len();
244//     let mut i = len;
245//     while i > 0 {
246//         i -= 1;
247//         let entry = &SOFA_TAI_UTC_PRE_1972[i];
248
249//         if jd >= entry.jd_added {
250//             let offset = entry.offset + (mjd - entry.mjd_ref) * entry.drift;
251//             return Some(offset);
252//         }
253//     }
254
255//     None
256// }
257
258// Returns the SOFA historical TAI−UTC offset (in seconds) for a given TAI instant.
259//
260// This is designed to go from a TAI time that was created using
261// `historical_sofa_offset_for_non_adjusted` where the offset was subtracted,
262// back to a historical UTC SOFA time.
263//
264// The offset is computed using the same piecewise linear formula as the forward direction:
265// `offset = entry.offset + (MJD − entry.mjd_ref) × entry.drift`
266//
267// The correct usage for the returned offset is to add to an existing TAI time.
268// pub const fn historical_sofa_offset_for_already_subbed(tai: &Dt) -> Option<Real> {
269//     // < 1961-01-01 after SOFA offset applied, or >= tai 1972-1-1 midnight
270//     if (tai.sec < -1230724801 || (tai.sec == -1230724801 && tai.attos < 577182000000000064))
271//         || tai.sec >= -883655990 // should be subbed value?
272//     {
273//         return None;
274//     }
275
276//     let jd = tai.to_jd(Scale::TAI);
277//     let mjd = tai.to_mjd(Scale::TAI);
278
279//     let len = SOFA_TAI_UTC_PRE_1972.len();
280//     let mut i = len;
281//     while i > 0 {
282//         i -= 1;
283//         let entry = &SOFA_TAI_UTC_PRE_1972[i];
284
285//         if jd >= entry.jd_subbed {
286//             let offset = entry.offset + (mjd - entry.mjd_ref) * entry.drift;
287//             return Some(offset);
288//         }
289//     }
290
291//     None
292// }