Skip to main content

astrotime/
epoch.rs

1use crate::duration::Duration;
2use crate::instant::Instant;
3
4/// A reference for a well known `Instant` in time, used for offsetting events from.
5pub enum Epoch {
6    /// The start of the Julian Period,
7    /// which is 4713 BCE on Jan 1st Julian, 12:00:00.0 TT
8    /// which is 4714 BCE on Nov 24 Gregorian, 12:00:00.0 TT
9    /// JD 0 (by definition)
10    JulianPeriod,
11
12    /// The start of the Julian Calendar period,
13    /// which is January 1st, CE 1, Julian, 00:00:00.0 TT
14    /// Specified in TT
15    /// Note that this is exactly two days prior to the Gregorian Epoch.
16    ///
17    /// Not to be confused with the `Epoch::JulianPeriod` which is much earlier.
18    /// JD 1721423.5 (unverified, based on 2 day offset from Gregorian)
19    JulianCalendar,
20
21    /// The start of the Gregorian Calendar period,
22    /// which is January 1st, CE 1, Gregorian, 00:00:00.0 TT
23    /// JD 1721425.5
24    GregorianCalendar,
25
26    /// The Spreadsheet Epoch
27    /// Which is December 30, 1899 CE gregorian, 00:00:00.0 UTC
28    /// This is the point from which dates in a spreadsheet are counted
29    /// as the (floating point) number of days since.
30    /// JD 2415018.500476666666  // 32.184 [TAI-TT] + 9 [UTC-TAI]
31    Spreadsheet,
32
33    /// The J1900.0 astronomical epoch,
34    /// which is December 31, 1899 CE gregorian, 12:00:00.0 TT
35    /// JD 2415020.0 (verified at <https://www.astronomyclub.xyz/celestial-sphere-2/epochs-for-coordinate-systems.html>)
36    J1900_0,
37
38    /// The NTP time base used in the IANA leap seconds file
39    /// which is January 1st, 1900 CE gregorian, 00:00:00.0 UTC
40    /// (seconds elapsed since 1900-01-01 00:00:00 UTC, except the leap seconds themselves)
41    /// JD 2415020.500476666666  // 32.184 [TAI-TT] + 9 [UTC-TAI]
42    Ntp,
43
44    /// The UNIX Epoch,
45    /// which is January 1st, 1970 CE gregorian, 00:00:00.0 UTC
46    /// JD 2440587.500476666666  // 32.184 [TAI-TT] + 9 [UTC-TAI]
47    Unix,
48
49    /// The epoch where TT, TCB, and TCG all read the same.
50    /// which is January 1st, 1977 CE gregorian, 00:00:00 TAI
51    /// JD 2443144.5003725 (verified at <https://en.wikipedia.org/wiki/International_Atomic_Time>)
52    /// This is 16 seconds different from this date in UTC, and another 32.184 [TAI-TT] from TT.
53    TimeStandard,
54
55    /// A year I use for testing
56    /// 1977-1-1 00:00:00 UTC
57    Y1977,
58
59    /// The J1991.25 astronomical epoch,
60    /// which is April 2, 1991 CE gregorian, 13:30:00.0 TT
61    /// JD 2448349.0625 (verified at <https://www.astronomyclub.xyz/celestial-sphere-2/epochs-for-coordinate-systems.html>)
62    J1991_25,
63
64    /// The Year 2000
65    /// which is January 1st, 2000 CE gregorian, 00:00:00.0 UTC
66    /// JD 2451544.50074287037 // 32.184 [TAI-TT] + 32 [UTC-TAI]
67    Y2k,
68
69    /// The J2000.0 astronomical epoch,
70    /// which is January 1, 2000 CE gregorian, 12:00:00.0 TT
71    /// JD 2451545.0 (verified at <https://www.astronomyclub.xyz/celestial-sphere-2/epochs-for-coordinate-systems.html>)
72    /// (verified at <https://en.wikipedia.org/wiki/Epoch_(astronomy)>)
73    J2000_0,
74
75    /// The J2100.0 astronomical epoch,
76    /// which is January 1, 2100 CE gregorian, 12:00:00.0 TT
77    /// JD 2488070.0 (verified at <https://www.astronomyclub.xyz/celestial-sphere-2/epochs-for-coordinate-systems.html>)
78    J2100_0,
79
80    /// The J2200.0 astronomical epoch,
81    /// which is January 2, 2200 CE gregorian, 12:00:00.0 TT
82    /// JD 2524595.0 (verified at <https://www.astronomyclub.xyz/celestial-sphere-2/epochs-for-coordinate-systems.html>)
83    J2200_0,
84}
85
86impl Epoch {
87    /// Generate the `Instant` that this `Epoch` refers to
88    #[must_use]
89    pub const fn as_instant(&self) -> Instant {
90        match *self {
91            // NOTE: all instants are internally represented in TT standard.
92            Self::JulianPeriod => Instant(Duration {
93                secs: -211_087_684_832,
94                attos: -184_000_000_000_000_000,
95            }),
96            Self::JulianCalendar => Instant(Duration {
97                secs: -62_356_694_432,
98                attos: -184_000_000_000_000_000,
99            }),
100            Self::GregorianCalendar => Instant(Duration {
101                secs: -62_356_521_632,
102                attos: -184_000_000_000_000_000,
103            }),
104            Self::Spreadsheet => Instant(Duration {
105                secs: -2_430_086_391,
106                attos: -0,
107            }),
108            Self::J1900_0 => Instant(Duration {
109                secs: -2_429_956_832,
110                attos: -184_000_000_000_000_000,
111            }),
112            Self::Ntp => Instant(Duration {
113                secs: -2_429_913_591,
114                attos: -0,
115            }),
116            Self::Unix => Instant(Duration {
117                secs: -220_924_791,
118                attos: -0,
119            }),
120            Self::TimeStandard => Instant(Duration { secs: 0, attos: 0 }),
121            Self::Y1977 => Instant(Duration { secs: 16, attos: 0 }),
122            Self::J1991_25 => Instant(Duration {
123                secs: 449_674_167,
124                attos: 816_000_000_000_000_000,
125            }),
126            Self::Y2k => Instant(Duration {
127                secs: 725_760_032,
128                attos: 0,
129            }),
130            Self::J2000_0 => Instant(Duration {
131                secs: 725_803_167,
132                attos: 816_000_000_000_000_000,
133            }),
134            Self::J2100_0 => Instant(Duration {
135                secs: 3_881_563_167,
136                attos: 816_000_000_000_000_000,
137            }),
138            Self::J2200_0 => Instant(Duration {
139                secs: 7_037_323_167,
140                attos: 816_000_000_000_000_000,
141            }),
142        }
143    }
144}
145
146#[cfg(test)]
147mod test {
148    use super::Epoch;
149    use crate::calendar::{Gregorian, Julian};
150    use crate::date_time::DateTime;
151    use crate::instant::Instant;
152    use crate::standard::{Tai, Tt, Utc};
153
154    macro_rules! epoch_check {
155        ($epoch:expr, $cal:ty, $std:ty, $def:expr) => {
156            let instant = $epoch.as_instant();
157            let dt: DateTime<$cal, $std> = From::from(instant);
158            assert_eq!(dt, $def);
159            let check: Instant = From::from(dt);
160            assert_eq!(instant, check);
161        };
162    }
163
164    #[test]
165    fn check_epochs_and_conversion() {
166        epoch_check!(
167            Epoch::JulianPeriod,
168            Julian,
169            Tt,
170            DateTime::<Julian, Tt>::new_bc(4713, 1, 1, 12, 0, 0, 0).unwrap()
171        );
172        epoch_check!(
173            Epoch::JulianCalendar,
174            Julian,
175            Tt,
176            DateTime::<Julian, Tt>::new(1, 1, 1, 0, 0, 0, 0).unwrap()
177        );
178        epoch_check!(
179            Epoch::GregorianCalendar,
180            Gregorian,
181            Tt,
182            DateTime::<Gregorian, Tt>::new(1, 1, 1, 0, 0, 0, 0).unwrap()
183        );
184        epoch_check!(
185            Epoch::Spreadsheet,
186            Gregorian,
187            Utc,
188            DateTime::<Gregorian, Utc>::new(1899, 12, 30, 0, 0, 0, 0).unwrap()
189        );
190        epoch_check!(
191            Epoch::J1900_0,
192            Gregorian,
193            Tt,
194            DateTime::<Gregorian, Tt>::new(1899, 12, 31, 12, 0, 0, 0).unwrap()
195        );
196        epoch_check!(
197            Epoch::Ntp,
198            Gregorian,
199            Utc,
200            DateTime::<Gregorian, Utc>::new(1900, 1, 1, 0, 0, 0, 0).unwrap()
201        );
202        epoch_check!(
203            Epoch::Unix,
204            Gregorian,
205            Utc,
206            DateTime::<Gregorian, Utc>::new(1970, 1, 1, 0, 0, 0, 0).unwrap()
207        );
208        epoch_check!(
209            Epoch::TimeStandard,
210            Gregorian,
211            Tai,
212            DateTime::<Gregorian, Tai>::new(1977, 1, 1, 0, 0, 0, 0).unwrap()
213        );
214        epoch_check!(
215            Epoch::Y1977,
216            Gregorian,
217            Utc,
218            DateTime::<Gregorian, Utc>::new(1977, 1, 1, 0, 0, 0, 0).unwrap()
219        );
220        epoch_check!(
221            Epoch::J1991_25,
222            Gregorian,
223            Tt,
224            DateTime::<Gregorian, Tt>::new(1991, 4, 2, 13, 30, 0, 0).unwrap()
225        );
226        epoch_check!(
227            Epoch::Y2k,
228            Gregorian,
229            Utc,
230            DateTime::<Gregorian, Utc>::new(2000, 1, 1, 0, 0, 0, 0).unwrap()
231        );
232        epoch_check!(
233            Epoch::J2000_0,
234            Gregorian,
235            Tt,
236            DateTime::<Gregorian, Tt>::new(2000, 1, 1, 12, 0, 0, 0).unwrap()
237        );
238        epoch_check!(
239            Epoch::J2100_0,
240            Gregorian,
241            Tt,
242            DateTime::<Gregorian, Tt>::new(2100, 1, 1, 12, 0, 0, 0).unwrap()
243        );
244        epoch_check!(
245            Epoch::J2200_0,
246            Gregorian,
247            Tt,
248            DateTime::<Gregorian, Tt>::new(2200, 1, 2, 12, 0, 0, 0).unwrap()
249        );
250    }
251
252    #[test]
253    fn test_instant_julian_day_formatted() {
254        assert_eq!(
255            Epoch::JulianPeriod.as_instant().as_julian_day_formatted(),
256            "JD 0"
257        );
258        assert_eq!(
259            Epoch::JulianCalendar.as_instant().as_julian_day_formatted(),
260            "JD 1721423.5"
261        );
262        assert_eq!(
263            Epoch::GregorianCalendar
264                .as_instant()
265                .as_julian_day_formatted(),
266            "JD 1721425.5"
267        );
268        assert_eq!(
269            Epoch::Spreadsheet.as_instant().as_julian_day_formatted(),
270            "JD 2415018.5004766666666667"
271        );
272        assert_eq!(
273            Epoch::J1900_0.as_instant().as_julian_day_formatted(),
274            "JD 2415020"
275        );
276        assert_eq!(
277            Epoch::Ntp.as_instant().as_julian_day_formatted(),
278            "JD 2415020.5004766666666667"
279        );
280        assert_eq!(
281            Epoch::Unix.as_instant().as_julian_day_formatted(),
282            "JD 2440587.5004766666666667"
283        );
284        assert_eq!(
285            Epoch::TimeStandard.as_instant().as_julian_day_formatted(),
286            "JD 2443144.5003725"
287        );
288        assert_eq!(
289            Epoch::J1991_25.as_instant().as_julian_day_formatted(),
290            "JD 2448349.0625"
291        );
292        assert_eq!(
293            Epoch::Y2k.as_instant().as_julian_day_formatted(),
294            "JD 2451544.5007428703703704"
295        );
296        assert_eq!(
297            Epoch::J2000_0.as_instant().as_julian_day_formatted(),
298            "JD 2451545"
299        );
300        assert_eq!(
301            Epoch::J2100_0.as_instant().as_julian_day_formatted(),
302            "JD 2488070"
303        );
304        assert_eq!(
305            Epoch::J2200_0.as_instant().as_julian_day_formatted(),
306            "JD 2524595"
307        );
308    }
309}