Skip to main content

deep_time/dt/
time.rs

1use crate::{Dt, Scale};
2use core::convert::From;
3use time::{Duration, OffsetDateTime, Timestamp, UtcDateTime, UtcOffset};
4
5impl Dt {
6    /// Converts this [`Dt`] to a [`time::Timestamp`].
7    ///
8    /// ## Time scale
9    ///
10    /// [`time::Timestamp`] is a **Unix / POSIX** instant: nanoseconds since
11    /// 1970-01-01 00:00:00 UTC. Like `chrono` and `jiff`, it does **not** count
12    /// leap seconds in the numeric value. Conversion therefore goes through
13    /// [`Scale::UTC`](../enum.Scale.html#variant.UTC) and the Unix epoch so deep-time's leap-second
14    /// tables are applied on the way in and out of TAI storage.
15    ///
16    /// This is **not** a TAI timestamp.
17    ///
18    /// ## Precision and range
19    ///
20    /// - Sub-nanosecond attoseconds are truncated toward zero.
21    /// - Saturates at [`Timestamp::MIN`] / [`Timestamp::MAX`] if out of range
22    ///   (year range depends on the `time` crate's `large-dates` feature; without
23    ///   it, roughly ±9999).
24    pub fn to_time_timestamp(&self) -> Timestamp {
25        let nanos = self.target(Scale::UTC).to_unix().to_ns().0;
26        match Timestamp::from_nanoseconds(nanos) {
27            Ok(ts) => ts,
28            Err(_) => {
29                if nanos >= 0 {
30                    Timestamp::MAX
31                } else {
32                    Timestamp::MIN
33                }
34            }
35        }
36    }
37
38    /// Creates a TAI [`Dt`] from a [`time::Timestamp`].
39    ///
40    /// Inverse of [`Dt::to_time_timestamp`]. The Unix nanosecond count is treated
41    /// as a POSIX/UTC elapsed time since the Unix epoch (not TAI), then converted
42    /// into deep-time's TAI storage via [`Dt::from_unix`].
43    #[inline]
44    pub fn from_time_timestamp(ts: Timestamp) -> Dt {
45        Self::from_unix_ns(ts.as_nanoseconds())
46    }
47
48    /// Converts this [`Dt`] to a [`time::OffsetDateTime`] with a UTC offset of zero.
49    ///
50    /// ## Time scale
51    ///
52    /// Same Unix/POSIX UTC semantics as [`Dt::to_time_timestamp`]. The returned value
53    /// always has [`UtcOffset::UTC`]; wall-clock fields are UTC civil time, not TAI.
54    ///
55    /// ## Precision and range
56    ///
57    /// - Sub-nanosecond attoseconds are truncated toward zero.
58    /// - Saturates at the minimum/maximum representable `OffsetDateTime` in UTC
59    ///   (via [`Timestamp::MIN`] / [`Timestamp::MAX`]).
60    #[inline]
61    pub fn to_time_offset_datetime_utc(&self) -> OffsetDateTime {
62        self.to_time_timestamp().to_offset(UtcOffset::UTC)
63    }
64
65    /// Creates a TAI [`Dt`] from a [`time::OffsetDateTime`].
66    ///
67    /// Uses [`OffsetDateTime::unix_timestamp_nanos`], so the absolute instant is
68    /// taken correctly regardless of the value's fixed offset. The offset itself
69    /// is not preserved on the resulting [`Dt`] (deep-time stores scale, not
70    /// civil zone offset).
71    ///
72    /// Same POSIX/UTC Unix semantics as [`Dt::from_time_timestamp`].
73    #[inline]
74    pub fn from_time_offset_datetime(dt: OffsetDateTime) -> Dt {
75        Self::from_unix_ns(dt.unix_timestamp_nanos())
76    }
77
78    /// Converts this [`Dt`] to a [`time::UtcDateTime`].
79    ///
80    /// ## Time scale
81    ///
82    /// Same Unix/POSIX UTC semantics as [`Dt::to_time_timestamp`]. Fields are UTC
83    /// civil time, not TAI.
84    ///
85    /// ## Precision and range
86    ///
87    /// - Sub-nanosecond attoseconds are truncated toward zero.
88    /// - Saturates at [`UtcDateTime::MIN`] / [`UtcDateTime::MAX`] if out of range.
89    #[inline]
90    pub fn to_time_utc_datetime(&self) -> UtcDateTime {
91        self.to_time_timestamp().to_utc()
92    }
93
94    /// Creates a TAI [`Dt`] from a [`time::UtcDateTime`].
95    ///
96    /// Inverse of [`Dt::to_time_utc_datetime`]. Same POSIX/UTC Unix semantics as
97    /// [`Dt::from_time_timestamp`].
98    #[inline]
99    pub fn from_time_utc_datetime(dt: UtcDateTime) -> Dt {
100        Self::from_unix_ns(dt.unix_timestamp_nanos())
101    }
102
103    /// Converts this [`Dt`] to a [`time::Duration`] (nanosecond precision).
104    ///
105    /// ## Time scale
106    ///
107    /// A [`time::Duration`] is a pure elapsed span (SI seconds + nanoseconds). It is
108    /// **not** tied to UTC, TAI, or any other time scale. The conversion uses this
109    /// [`Dt`]'s raw attosecond count (same convention as chrono/jiff duration interop).
110    ///
111    /// ## Precision and range
112    ///
113    /// - Sub-nanosecond attoseconds are truncated toward zero.
114    /// - Saturates at [`Duration::MIN`] / [`Duration::MAX`]
115    ///   (roughly ±292 billion years) if out of range.
116    pub fn to_time_duration(&self) -> Duration {
117        let total_nanos = self.to_ns().0;
118        let max_ns = Duration::MAX.whole_nanoseconds();
119        let min_ns = Duration::MIN.whole_nanoseconds();
120        if total_nanos >= max_ns {
121            Duration::MAX
122        } else if total_nanos <= min_ns {
123            Duration::MIN
124        } else {
125            Duration::nanoseconds_i128(total_nanos)
126        }
127    }
128
129    /// Creates a [`Dt`] from a [`time::Duration`] (nanosecond precision).
130    ///
131    /// Inverse of [`Dt::to_time_duration`]. The result is a span stored on TAI
132    /// (no leap-second adjustment of the duration itself), matching chrono/jiff
133    /// duration interop.
134    #[inline]
135    pub fn from_time_duration(dur: Duration) -> Dt {
136        Self::from_ns(dur.whole_nanoseconds(), 0, Scale::TAI, Scale::TAI)
137    }
138}
139
140impl From<Timestamp> for Dt {
141    #[inline]
142    fn from(ts: Timestamp) -> Self {
143        Self::from_time_timestamp(ts)
144    }
145}
146
147impl From<Dt> for Timestamp {
148    #[inline]
149    fn from(dt: Dt) -> Self {
150        dt.to_time_timestamp()
151    }
152}
153
154impl From<OffsetDateTime> for Dt {
155    #[inline]
156    fn from(dt: OffsetDateTime) -> Self {
157        Self::from_time_offset_datetime(dt)
158    }
159}
160
161impl From<Dt> for OffsetDateTime {
162    #[inline]
163    fn from(dt: Dt) -> Self {
164        dt.to_time_offset_datetime_utc()
165    }
166}
167
168impl From<UtcDateTime> for Dt {
169    #[inline]
170    fn from(dt: UtcDateTime) -> Self {
171        Self::from_time_utc_datetime(dt)
172    }
173}
174
175impl From<Dt> for UtcDateTime {
176    #[inline]
177    fn from(dt: Dt) -> Self {
178        dt.to_time_utc_datetime()
179    }
180}
181
182impl From<Duration> for Dt {
183    #[inline]
184    fn from(dur: Duration) -> Self {
185        Self::from_time_duration(dur)
186    }
187}
188
189impl From<Dt> for Duration {
190    #[inline]
191    fn from(dt: Dt) -> Self {
192        dt.to_time_duration()
193    }
194}