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`].
6    pub fn to_jiff_timestamp(&self) -> Timestamp {
7        let nanos = self.target(Scale::UTC).to_unix().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    /// Creates a [`Dt`] from a [`jiff::Timestamp`].
22    ///
23    /// This is the inverse of [`Dt::to_jiff_timestamp`].
24    #[inline]
25    pub fn from_jiff_timestamp(ts: Timestamp) -> Dt {
26        Dt::from_diff_and_scale(
27            Dt::from_ns(ts.as_nanosecond(), Scale::UTC),
28            Self::UNIX_EPOCH,
29            false,
30        )
31    }
32
33    /// Converts this [`Dt`] to a [`jiff::Span`] (seconds + nanoseconds only).
34    pub fn to_jiff_span(&self) -> Span {
35        let total_nanos = self.to_ns();
36        let seconds = Dt::i128_to_i64(total_nanos.div_euclid(1_000_000_000));
37        let nanoseconds = Dt::i128_to_i64(total_nanos.rem_euclid(1_000_000_000));
38
39        if let Ok(base) = Span::new().try_seconds(seconds)
40            && let Ok(span) = base.try_nanoseconds(nanoseconds)
41        {
42            return span;
43        }
44        // Saturate to Jiff's Span limits
45        if total_nanos >= 0 {
46            Span::new()
47                .seconds(631_107_417_600i64)
48                .nanoseconds(999_999_999i64)
49        } else {
50            Span::new()
51                .seconds(-631_107_417_600i64)
52                .nanoseconds(-999_999_999i64)
53        }
54    }
55
56    /// Converts this `Span` to a `jiff::SignedDuration` (nanosecond precision).
57    ///
58    /// - Sub-nanosecond attoseconds are **truncated toward zero**.
59    /// - Supports the **entire** range of `Span` (never saturates).
60    #[inline]
61    pub fn to_jiff_signed_duration(&self) -> SignedDuration {
62        SignedDuration::from_nanos_i128(self.to_ns())
63    }
64
65    /// Creates a [`Dt`] from a `jiff::SignedDuration` (nanosecond precision).
66    ///
67    /// This is the inverse of [`Dt::to_jiff_signed_duration`].
68    #[inline]
69    pub fn from_jiff_signed_duration(dur: SignedDuration) -> Dt {
70        Self::from_ns(dur.as_nanos(), Scale::TAI)
71    }
72
73    /// Creates a [`Dt`] from a `jiff::Dt`.
74    ///
75    /// This is the inverse of [`Dt::to_jiff_span`].
76    #[inline]
77    pub fn from_jiff_span(span: Span) -> Result<Self, DtErr> {
78        let dur = SignedDuration::try_from(span)
79            .map_err(|e| an_err!(DtErrKind::InvalidInput, "{:?}: {}", span, e))?;
80        Ok(Self::from_jiff_signed_duration(dur))
81    }
82}