deep-time 0.1.0-beta.32

High-precision, no-std, no-alloc date-time library, leap-seconds, time scales, relativistic time, and a powerful date & duration parser
Documentation
use crate::{Dt, DtErr, DtErrKind, Scale, an_err};
use core::convert::{From, TryFrom};
use jiff::{SignedDuration, Span, Timestamp};

impl Dt {
    /// Converts this [`Dt`] to a [`jiff::Timestamp`].
    ///
    /// ## Time scale
    ///
    /// [`jiff::Timestamp`] is a **Unix / POSIX** instant: nanoseconds since
    /// `1970-01-01 00:00:00Z`. Jiff documents this as “the Unix timescale with a
    /// UTC offset of zero” and **does not support leap seconds** (it behaves as if
    /// they do not exist in the numeric count). Conversion therefore goes through
    /// [`Scale::UTC`](../enum.Scale.html#variant.UTC) and the Unix epoch so deep-time's
    /// leap-second tables are applied on the way out of TAI storage.
    ///
    /// This is **not** a TAI timestamp. A [`Dt`] stored on TAI (or any other scale)
    /// is converted to the equivalent UTC civil instant before the Unix count is
    /// taken.
    ///
    /// ## Precision and range
    ///
    /// - Sub-nanosecond attoseconds are truncated toward zero.
    /// - Saturates at [`Timestamp::MIN`] / [`Timestamp::MAX`] if out of range
    ///   (jiff's supported range is roughly years −9999…9999).
    pub fn to_jiff_timestamp(&self) -> Timestamp {
        let mut nanos = self.target(Scale::UTC).to_unix().to_ns().0;
        // required to due jiff panic in v0.2.33
        if nanos > Timestamp::MAX.as_nanosecond() {
            nanos = Timestamp::MAX.as_nanosecond();
        } else if nanos < Timestamp::MIN.as_nanosecond() {
            nanos = Timestamp::MIN.as_nanosecond();
        }
        match Timestamp::from_nanosecond(nanos) {
            Ok(ts) => ts,
            Err(_) => {
                if nanos >= 0 {
                    Timestamp::MAX
                } else {
                    Timestamp::MIN
                }
            }
        }
    }

    /// Creates a TAI [`Dt`] from a [`jiff::Timestamp`].
    ///
    /// Inverse of [`Dt::to_jiff_timestamp`]. The Unix nanosecond count is treated
    /// as a POSIX/UTC elapsed time since the Unix epoch (not TAI, and not as a
    /// count since J2000), then converted into deep-time's TAI storage via
    /// [`Dt::from_unix`].
    #[inline]
    pub fn from_jiff_timestamp(ts: Timestamp) -> Dt {
        Dt::from_unix_ns(ts.as_nanosecond())
    }

    /// Converts this [`Dt`] to a [`jiff::Span`] (seconds + nanoseconds only).
    ///
    /// ## Time scale
    ///
    /// A [`Span`] built this way is a pure elapsed span of SI nanoseconds taken
    /// from this [`Dt`]'s raw attosecond count. It is **not** tied to UTC leap
    /// seconds or calendar units (years/months/days are left at zero).
    ///
    /// ## Precision and range
    ///
    /// - Sub-nanosecond attoseconds are truncated toward zero via
    ///   [`Dt::to_jiff_signed_duration`](../struct.Dt.html#method.to_jiff_signed_duration).
    /// - Converts that duration with jiff's `Span::try_from` (`TryFrom<SignedDuration>`).
    ///   Returns [`Err`] when the duration is wider than a `Span` can represent.
    ///   Never panics.
    #[inline]
    pub fn to_jiff_span(&self) -> Result<Span, DtErr> {
        Span::try_from(self.to_jiff_signed_duration())
            .map_err(|e| an_err!(DtErrKind::InvalidInput, "{}", e))
    }

    /// Converts this [`Dt`] to a [`jiff::SignedDuration`] (nanosecond precision).
    ///
    /// ## Time scale
    ///
    /// A [`SignedDuration`] is a pure elapsed span (SI seconds + nanoseconds). It
    /// is **not** tied to UTC, TAI, or any other time scale. The conversion uses
    /// this [`Dt`]'s raw attosecond count (same convention as chrono/`time` duration
    /// interop).
    ///
    /// ## Precision and range
    ///
    /// - Sub-nanosecond attoseconds are **truncated toward zero**.
    /// - Saturates at [`SignedDuration::MIN`] / [`SignedDuration::MAX`] when the
    ///   nanosecond count requires more than an `i64` whole-second field
    ///   (jiff's `from_nanos_i128` would otherwise panic). Never panics.
    pub fn to_jiff_signed_duration(&self) -> SignedDuration {
        let nanos = self.to_ns().0;
        match SignedDuration::try_from_nanos_i128(nanos) {
            Some(dur) => dur,
            None => {
                if nanos >= 0 {
                    SignedDuration::MAX
                } else {
                    SignedDuration::MIN
                }
            }
        }
    }

    /// Creates a [`Dt`] from a [`jiff::SignedDuration`] (nanosecond precision).
    ///
    /// Inverse of [`Dt::to_jiff_signed_duration`]. The result is a span stored on
    /// TAI (no leap-second adjustment of the duration itself), matching
    /// chrono/`time` duration interop.
    #[inline(always)]
    pub fn from_jiff_signed_duration(dur: SignedDuration) -> Dt {
        Self::from_ns(dur.as_nanos(), 0, Scale::TAI, Scale::TAI)
    }

    /// Creates a [`Dt`] from a [`jiff::Span`] (nanosecond precision).
    ///
    /// Inverse of [`Dt::to_jiff_span`](../struct.Dt.html#method.to_jiff_span).
    /// Converts the span to a `SignedDuration` (seconds + nanoseconds only;
    /// calendar units must already be zero or convertible without a relative
    /// datetime) and then uses
    /// [`Dt::from_jiff_signed_duration`](../struct.Dt.html#method.from_jiff_signed_duration).
    pub fn from_jiff_span(span: Span) -> Result<Self, DtErr> {
        let dur = SignedDuration::try_from(span)
            .map_err(|e| an_err!(DtErrKind::InvalidInput, "{}", e))?;
        Ok(Self::from_jiff_signed_duration(dur))
    }
}

impl From<Timestamp> for Dt {
    #[inline]
    fn from(ts: Timestamp) -> Self {
        Self::from_jiff_timestamp(ts)
    }
}

impl From<Dt> for Timestamp {
    #[inline]
    fn from(dt: Dt) -> Self {
        dt.to_jiff_timestamp()
    }
}

impl From<SignedDuration> for Dt {
    #[inline]
    fn from(dur: SignedDuration) -> Self {
        Self::from_jiff_signed_duration(dur)
    }
}

impl From<Dt> for SignedDuration {
    #[inline]
    fn from(dt: Dt) -> Self {
        dt.to_jiff_signed_duration()
    }
}

impl TryFrom<Span> for Dt {
    type Error = DtErr;

    fn try_from(span: Span) -> Result<Self, Self::Error> {
        Self::from_jiff_span(span)
    }
}