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}