1use core::fmt;
23use core::marker::PhantomData;
24
25use crate::context::TimeContext;
26use crate::encoding::{
27 j2000_seconds_to_jd, j2000_seconds_to_mjd, jd_to_j2000_seconds, mjd_to_j2000_seconds,
28};
29use crate::error::ConversionError;
30use crate::scale::conversion::InfallibleScaleConvert;
31use crate::scale::{CoordinateScale, Scale, TAI, UTC};
32use crate::sealed::Sealed;
33use crate::target::{ContextConversionTarget, ConversionTarget, InfallibleConversionTarget};
34use crate::time::Time;
35use qtty::{Day, Quantity, Second, Unit};
36
37#[allow(private_bounds)]
46pub trait TimeFormat: Sealed + Copy + Clone + fmt::Debug + 'static {
47 type Unit: Unit;
49
50 const NAME: &'static str;
52}
53
54#[allow(private_bounds)]
56pub trait FormatForScale<S: Scale>: TimeFormat + Sealed {
57 fn try_from_time(
58 time: Time<S>,
59 ctx: &TimeContext,
60 ) -> Result<Quantity<Self::Unit>, ConversionError>;
61 fn try_into_time(
62 raw: Quantity<Self::Unit>,
63 ctx: &TimeContext,
64 ) -> Result<Time<S>, ConversionError>;
65}
66
67#[allow(private_bounds)]
69pub trait InfallibleFormatForScale<S: Scale>: FormatForScale<S> + Sealed {
70 fn from_time(time: Time<S>) -> Quantity<Self::Unit>;
71 fn into_time(raw: Quantity<Self::Unit>) -> Time<S>;
72}
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub struct J2000s;
79
80#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
82pub struct JD;
83
84#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
86pub struct MJD;
87
88#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
90pub struct Unix;
91
92#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
94pub struct GPS;
95
96impl Sealed for J2000s {}
97impl Sealed for JD {}
98impl Sealed for MJD {}
99impl Sealed for Unix {}
100impl Sealed for GPS {}
101
102impl TimeFormat for J2000s {
103 type Unit = qtty::unit::Second;
104 const NAME: &'static str = "J2000s";
105}
106
107impl TimeFormat for JD {
108 type Unit = qtty::unit::Day;
109 const NAME: &'static str = "JD";
110}
111
112impl TimeFormat for MJD {
113 type Unit = qtty::unit::Day;
114 const NAME: &'static str = "MJD";
115}
116
117impl TimeFormat for Unix {
118 type Unit = qtty::unit::Second;
119 const NAME: &'static str = "Unix";
120}
121
122impl TimeFormat for GPS {
123 type Unit = qtty::unit::Second;
124 const NAME: &'static str = "GPS";
125}
126
127pub struct EncodedTime<S: Scale, F: TimeFormat> {
141 raw: Quantity<F::Unit>,
142 _marker: PhantomData<fn() -> S>,
143}
144
145impl<S: Scale, F: TimeFormat> Copy for EncodedTime<S, F> {}
146
147impl<S: Scale, F: TimeFormat> Clone for EncodedTime<S, F> {
148 #[inline]
149 fn clone(&self) -> Self {
150 *self
151 }
152}
153
154impl<S: Scale, F: TimeFormat> fmt::Debug for EncodedTime<S, F> {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.debug_struct("EncodedTime")
157 .field("scale", &S::NAME)
158 .field("format", &F::NAME)
159 .field("raw", &self.raw)
160 .finish()
161 }
162}
163
164impl<S: Scale, F: TimeFormat> fmt::Display for EncodedTime<S, F>
165where
166 qtty::Quantity<F::Unit>: fmt::Display,
167{
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 fmt::Display::fmt(&self.raw, f)
170 }
171}
172
173impl<S: Scale, F: TimeFormat> fmt::LowerExp for EncodedTime<S, F>
174where
175 qtty::Quantity<F::Unit>: fmt::LowerExp,
176{
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 fmt::LowerExp::fmt(&self.raw, f)
179 }
180}
181
182impl<S: Scale, F: TimeFormat> fmt::UpperExp for EncodedTime<S, F>
183where
184 qtty::Quantity<F::Unit>: fmt::UpperExp,
185{
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 fmt::UpperExp::fmt(&self.raw, f)
188 }
189}
190
191impl<S: Scale, F: TimeFormat> PartialEq for EncodedTime<S, F> {
192 #[inline]
193 fn eq(&self, other: &Self) -> bool {
194 self.raw == other.raw
195 }
196}
197
198impl<S: Scale, F: TimeFormat> PartialOrd for EncodedTime<S, F> {
199 #[inline]
200 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
201 self.raw.partial_cmp(&other.raw)
202 }
203}
204
205impl<S: Scale, F: TimeFormat> EncodedTime<S, F> {
206 #[inline]
207 pub(crate) const fn new_unchecked(raw: Quantity<F::Unit>) -> Self {
208 Self {
209 raw,
210 _marker: PhantomData,
211 }
212 }
213
214 #[inline]
219 pub const fn from_raw_unchecked(raw: Quantity<F::Unit>) -> Self {
220 Self::new_unchecked(raw)
221 }
222
223 #[inline]
225 pub const fn raw(self) -> Quantity<F::Unit> {
226 self.raw
227 }
228
229 #[inline]
231 pub const fn quantity(self) -> Quantity<F::Unit> {
232 self.raw
233 }
234}
235
236impl<S: Scale> EncodedTime<S, JD> {
237 pub const J2000: Self = Self::from_raw_unchecked(crate::constats::J2000_JD_TT.raw());
239}
240
241impl<S: Scale, F> EncodedTime<S, F>
242where
243 F: FormatForScale<S>,
244{
245 #[inline]
247 pub fn try_new(raw: Quantity<F::Unit>) -> Result<Self, ConversionError> {
248 if raw.is_finite() {
249 Ok(Self::new_unchecked(raw))
250 } else {
251 Err(ConversionError::NonFinite)
252 }
253 }
254
255 #[inline]
261 pub fn try_to_time(self) -> Result<Time<S>, ConversionError> {
262 F::try_into_time(self.raw, &TimeContext::new())
263 }
264
265 #[inline]
267 pub fn to_time_with(self, ctx: &TimeContext) -> Result<Time<S>, ConversionError> {
268 F::try_into_time(self.raw, ctx)
269 }
270}
271
272impl<S: Scale, F> EncodedTime<S, F>
273where
274 F: InfallibleFormatForScale<S>,
275{
276 #[inline]
277 pub(crate) fn from_time_infallible(time: Time<S>) -> Self {
278 Self::new_unchecked(F::from_time(time))
279 }
280
281 #[inline]
283 pub fn to_time(self) -> Time<S> {
284 F::into_time(self.raw)
285 }
286
287 #[allow(private_bounds)]
289 #[inline]
290 pub fn to<T>(self) -> T::Output
291 where
292 T: InfallibleConversionTarget<S>,
293 {
294 T::convert(self.to_time())
295 }
296
297 #[allow(private_bounds)]
299 #[inline]
300 pub fn try_to<T>(self) -> Result<T::Output, ConversionError>
301 where
302 T: ConversionTarget<S>,
303 {
304 T::try_convert(self.to_time())
305 }
306}
307
308impl<S: Scale, F> EncodedTime<S, F>
309where
310 F: FormatForScale<S>,
311{
312 #[allow(private_bounds)]
314 #[inline]
315 pub fn to_with<T>(self, ctx: &TimeContext) -> Result<T::Output, ConversionError>
316 where
317 T: ContextConversionTarget<S>,
318 {
319 T::convert_with(self.to_time_with(ctx)?, ctx)
320 }
321}
322
323pub type JulianDate<S> = EncodedTime<S, JD>;
327
328pub type ModifiedJulianDate<S> = EncodedTime<S, MJD>;
330
331pub type J2000Seconds<S> = EncodedTime<S, J2000s>;
333
334pub type UnixTime = EncodedTime<UTC, Unix>;
336
337pub type GpsTime = EncodedTime<TAI, GPS>;
339
340macro_rules! coordinate_format {
343 ($fmt:ty, $quantity:ty, $from_time:expr, $to_time:expr) => {
344 impl<S: CoordinateScale> FormatForScale<S> for $fmt {
345 #[inline]
346 fn try_from_time(
347 time: Time<S>,
348 _ctx: &TimeContext,
349 ) -> Result<$quantity, ConversionError> {
350 Ok(<Self as InfallibleFormatForScale<S>>::from_time(time))
351 }
352
353 #[inline]
354 fn try_into_time(
355 raw: $quantity,
356 _ctx: &TimeContext,
357 ) -> Result<Time<S>, ConversionError> {
358 Ok(<Self as InfallibleFormatForScale<S>>::into_time(raw))
359 }
360 }
361
362 impl<S: CoordinateScale> InfallibleFormatForScale<S> for $fmt {
363 #[inline]
364 fn from_time(time: Time<S>) -> $quantity {
365 $from_time(time)
366 }
367
368 #[inline]
369 fn into_time(raw: $quantity) -> Time<S> {
370 $to_time(raw)
371 }
372 }
373 };
374}
375
376coordinate_format!(
377 J2000s,
378 Second,
379 |time: Time<_>| time.raw_j2000_seconds(),
380 |raw: Second| Time::from_raw_j2000_seconds(raw).expect("finite J2000 seconds must decode")
381);
382coordinate_format!(
383 JD,
384 Day,
385 |time: Time<_>| j2000_seconds_to_jd(time.raw_j2000_seconds()),
386 |raw: Day| Time::from_raw_j2000_seconds(jd_to_j2000_seconds(raw))
387 .expect("finite Julian date must decode")
388);
389coordinate_format!(
390 MJD,
391 Day,
392 |time: Time<_>| j2000_seconds_to_mjd(time.raw_j2000_seconds()),
393 |raw: Day| Time::from_raw_j2000_seconds(mjd_to_j2000_seconds(raw))
394 .expect("finite Modified Julian date must decode")
395);
396
397impl FormatForScale<UTC> for Unix {
398 #[inline]
399 fn try_from_time(time: Time<UTC>, ctx: &TimeContext) -> Result<Second, ConversionError> {
400 time.raw_unix_seconds_with(ctx)
401 }
402
403 #[inline]
404 fn try_into_time(raw: Second, ctx: &TimeContext) -> Result<Time<UTC>, ConversionError> {
405 Time::from_raw_unix_seconds_with(raw, ctx)
406 }
407}
408
409impl FormatForScale<TAI> for GPS {
410 #[inline]
411 fn try_from_time(time: Time<TAI>, _ctx: &TimeContext) -> Result<Second, ConversionError> {
412 Ok(<Self as InfallibleFormatForScale<TAI>>::from_time(time))
413 }
414
415 #[inline]
416 fn try_into_time(raw: Second, _ctx: &TimeContext) -> Result<Time<TAI>, ConversionError> {
417 Ok(<Self as InfallibleFormatForScale<TAI>>::into_time(raw))
418 }
419}
420
421impl InfallibleFormatForScale<TAI> for GPS {
422 #[inline]
423 fn from_time(time: Time<TAI>) -> Second {
424 time.raw_gps_seconds()
425 }
426
427 #[inline]
428 fn into_time(raw: Second) -> Time<TAI> {
429 Time::from_raw_gps_seconds(raw).expect("finite GPS seconds must decode")
430 }
431}
432
433impl<S: Scale, F> From<EncodedTime<S, F>> for Time<S>
436where
437 F: InfallibleFormatForScale<S>,
438{
439 #[inline]
440 fn from(value: EncodedTime<S, F>) -> Self {
441 value.to_time()
442 }
443}
444
445impl<S: Scale, F> From<Time<S>> for EncodedTime<S, F>
446where
447 F: InfallibleFormatForScale<S>,
448{
449 #[inline]
450 fn from(value: Time<S>) -> Self {
451 Self::from_time_infallible(value)
452 }
453}
454
455impl<S: CoordinateScale> ConversionTarget<S> for J2000s {
458 type Output = EncodedTime<S, J2000s>;
459
460 #[inline]
461 fn try_convert(src: Time<S>) -> Result<Self::Output, ConversionError> {
462 Ok(EncodedTime::from_time_infallible(src))
463 }
464}
465
466impl<S: CoordinateScale> InfallibleConversionTarget<S> for J2000s {
467 #[inline]
468 fn convert(src: Time<S>) -> Self::Output {
469 EncodedTime::from_time_infallible(src)
470 }
471}
472
473impl<S: CoordinateScale> ConversionTarget<S> for JD {
474 type Output = EncodedTime<S, JD>;
475
476 #[inline]
477 fn try_convert(src: Time<S>) -> Result<Self::Output, ConversionError> {
478 Ok(EncodedTime::from_time_infallible(src))
479 }
480}
481
482impl<S: CoordinateScale> InfallibleConversionTarget<S> for JD {
483 #[inline]
484 fn convert(src: Time<S>) -> Self::Output {
485 EncodedTime::from_time_infallible(src)
486 }
487}
488
489impl<S: CoordinateScale> ConversionTarget<S> for MJD {
490 type Output = EncodedTime<S, MJD>;
491
492 #[inline]
493 fn try_convert(src: Time<S>) -> Result<Self::Output, ConversionError> {
494 Ok(EncodedTime::from_time_infallible(src))
495 }
496}
497
498impl<S: CoordinateScale> InfallibleConversionTarget<S> for MJD {
499 #[inline]
500 fn convert(src: Time<S>) -> Self::Output {
501 EncodedTime::from_time_infallible(src)
502 }
503}
504
505impl<S> ConversionTarget<S> for Unix
506where
507 S: crate::scale::Scale + InfallibleScaleConvert<UTC>,
508{
509 type Output = EncodedTime<UTC, Unix>;
510
511 #[inline]
515 fn try_convert(src: Time<S>) -> Result<Self::Output, ConversionError> {
516 let utc = src.to_scale::<UTC>();
517 let raw = Unix::try_from_time(utc, &TimeContext::new())?;
518 Ok(EncodedTime::new_unchecked(raw))
519 }
520}
521
522impl ContextConversionTarget<UTC> for Unix {
523 type Output = EncodedTime<UTC, Unix>;
524
525 #[inline]
526 fn convert_with(src: Time<UTC>, ctx: &TimeContext) -> Result<Self::Output, ConversionError> {
527 let raw = Unix::try_from_time(src, ctx)?;
528 Ok(EncodedTime::new_unchecked(raw))
529 }
530}
531
532impl<S> ContextConversionTarget<S> for Unix
533where
534 S: crate::scale::Scale + crate::scale::conversion::ContextScaleConvert<UTC>,
535{
536 type Output = EncodedTime<UTC, Unix>;
537
538 #[inline]
539 fn convert_with(src: Time<S>, ctx: &TimeContext) -> Result<Self::Output, ConversionError> {
540 let utc = src.to_scale_with::<UTC>(ctx)?;
541 let raw = Unix::try_from_time(utc, ctx)?;
542 Ok(EncodedTime::new_unchecked(raw))
543 }
544}
545
546impl<S> ConversionTarget<S> for GPS
547where
548 S: crate::scale::Scale + InfallibleScaleConvert<TAI>,
549{
550 type Output = EncodedTime<TAI, GPS>;
551
552 #[inline]
553 fn try_convert(src: Time<S>) -> Result<Self::Output, ConversionError> {
554 Ok(Self::convert(src))
555 }
556}
557
558impl<S> InfallibleConversionTarget<S> for GPS
559where
560 S: crate::scale::Scale + InfallibleScaleConvert<TAI>,
561{
562 #[inline]
563 fn convert(src: Time<S>) -> Self::Output {
564 EncodedTime::from_time_infallible(src.to_scale::<TAI>())
565 }
566}
567
568impl<S> ContextConversionTarget<S> for GPS
569where
570 S: crate::scale::Scale + crate::scale::conversion::ContextScaleConvert<TAI>,
571{
572 type Output = EncodedTime<TAI, GPS>;
573
574 #[inline]
575 fn convert_with(src: Time<S>, ctx: &TimeContext) -> Result<Self::Output, ConversionError> {
576 let tai = src.to_scale_with::<TAI>(ctx)?;
577 Ok(EncodedTime::from_time_infallible(tai))
578 }
579}
580
581#[cfg(test)]
584mod tests {
585 use super::*;
586 use crate::context::TimeContext;
587 use crate::scale::{TAI, TT, UTC};
588 use qtty::{Day, Second};
589
590 #[test]
591 fn encoded_time_display_delegates_to_quantity() {
592 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.123_456_789)).unwrap();
593
594 assert_eq!(format!("{jd:.9}"), "2451545.123456789 d");
595 }
596
597 #[test]
598 fn encoded_time_lower_exp_delegates_to_quantity() {
599 let seconds = J2000Seconds::<TT>::try_new(Second::new(1_234.5)).unwrap();
600 let formatted = format!("{seconds:.2e}");
601
602 assert_eq!(formatted, format!("{:.2e}", seconds.raw()));
603 }
604
605 #[test]
606 fn encoded_time_upper_exp_delegates_to_quantity() {
607 let seconds = J2000Seconds::<TT>::try_new(Second::new(1_234.5)).unwrap();
608 let formatted = format!("{seconds:.2E}");
609
610 assert_eq!(formatted, format!("{:.2E}", seconds.raw()));
611 }
612
613 #[test]
614 fn encoded_time_clone_matches_original() {
615 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
616 let cloned = <JulianDate<TT> as Clone>::clone(&jd);
617 assert_eq!(jd.raw(), cloned.raw());
618 }
619
620 #[test]
621 fn encoded_time_partial_eq() {
622 let a = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
623 let b = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
624 let c = JulianDate::<TT>::try_new(Day::new(2_451_546.0)).unwrap();
625 assert_eq!(a, b);
626 assert_ne!(a, c);
627 }
628
629 #[test]
630 fn encoded_time_quantity_is_alias_for_raw() {
631 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.5)).unwrap();
632 assert_eq!(jd.raw(), jd.quantity());
633 }
634
635 #[test]
636 fn encoded_time_try_to_time_on_unix() {
637 let ctx = TimeContext::new();
638 let unix = UnixTime::try_new(Second::new(946_727_935.816)).unwrap();
640 let time = unix.to_time_with(&ctx).unwrap();
641 let back = <Unix as FormatForScale<UTC>>::try_from_time(time, &ctx).unwrap();
643 assert!((back - Second::new(946_727_935.816)).abs() < Second::new(1e-3));
644 }
645
646 #[test]
647 fn encoded_time_to_infallible_conversion() {
648 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
650 let mjd: ModifiedJulianDate<TT> = jd.to::<MJD>();
651 assert!((mjd.raw().value() - 51_544.5).abs() < 1e-9);
653 }
654
655 #[test]
656 fn encoded_time_try_to_conversion() {
657 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
658 let mjd: ModifiedJulianDate<TT> = jd.try_to::<MJD>().unwrap();
659 assert!((mjd.raw().value() - 51_544.5).abs() < 1e-9);
660 }
661
662 #[test]
663 fn encoded_time_to_with_for_unix() {
664 let ctx = TimeContext::new();
665 let jd = JulianDate::<UTC>::try_new(Day::new(2_451_545.0)).unwrap();
666 let unix: UnixTime = jd.to_with::<Unix>(&ctx).unwrap();
667 assert!(unix.raw().value().is_finite());
669 assert!(unix.raw().value() > 9e8 && unix.raw().value() < 1e10);
670 }
671
672 #[test]
673 fn gps_format_roundtrip_through_tai() {
674 let gps_seconds = Second::new(0.0);
675 let time: crate::time::Time<TAI> =
676 <GPS as InfallibleFormatForScale<TAI>>::into_time(gps_seconds);
677 let back = <GPS as InfallibleFormatForScale<TAI>>::from_time(time);
678 assert!((back - gps_seconds).abs() < Second::new(1e-12));
679 }
680
681 #[test]
682 fn gps_encoded_time_to_time_roundtrip() {
683 let gps = GpsTime::try_new(Second::new(1_234_567.89)).unwrap();
684 let time = gps.to_time();
685 let back: GpsTime = time.into();
686 assert!((back.raw() - gps.raw()).abs() < Second::new(1e-6));
688 }
689
690 #[test]
691 fn from_encoded_time_into_time() {
692 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
693 let time: crate::time::Time<TT> = jd.into();
694 let back: JulianDate<TT> = time.into();
695 assert!((back.raw() - Day::new(2_451_545.0)).abs() < Day::new(1e-12));
696 }
697
698 #[test]
699 fn infallible_conversion_target_for_j2000s() {
700 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
701 let time = jd.to_time();
702 let j2k: J2000Seconds<TT> = J2000s::convert(time);
704 assert!((j2k.raw().value()).abs() < 1e-6);
706 }
707
708 #[test]
709 fn conversion_target_try_convert_for_j2000s() {
710 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
711 let time = jd.to_time();
712 let j2k: J2000Seconds<TT> = J2000s::try_convert(time).unwrap();
713 assert!((j2k.raw().value()).abs() < 1e-6);
714 }
715
716 #[test]
717 fn conversion_target_try_convert_for_jd() {
718 let mjd = ModifiedJulianDate::<TT>::try_new(Day::new(51_544.0)).unwrap();
719 let time = mjd.to_time();
720 let jd: JulianDate<TT> = JD::try_convert(time).unwrap();
721 assert!((jd.raw().value() - 2_451_544.5).abs() < 1e-9);
723 }
724
725 #[test]
726 fn conversion_target_try_convert_for_mjd() {
727 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
728 let time = jd.to_time();
729 let mjd: ModifiedJulianDate<TT> = MJD::try_convert(time).unwrap();
730 assert!((mjd.raw().value() - 51_544.5).abs() < 1e-9);
732 }
733
734 #[test]
735 fn gps_conversion_target_try_convert() {
736 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
737 let time = jd.to_time();
738 let gps: GpsTime = GPS::try_convert(time).unwrap();
739 assert!(gps.raw().is_finite());
740 }
741
742 #[test]
743 fn unix_context_conversion_target() {
744 let ctx = TimeContext::new();
745 let jd = JulianDate::<UTC>::try_new(Day::new(2_451_545.0)).unwrap();
746 let utc_time = jd.to_time();
747 let unix =
748 <Unix as crate::target::ContextConversionTarget<UTC>>::convert_with(utc_time, &ctx)
749 .unwrap();
750 assert!(unix.raw().value().is_finite());
752 assert!(unix.raw().value() > 9e8 && unix.raw().value() < 1e10);
753 }
754
755 #[test]
756 fn debug_includes_format_and_scale() {
757 let jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
758 let dbg = format!("{jd:?}");
759 assert!(dbg.contains("TT"), "debug should contain scale name");
760 assert!(dbg.contains("JD"), "debug should contain format name");
761 }
762
763 #[test]
769 fn jd_on_tt_and_utc_are_distinct_types() {
770 fn accept_tt(x: EncodedTime<TT, JD>) -> Day {
771 x.raw()
772 }
773 fn accept_utc(x: EncodedTime<UTC, JD>) -> Day {
774 x.raw()
775 }
776
777 let tt_jd = JulianDate::<TT>::try_new(Day::new(2_451_545.0)).unwrap();
778 let utc_jd = JulianDate::<UTC>::try_new(Day::new(2_451_545.0)).unwrap();
779
780 let _ = accept_tt(tt_jd);
782 let _ = accept_utc(utc_jd);
783 }
784
785 #[test]
786 fn format_names_are_correct() {
787 assert_eq!(JD::NAME, "JD");
788 assert_eq!(MJD::NAME, "MJD");
789 assert_eq!(J2000s::NAME, "J2000s");
790 assert_eq!(Unix::NAME, "Unix");
791 assert_eq!(GPS::NAME, "GPS");
792 }
793}