Skip to main content

deep_time/dt/
jiff.rs

1use crate::{Dt, DtErr, DtErrKind, Scale, an_err};
2use jiff::{SignedDuration, Span, Timestamp};
3
4impl Dt {
5    /// Converts this [`Dt`] to a [`jiff::Timestamp`] (always in UTC).
6    pub fn to_jiff_timestamp(&self, current: Scale) -> Timestamp {
7        let nanos = self.to(current, Scale::UTC).to_ns();
8
9        match Timestamp::from_nanosecond(nanos) {
10            Ok(ts) => ts,
11            Err(_) => {
12                if nanos >= 0 {
13                    Timestamp::MAX
14                } else {
15                    Timestamp::MIN
16                }
17            }
18        }
19    }
20
21    /// Converts this `Dt` to a [`jiff::Span`] (seconds + nanoseconds only).
22    pub fn to_jiff_span(&self) -> Span {
23        let total_nanos = self.to_ns();
24        let seconds = Dt::clamp_i128_to_i64(total_nanos.div_euclid(1_000_000_000));
25        let nanoseconds = Dt::clamp_i128_to_i64(total_nanos.rem_euclid(1_000_000_000));
26
27        if let Ok(base) = Span::new().try_seconds(seconds)
28            && let Ok(span) = base.try_nanoseconds(nanoseconds)
29        {
30            return span;
31        }
32        // Saturate to Jiff's Span limits
33        if total_nanos >= 0 {
34            Span::new()
35                .seconds(631_107_417_600i64)
36                .nanoseconds(999_999_999i64)
37        } else {
38            Span::new()
39                .seconds(-631_107_417_600i64)
40                .nanoseconds(-999_999_999i64)
41        }
42    }
43
44    /// Converts this `Span` to a `jiff::SignedDuration` (nanosecond precision).
45    ///
46    /// - Sub-nanosecond attoseconds are **truncated toward zero**.
47    /// - Supports the **entire** range of `Span` (never saturates).
48    #[inline]
49    pub fn to_jiff_signed_duration(&self) -> SignedDuration {
50        SignedDuration::from_nanos_i128(self.to_ns())
51    }
52
53    /// Creates a `Dt` from a `jiff::Timestamp`.
54    ///
55    /// This is the inverse of [`Dt::to_jiff_timestamp`].
56    #[inline]
57    pub fn from_jiff_timestamp(ts: Timestamp) -> Self {
58        Dt::from_dt(Dt::from_ns(ts.as_nanosecond(), Scale::TAI), Scale::UTC)
59    }
60
61    /// Creates a [`Dt`] from a `jiff::SignedDuration` (nanosecond precision).
62    ///
63    /// This is the inverse of [`Dt::to_jiff_signed_duration`].
64    #[inline]
65    pub fn from_jiff_signed_duration(dur: SignedDuration) -> Self {
66        Self::from_ns(dur.as_nanos(), Scale::TAI)
67    }
68
69    /// Creates a [`Dt`] from a `jiff::Dt`.
70    ///
71    /// This is the inverse of [`Dt::to_jiff_span`].
72    #[inline]
73    pub fn from_jiff_span(span: Span) -> Result<Self, DtErr> {
74        let dur = SignedDuration::try_from(span)
75            .map_err(|e| an_err!(DtErrKind::InvalidInput, "{:?}: {}", span, e))?;
76        Ok(Self::from_jiff_signed_duration(dur))
77    }
78}