1use core::convert::Infallible;
6
7use lox_core::time::deltas::TimeDelta;
8
9use crate::{
10 offsets::{Offset, OffsetProvider, TryOffset},
11 time_scales::{Gps, Tai, Tcb, Tcg, Tdb, TimeScale, Tt, Ut1},
12};
13
14macro_rules! impl_noop {
17 ($($scale:ident),*) => {
18 $(
19 impl<T> TryOffset<$scale, $scale> for T
20 where
21 T: OffsetProvider,
22 {
23 type Error = Infallible;
24
25 fn try_offset(
26 &self,
27 _origin: $scale,
28 _target: $scale,
29 _delta: TimeDelta
30 ) -> Result<TimeDelta, Self::Error> {
31 Ok(TimeDelta::default())
32 }
33 }
34 )*
35 };
36}
37
38impl_noop!(Gps, Tai, Tcb, Tcg, Tdb, Tt, Ut1);
39
40impl<T> TryOffset<Tai, Tt> for T
43where
44 T: OffsetProvider,
45{
46 type Error = Infallible;
47
48 fn try_offset(
49 &self,
50 _origin: Tai,
51 _target: Tt,
52 _delta: TimeDelta,
53 ) -> Result<TimeDelta, Self::Error> {
54 Ok(self.tai_to_tt())
55 }
56}
57
58impl<T> TryOffset<Tt, Tai> for T
59where
60 T: OffsetProvider,
61{
62 type Error = Infallible;
63
64 fn try_offset(
65 &self,
66 _origin: Tt,
67 _target: Tai,
68 _delta: TimeDelta,
69 ) -> Result<TimeDelta, Self::Error> {
70 Ok(self.tt_to_tai())
71 }
72}
73
74impl<T> TryOffset<Tai, Gps> for T
77where
78 T: OffsetProvider,
79{
80 type Error = Infallible;
81
82 fn try_offset(
83 &self,
84 _origin: Tai,
85 _target: Gps,
86 _delta: TimeDelta,
87 ) -> Result<TimeDelta, Self::Error> {
88 Ok(self.tai_to_gps())
89 }
90}
91
92impl<T> TryOffset<Gps, Tai> for T
93where
94 T: OffsetProvider,
95{
96 type Error = Infallible;
97
98 fn try_offset(
99 &self,
100 _origin: Gps,
101 _target: Tai,
102 _delta: TimeDelta,
103 ) -> Result<TimeDelta, Self::Error> {
104 Ok(self.gps_to_tai())
105 }
106}
107
108impl<T> TryOffset<Tt, Tcg> for T
111where
112 T: OffsetProvider,
113{
114 type Error = Infallible;
115
116 fn try_offset(
117 &self,
118 _origin: Tt,
119 _target: Tcg,
120 delta: TimeDelta,
121 ) -> Result<TimeDelta, Self::Error> {
122 Ok(self.tt_to_tcg(delta))
123 }
124}
125
126impl<T> TryOffset<Tcg, Tt> for T
127where
128 T: OffsetProvider,
129{
130 type Error = Infallible;
131
132 fn try_offset(
133 &self,
134 _origin: Tcg,
135 _target: Tt,
136 delta: TimeDelta,
137 ) -> Result<TimeDelta, Self::Error> {
138 Ok(self.tcg_to_tt(delta))
139 }
140}
141
142impl<T> TryOffset<Tdb, Tcb> for T
145where
146 T: OffsetProvider,
147{
148 type Error = Infallible;
149
150 fn try_offset(
151 &self,
152 _origin: Tdb,
153 _target: Tcb,
154 delta: TimeDelta,
155 ) -> Result<TimeDelta, Self::Error> {
156 Ok(self.tdb_to_tcb(delta))
157 }
158}
159
160impl<T> TryOffset<Tcb, Tdb> for T
161where
162 T: OffsetProvider,
163{
164 type Error = Infallible;
165
166 fn try_offset(
167 &self,
168 _origin: Tcb,
169 _target: Tdb,
170 delta: TimeDelta,
171 ) -> Result<TimeDelta, Self::Error> {
172 Ok(self.tcb_to_tdb(delta))
173 }
174}
175
176impl<T> TryOffset<Tt, Tdb> for T
179where
180 T: OffsetProvider,
181{
182 type Error = Infallible;
183
184 fn try_offset(
185 &self,
186 _origin: Tt,
187 _target: Tdb,
188 delta: TimeDelta,
189 ) -> Result<TimeDelta, Self::Error> {
190 Ok(self.tt_to_tdb(delta))
191 }
192}
193
194impl<T> TryOffset<Tdb, Tt> for T
195where
196 T: OffsetProvider,
197{
198 type Error = Infallible;
199
200 fn try_offset(
201 &self,
202 _origin: Tdb,
203 _target: Tt,
204 delta: TimeDelta,
205 ) -> Result<TimeDelta, Self::Error> {
206 Ok(self.tdb_to_tt(delta))
207 }
208}
209
210impl<T> TryOffset<Tai, Ut1> for T
213where
214 T: OffsetProvider,
215{
216 type Error = <Self as OffsetProvider>::Error;
217
218 fn try_offset(
219 &self,
220 _origin: Tai,
221 _target: Ut1,
222 delta: TimeDelta,
223 ) -> Result<TimeDelta, Self::Error> {
224 self.tai_to_ut1(delta)
225 }
226}
227
228impl<T> TryOffset<Ut1, Tai> for T
229where
230 T: OffsetProvider,
231{
232 type Error = <Self as OffsetProvider>::Error;
233
234 fn try_offset(
235 &self,
236 _origin: Ut1,
237 _target: Tai,
238 delta: TimeDelta,
239 ) -> Result<TimeDelta, Self::Error> {
240 self.ut1_to_tai(delta)
241 }
242}
243
244macro_rules! impl_two_step {
247 ($(($origin:ident, $via:ident, $target:ident)),*) => {
248 $(
249 impl<T> TryOffset<$origin, $target> for T
250 where
251 T: OffsetProvider,
252 {
253 type Error = Infallible;
254
255 fn try_offset(
256 &self,
257 origin: $origin,
258 target: $target,
259 delta: TimeDelta,
260 ) -> Result<TimeDelta, Self::Error> {
261 Ok(super::two_step_offset(self, origin, $via, target, delta))
262 }
263 }
264
265 impl<T> TryOffset<$target, $origin> for T
266 where
267 T: OffsetProvider,
268 {
269 type Error = Infallible;
270
271 fn try_offset(
272 &self,
273 origin: $target,
274 target: $origin,
275 delta: TimeDelta,
276 ) -> Result<TimeDelta, Self::Error> {
277 Ok(super::two_step_offset(self, origin, $via, target, delta))
278 }
279 }
280 )*
281 }
282}
283
284impl_two_step!(
285 (Tai, Tt, Tdb),
286 (Tdb, Tt, Tcg),
287 (Tai, Tt, Tcg),
288 (Tai, Tdb, Tcb),
289 (Tt, Tdb, Tcb),
290 (Tcb, Tdb, Tcg),
291 (Gps, Tai, Tt),
292 (Gps, Tai, Tcg),
293 (Gps, Tai, Tdb),
294 (Gps, Tai, Tcb)
295);
296
297macro_rules! impl_two_step_ut1 {
298 ($($scale:ident),*) => {
299 $(
300 impl<T> TryOffset<$scale, Ut1> for T
301 where
302 T: OffsetProvider,
303 {
304 type Error = <Self as OffsetProvider>::Error;
305
306 fn try_offset(
307 &self,
308 _origin: $scale,
309 _target: Ut1,
310 delta: TimeDelta,
311 ) -> Result<TimeDelta, Self::Error> {
312 let mut offset = self.offset($scale, Tai, delta);
313 offset += self.try_offset(Tai, Ut1, delta + offset)?;
314 Ok(offset)
315 }
316 }
317
318 impl<T> TryOffset<Ut1, $scale> for T
319 where
320 T: OffsetProvider,
321 {
322 type Error = <Self as OffsetProvider>::Error;
323
324 fn try_offset(
325 &self,
326 _origin: Ut1,
327 _target: $scale,
328 delta: TimeDelta,
329 ) -> Result<TimeDelta, Self::Error> {
330 let mut offset = self.try_offset(Ut1, Tai, delta)?;
331 offset += self.offset(Tai, $scale, delta + offset);
332 Ok(offset)
333 }
334 }
335 )*
336 };
337}
338
339impl_two_step_ut1!(Gps, Tcb, Tcg, Tdb, Tt);
340
341impl<T> TryOffset<TimeScale, TimeScale> for T
344where
345 T: OffsetProvider,
346{
347 type Error = <Self as OffsetProvider>::Error;
348
349 fn try_offset(
350 &self,
351 origin: TimeScale,
352 target: TimeScale,
353 delta: TimeDelta,
354 ) -> Result<TimeDelta, Self::Error> {
355 if origin == target {
356 return Ok(TimeDelta::default());
357 }
358 match (origin, target) {
359 (TimeScale::Gps, TimeScale::Tai) => Ok(self.offset(Gps, Tai, delta)),
360 (TimeScale::Gps, TimeScale::Tcb) => Ok(self.offset(Gps, Tcb, delta)),
361 (TimeScale::Gps, TimeScale::Tcg) => Ok(self.offset(Gps, Tcg, delta)),
362 (TimeScale::Gps, TimeScale::Tdb) => Ok(self.offset(Gps, Tdb, delta)),
363 (TimeScale::Gps, TimeScale::Tt) => Ok(self.offset(Gps, Tt, delta)),
364 (TimeScale::Gps, TimeScale::Ut1) => self.try_offset(Gps, Ut1, delta),
365 (TimeScale::Tai, TimeScale::Gps) => Ok(self.offset(Tai, Gps, delta)),
366 (TimeScale::Tai, TimeScale::Tcb) => Ok(self.offset(Tai, Tcb, delta)),
367 (TimeScale::Tai, TimeScale::Tcg) => Ok(self.offset(Tai, Tcg, delta)),
368 (TimeScale::Tai, TimeScale::Tdb) => Ok(self.offset(Tai, Tdb, delta)),
369 (TimeScale::Tai, TimeScale::Tt) => Ok(self.offset(Tai, Tt, delta)),
370 (TimeScale::Tai, TimeScale::Ut1) => self.try_offset(Tai, Ut1, delta),
371 (TimeScale::Tcb, TimeScale::Gps) => Ok(self.offset(Tcb, Gps, delta)),
372 (TimeScale::Tcb, TimeScale::Tai) => Ok(self.offset(Tcb, Tai, delta)),
373 (TimeScale::Tcb, TimeScale::Tcg) => Ok(self.offset(Tcb, Tcg, delta)),
374 (TimeScale::Tcb, TimeScale::Tdb) => Ok(self.offset(Tcb, Tdb, delta)),
375 (TimeScale::Tcb, TimeScale::Tt) => Ok(self.offset(Tcb, Tt, delta)),
376 (TimeScale::Tcb, TimeScale::Ut1) => self.try_offset(Tcb, Ut1, delta),
377 (TimeScale::Tcg, TimeScale::Gps) => Ok(self.offset(Tcg, Gps, delta)),
378 (TimeScale::Tcg, TimeScale::Tai) => Ok(self.offset(Tcg, Tai, delta)),
379 (TimeScale::Tcg, TimeScale::Tcb) => Ok(self.offset(Tcg, Tcb, delta)),
380 (TimeScale::Tcg, TimeScale::Tdb) => Ok(self.offset(Tcg, Tdb, delta)),
381 (TimeScale::Tcg, TimeScale::Tt) => Ok(self.offset(Tcg, Tt, delta)),
382 (TimeScale::Tcg, TimeScale::Ut1) => self.try_offset(Tcg, Ut1, delta),
383 (TimeScale::Tdb, TimeScale::Gps) => Ok(self.offset(Tdb, Gps, delta)),
384 (TimeScale::Tdb, TimeScale::Tai) => Ok(self.offset(Tdb, Tai, delta)),
385 (TimeScale::Tdb, TimeScale::Tcb) => Ok(self.offset(Tdb, Tcb, delta)),
386 (TimeScale::Tdb, TimeScale::Tcg) => Ok(self.offset(Tdb, Tcg, delta)),
387 (TimeScale::Tdb, TimeScale::Tt) => Ok(self.offset(Tdb, Tt, delta)),
388 (TimeScale::Tdb, TimeScale::Ut1) => self.try_offset(Tdb, Ut1, delta),
389 (TimeScale::Tt, TimeScale::Gps) => Ok(self.offset(Tt, Gps, delta)),
390 (TimeScale::Tt, TimeScale::Tai) => Ok(self.offset(Tt, Tai, delta)),
391 (TimeScale::Tt, TimeScale::Tcb) => Ok(self.offset(Tt, Tcb, delta)),
392 (TimeScale::Tt, TimeScale::Tcg) => Ok(self.offset(Tt, Tcg, delta)),
393 (TimeScale::Tt, TimeScale::Tdb) => Ok(self.offset(Tt, Tdb, delta)),
394 (TimeScale::Tt, TimeScale::Ut1) => self.try_offset(Tt, Ut1, delta),
395 (TimeScale::Ut1, TimeScale::Gps) => self.try_offset(Ut1, Gps, delta),
396 (TimeScale::Ut1, TimeScale::Tai) => self.try_offset(Ut1, Tai, delta),
397 (TimeScale::Ut1, TimeScale::Tcb) => self.try_offset(Ut1, Tcb, delta),
398 (TimeScale::Ut1, TimeScale::Tcg) => self.try_offset(Ut1, Tcg, delta),
399 (TimeScale::Ut1, TimeScale::Tdb) => self.try_offset(Ut1, Tdb, delta),
400 (TimeScale::Ut1, TimeScale::Tt) => self.try_offset(Ut1, Tt, delta),
401 (TimeScale::Gps, TimeScale::Gps)
402 | (TimeScale::Tai, TimeScale::Tai)
403 | (TimeScale::Tcb, TimeScale::Tcb)
404 | (TimeScale::Tcg, TimeScale::Tcg)
405 | (TimeScale::Tdb, TimeScale::Tdb)
406 | (TimeScale::Tt, TimeScale::Tt)
407 | (TimeScale::Ut1, TimeScale::Ut1) => Ok(TimeDelta::default()),
408 }
409 }
410}
411
412macro_rules! impl_dynamic {
413 ($($scale:ident),*) => {
414 $(
415 impl<T> TryOffset<$scale, TimeScale> for T
416 where
417 T: OffsetProvider,
418 {
419 type Error = <Self as OffsetProvider>::Error;
420
421 fn try_offset(
422 &self,
423 origin: $scale,
424 target: TimeScale,
425 delta: TimeDelta,
426 ) -> Result<TimeDelta, Self::Error> {
427 let origin: TimeScale = origin.into();
428 self.try_offset(origin, target, delta)
429 }
430 }
431
432 impl<T> TryOffset<TimeScale, $scale> for T
433 where
434 T: OffsetProvider,
435 {
436 type Error = <Self as OffsetProvider>::Error;
437
438 fn try_offset(
439 &self,
440 origin: TimeScale,
441 target: $scale,
442 delta: TimeDelta,
443 ) -> Result<TimeDelta, Self::Error> {
444 let target: TimeScale = target.into();
445 self.try_offset(origin, target, delta)
446 }
447 }
448 )*
449 };
450}
451
452impl_dynamic!(Gps, Tai, Tcb, Tcg, Tdb, Tt, Ut1);
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use crate::offsets::DefaultOffsetProvider;
458
459 #[test]
460 fn tai_to_gps_is_negative_19_seconds() {
461 let p = DefaultOffsetProvider;
462 let delta = p.offset(Tai, Gps, TimeDelta::default());
463 assert_eq!(delta, TimeDelta::builder().seconds(-19).build());
464 }
465
466 #[test]
467 fn gps_to_tai_is_positive_19_seconds() {
468 let p = DefaultOffsetProvider;
469 let delta = p.offset(Gps, Tai, TimeDelta::default());
470 assert_eq!(delta, TimeDelta::builder().seconds(19).build());
471 }
472
473 #[test]
474 fn tai_gps_round_trip_is_zero() {
475 let p = DefaultOffsetProvider;
476 let to_gps = p.offset(Tai, Gps, TimeDelta::default());
477 let back_to_tai = p.offset(Gps, Tai, to_gps);
478 assert_eq!(to_gps + back_to_tai, TimeDelta::default());
479 }
480}