deep_time/historical_utc.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 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)
196/// for an **un-adjusted instant**.
197///
198/// - Only for instants that have not already been offset
199/// by a historical UTC offset value.
200/// - Not as accurate as ERFA / Astropy.
201/// - **Do not use this for round tripping.**
202/// - Unlike ERFA it does not support dates between 1960
203/// and 1961.
204pub const fn historical_utc_offset(dt: &Dt) -> Option<Real> {
205 // < 1961-1-1 midnight, or >= 1972-1-1 midnight
206 if dt.to_attos() < -1230724800000000000000000000
207 // tai attos for 1972 (10 leap seconds added)
208 || dt.to_attos() >= -883655990000000000000000000
209 {
210 return None;
211 }
212 // if dt.to_attos() >= -883656000000000000000000000 {
213 // return None;
214 // }
215
216 let jd = dt.to_jd_f();
217 let mjd = dt.to_mjd_f();
218 let len = TAI_UTC_PRE_1972.len();
219 let mut i = len;
220 while i > 0 {
221 i -= 1;
222 let entry = &TAI_UTC_PRE_1972[i];
223 if jd >= entry.jd {
224 let offset = entry.offset + (mjd - entry.mjd_ref) * entry.drift;
225 return Some(offset);
226 }
227 }
228
229 None
230}