1use num_traits::ConstZero;
4
5use crate::{
6 Date, Duration, FromTimeScale, IntoTimeScale, Month, TimePoint,
7 time_scale::{AbsoluteTimeScale, TerrestrialTime, TimeScale, datetime::UniformDateTimeScale},
8};
9
10pub type TaiTime = TimePoint<Tai>;
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
16pub struct Tai;
17
18impl TimeScale for Tai {
19 const NAME: &'static str = "International Atomic Time";
20
21 const ABBREVIATION: &'static str = "TAI";
22}
23
24impl AbsoluteTimeScale for Tai {
25 const EPOCH: Date = match Date::from_historic_date(1958, Month::January, 1) {
26 Ok(epoch) => epoch,
27 Err(_) => unreachable!(),
28 };
29}
30
31impl UniformDateTimeScale for Tai {}
32
33impl<Scale: ?Sized> TimePoint<Scale> {
34 pub fn from_tai(time_point: TaiTime) -> Self
35 where
36 Self: FromTimeScale<Tai>,
37 {
38 Self::from_time_scale(time_point)
39 }
40
41 pub fn into_tai(self) -> TaiTime
42 where
43 Self: IntoTimeScale<Tai>,
44 {
45 self.into_time_scale()
46 }
47}
48
49impl TerrestrialTime for Tai {
50 const TAI_OFFSET: Duration = Duration::ZERO;
51}
52
53#[cfg(test)]
56fn check_gregorian_datetime(
57 year: i32,
58 month: Month,
59 day: u8,
60 hour: u8,
61 minute: u8,
62 second: u8,
63 time_since_epoch: Duration,
64) {
65 assert_eq!(
66 TaiTime::from_gregorian_datetime(year, month, day, hour, minute, second)
67 .unwrap()
68 .time_since_epoch(),
69 time_since_epoch
70 );
71}
72
73#[test]
76fn known_timestamps() {
77 use crate::Month::*;
78
79 check_gregorian_datetime(1958, January, 1, 0, 0, 0, Duration::seconds(0));
80 check_gregorian_datetime(1958, January, 2, 0, 0, 0, Duration::seconds(86400));
81 check_gregorian_datetime(1960, January, 1, 0, 0, 0, Duration::seconds(63072000));
82 check_gregorian_datetime(1961, January, 1, 0, 0, 0, Duration::seconds(94694400));
83 check_gregorian_datetime(1970, January, 1, 0, 0, 0, Duration::seconds(378691200));
84 check_gregorian_datetime(1976, January, 1, 0, 0, 0, Duration::seconds(567993600));
85 check_gregorian_datetime(2025, July, 16, 16, 23, 24, Duration::seconds(2131374204));
86 check_gregorian_datetime(2034, December, 26, 8, 2, 37, Duration::seconds(2429424157));
87 check_gregorian_datetime(2760, April, 1, 21, 59, 58, Duration::seconds(25316575198));
88 check_gregorian_datetime(1643, January, 4, 1, 1, 33, Duration::seconds(-9940143507));
89}
90
91#[cfg(test)]
92fn gregorian_datetime_roundtrip(
93 year: i32,
94 month: Month,
95 day: u8,
96 hour: u8,
97 minute: u8,
98 second: u8,
99) {
100 use crate::GregorianDate;
101 use crate::IntoDateTime;
102
103 let time = TaiTime::from_gregorian_datetime(year, month, day, hour, minute, second).unwrap();
104 let (date, hour2, minute2, second2) = time.into_datetime();
105 let gregorian_date = GregorianDate::from_date(date);
106 assert_eq!(gregorian_date.year(), year);
107 assert_eq!(gregorian_date.month(), month);
108 assert_eq!(gregorian_date.day(), day);
109 assert_eq!(hour, hour2);
110 assert_eq!(minute, minute2);
111 assert_eq!(second, second2);
112}
113
114#[test]
115fn date_decomposition() {
116 gregorian_datetime_roundtrip(1999, Month::August, 22, 0, 0, 0);
117 gregorian_datetime_roundtrip(1958, Month::January, 1, 0, 0, 0);
118 gregorian_datetime_roundtrip(1958, Month::January, 2, 0, 0, 0);
119 gregorian_datetime_roundtrip(1960, Month::January, 1, 0, 0, 0);
120 gregorian_datetime_roundtrip(1961, Month::January, 1, 0, 0, 0);
121 gregorian_datetime_roundtrip(1970, Month::January, 1, 0, 0, 0);
122 gregorian_datetime_roundtrip(1976, Month::January, 1, 0, 0, 0);
123 gregorian_datetime_roundtrip(2025, Month::July, 16, 16, 23, 24);
124 gregorian_datetime_roundtrip(2034, Month::December, 26, 8, 2, 37);
125 gregorian_datetime_roundtrip(2760, Month::April, 1, 21, 59, 58);
126 gregorian_datetime_roundtrip(1643, Month::January, 4, 1, 1, 33);
127 gregorian_datetime_roundtrip(1996, Month::January, 1, 3, 0, 0);
128}