1use std::fmt::Display;
17use std::str::FromStr;
18
19use thiserror::Error;
20
21pub trait TimeScale {
23 fn abbreviation(&self) -> &'static str;
25 fn name(&self) -> &'static str;
27}
28
29#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Tai;
33
34impl TimeScale for Tai {
35 fn abbreviation(&self) -> &'static str {
36 "TAI"
37 }
38 fn name(&self) -> &'static str {
39 "International Atomic Time"
40 }
41}
42
43impl Display for Tai {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 write!(f, "{}", self.abbreviation())
46 }
47}
48
49#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct Tcb;
53
54impl TimeScale for Tcb {
55 fn abbreviation(&self) -> &'static str {
56 "TCB"
57 }
58 fn name(&self) -> &'static str {
59 "Barycentric Coordinate Time"
60 }
61}
62
63impl Display for Tcb {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 write!(f, "{}", self.abbreviation())
66 }
67}
68
69#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct Tcg;
73
74impl TimeScale for Tcg {
75 fn abbreviation(&self) -> &'static str {
76 "TCG"
77 }
78 fn name(&self) -> &'static str {
79 "Geocentric Coordinate Time"
80 }
81}
82
83impl Display for Tcg {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(f, "{}", self.abbreviation())
86 }
87}
88
89#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92pub struct Tdb;
93
94impl TimeScale for Tdb {
95 fn abbreviation(&self) -> &'static str {
96 "TDB"
97 }
98 fn name(&self) -> &'static str {
99 "Barycentric Dynamical Time"
100 }
101}
102
103impl Display for Tdb {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{}", self.abbreviation())
106 }
107}
108
109#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
111#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
112pub struct Tt;
113
114impl TimeScale for Tt {
115 fn abbreviation(&self) -> &'static str {
116 "TT"
117 }
118 fn name(&self) -> &'static str {
119 "Terrestrial Time"
120 }
121}
122
123impl Display for Tt {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 write!(f, "{}", self.abbreviation())
126 }
127}
128
129#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132pub struct Ut1;
133
134impl TimeScale for Ut1 {
135 fn abbreviation(&self) -> &'static str {
136 "UT1"
137 }
138 fn name(&self) -> &'static str {
139 "Universal Time"
140 }
141}
142
143impl Display for Ut1 {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(f, "{}", self.abbreviation())
146 }
147}
148
149#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152pub enum DynTimeScale {
153 #[default]
155 Tai,
156 Tcb,
158 Tcg,
160 Tdb,
162 Tt,
164 Ut1,
166}
167
168impl TimeScale for DynTimeScale {
169 fn abbreviation(&self) -> &'static str {
170 match self {
171 DynTimeScale::Tai => Tai.abbreviation(),
172 DynTimeScale::Tcb => Tcb.abbreviation(),
173 DynTimeScale::Tcg => Tcg.abbreviation(),
174 DynTimeScale::Tdb => Tdb.abbreviation(),
175 DynTimeScale::Tt => Tt.abbreviation(),
176 DynTimeScale::Ut1 => Ut1.abbreviation(),
177 }
178 }
179
180 fn name(&self) -> &'static str {
181 match self {
182 DynTimeScale::Tai => Tai.name(),
183 DynTimeScale::Tcb => Tcb.name(),
184 DynTimeScale::Tcg => Tcg.name(),
185 DynTimeScale::Tdb => Tdb.name(),
186 DynTimeScale::Tt => Tt.name(),
187 DynTimeScale::Ut1 => Ut1.name(),
188 }
189 }
190}
191
192impl From<Tai> for DynTimeScale {
193 fn from(_: Tai) -> Self {
194 Self::Tai
195 }
196}
197
198impl From<Tcb> for DynTimeScale {
199 fn from(_: Tcb) -> Self {
200 Self::Tcb
201 }
202}
203
204impl From<Tcg> for DynTimeScale {
205 fn from(_: Tcg) -> Self {
206 Self::Tcg
207 }
208}
209
210impl From<Tdb> for DynTimeScale {
211 fn from(_: Tdb) -> Self {
212 Self::Tdb
213 }
214}
215
216impl From<Tt> for DynTimeScale {
217 fn from(_: Tt) -> Self {
218 Self::Tt
219 }
220}
221
222impl From<Ut1> for DynTimeScale {
223 fn from(_: Ut1) -> Self {
224 Self::Ut1
225 }
226}
227
228#[derive(Clone, Debug, Error, Eq, PartialEq)]
230#[error("unknown time scale: {0}")]
231pub struct UnknownTimeScaleError(String);
232
233impl FromStr for DynTimeScale {
234 type Err = UnknownTimeScaleError;
235
236 fn from_str(s: &str) -> Result<Self, Self::Err> {
237 match s {
238 "tai" | "TAI" => Ok(DynTimeScale::Tai),
239 "tcb" | "TCB" => Ok(DynTimeScale::Tcb),
240 "tcg" | "TCG" => Ok(DynTimeScale::Tcg),
241 "tdb" | "TDB" => Ok(DynTimeScale::Tdb),
242 "tt" | "TT" => Ok(DynTimeScale::Tt),
243 "ut1" | "UT1" => Ok(DynTimeScale::Ut1),
244 _ => Err(UnknownTimeScaleError(s.to_owned())),
245 }
246 }
247}
248
249impl Display for DynTimeScale {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 write!(f, "{}", self.abbreviation())
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use rstest::rstest;
259
260 #[rstest]
261 #[case(Tai, "TAI", "International Atomic Time")]
262 #[case(Tcb, "TCB", "Barycentric Coordinate Time")]
263 #[case(Tcg, "TCG", "Geocentric Coordinate Time")]
264 #[case(Tdb, "TDB", "Barycentric Dynamical Time")]
265 #[case(Tt, "TT", "Terrestrial Time")]
266 #[case(Ut1, "UT1", "Universal Time")]
267 fn test_time_scales<T: TimeScale + ToString>(
268 #[case] scale: T,
269 #[case] abbreviation: &'static str,
270 #[case] name: &'static str,
271 ) {
272 assert_eq!(scale.abbreviation(), abbreviation);
273 assert_eq!(scale.to_string(), abbreviation);
274 assert_eq!(scale.name(), name);
275 }
276
277 #[rstest]
278 #[case("TAI", "International Atomic Time")]
279 #[case("TCB", "Barycentric Coordinate Time")]
280 #[case("TCG", "Geocentric Coordinate Time")]
281 #[case("TDB", "Barycentric Dynamical Time")]
282 #[case("TT", "Terrestrial Time")]
283 #[case("UT1", "Universal Time")]
284 fn test_dyn_time_scale(#[case] abbreviation: &str, #[case] name: &str) {
285 let scale: DynTimeScale = abbreviation.parse().unwrap();
286 assert_eq!(scale.abbreviation(), abbreviation);
287 assert_eq!(scale.to_string(), abbreviation);
288 assert_eq!(scale.name(), name);
289 }
290
291 #[test]
292 fn test_dyn_time_scale_invalid() {
293 let scale: Result<DynTimeScale, UnknownTimeScaleError> = "NTS".parse();
294 assert_eq!(scale, Err(UnknownTimeScaleError("NTS".to_owned())))
295 }
296}