Skip to main content

tempoch_core/format/
chrono.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! `chrono` convenience impls for TT-encoded instants whose format round-trips on TT
5//! (e.g. [`JD`], [`MJD`], [`J2000s`] — not [`GPS`], which encodes TAI).
6
7use crate::earth::context::TimeContext;
8use crate::format::TimeFormat;
9use crate::foundation::error::ConversionError;
10use crate::model::scale::{TT, UTC};
11use crate::model::target::InfallibleConversionTarget;
12use crate::model::time::Time;
13use crate::InfallibleFormatForScale;
14use chrono::{DateTime, Utc};
15
16impl<F: TimeFormat> Time<TT, F>
17where
18    F: InfallibleFormatForScale<TT> + InfallibleConversionTarget<TT, Output = Time<TT, F>>,
19{
20    /// Build a TT [`Time`] in format `F` from a UTC `chrono` timestamp.
21    #[inline]
22    pub fn try_from_chrono(dt: DateTime<Utc>) -> Result<Self, ConversionError> {
23        Ok(Time::<UTC>::try_from_chrono(dt)?.to::<TT>().reinterpret())
24    }
25
26    /// Build a TT [`Time`] in format `F` from a UTC `chrono` timestamp.
27    #[track_caller]
28    #[inline]
29    pub fn from_chrono(dt: DateTime<Utc>) -> Self {
30        Self::try_from_chrono(dt).expect("UTC conversion failed; use try_from_chrono")
31    }
32
33    /// Like [`Self::try_from_chrono`], but uses an explicit [`TimeContext`].
34    #[inline]
35    pub fn try_from_chrono_with(
36        dt: DateTime<Utc>,
37        ctx: &TimeContext,
38    ) -> Result<Self, ConversionError> {
39        Ok(Time::<UTC>::try_from_chrono_with(dt, ctx)?
40            .to::<TT>()
41            .reinterpret())
42    }
43
44    /// Like [`Self::from_chrono`], but uses an explicit [`TimeContext`].
45    #[track_caller]
46    #[inline]
47    pub fn from_chrono_with(dt: DateTime<Utc>, ctx: &TimeContext) -> Self {
48        Self::try_from_chrono_with(dt, ctx)
49            .expect("UTC conversion failed; use try_from_chrono_with")
50    }
51
52    /// Convert to a UTC `chrono` timestamp (TT → UTC using a default context).
53    #[inline]
54    pub fn try_to_chrono(self) -> Result<DateTime<Utc>, ConversionError> {
55        self.to_j2000s().to::<UTC>().try_to_chrono()
56    }
57
58    /// Convert to a UTC `chrono` timestamp (TT → UTC using a default context).
59    #[inline]
60    pub fn to_chrono(self) -> Option<DateTime<Utc>> {
61        self.try_to_chrono().ok()
62    }
63
64    /// Like [`Self::try_to_chrono`], but uses an explicit [`TimeContext`].
65    #[inline]
66    pub fn try_to_chrono_with(self, ctx: &TimeContext) -> Result<DateTime<Utc>, ConversionError> {
67        self.to_j2000s().to::<UTC>().try_to_chrono_with(ctx)
68    }
69
70    /// Like [`Self::to_chrono`], but uses an explicit [`TimeContext`].
71    #[inline]
72    pub fn to_chrono_with(self, ctx: &TimeContext) -> Option<DateTime<Utc>> {
73        self.try_to_chrono_with(ctx).ok()
74    }
75}
76
77impl<F: TimeFormat> From<DateTime<Utc>> for Time<TT, F>
78where
79    F: InfallibleFormatForScale<TT> + InfallibleConversionTarget<TT, Output = Time<TT, F>>,
80{
81    #[inline]
82    fn from(value: DateTime<Utc>) -> Self {
83        Self::from_chrono(value)
84    }
85}