Skip to main content

deep_time/dt/
hifitime.rs

1use crate::{Dt, Scale};
2use hifitime::{Duration, Epoch};
3
4impl Dt {
5    /// Converts this [`Dt`] to a [`hifitime::Epoch`] (TAI scale).
6    ///
7    /// Round-trips with [`Dt::from_hifitime`].
8    pub fn to_hifitime_epoch(&self, current: Scale) -> Epoch {
9        let nanos = self.to(current, Scale::TAI).to_ns();
10
11        let j1900 = Epoch::from_gregorian_tai(1900, 1, 1, 12, 0, 0, 0);
12        let j2000 = Epoch::from_gregorian_tai(2000, 1, 1, 12, 0, 0, 0);
13        let offset_ns = j2000.to_tai_duration().total_nanoseconds()
14            - j1900.to_tai_duration().total_nanoseconds();
15
16        let ns_since_j1900 = nanos + offset_ns;
17
18        let dur = Duration::from_total_nanoseconds(ns_since_j1900);
19        let (centuries, nanos) = dur.to_parts();
20
21        Epoch::from_tai_parts(centuries, nanos)
22    }
23
24    /// Creates a [`Dt`] from a [`hifitime::Epoch`].
25    ///
26    /// - The conversion is exact (within hifitime's nanosecond precision).
27    /// - Uses a runtime-computed offset so it always matches whatever
28    ///   calendar math hifitime uses (including negative years).
29    pub fn from_hifitime_epoch(epoch: Epoch) -> Self {
30        let ns_since_j1900 = epoch.to_tai_duration().total_nanoseconds();
31
32        let j1900 = Epoch::from_gregorian_tai(1900, 1, 1, 12, 0, 0, 0);
33        let j2000 = Epoch::from_gregorian_tai(2000, 1, 1, 12, 0, 0, 0);
34        let offset_ns = j2000.to_tai_duration().total_nanoseconds()
35            - j1900.to_tai_duration().total_nanoseconds();
36
37        let ns_since_zero_tai = ns_since_j1900 - offset_ns;
38        Self::from_ns(ns_since_zero_tai, Scale::TAI)
39    }
40
41    /// Converts this [`Dt`] to a [`hifitime::Duration`] (nanosecond precision).
42    ///
43    /// - Sub-nanosecond attoseconds are **truncated toward zero**.
44    /// - The conversion is exact up to the nanosecond (128-bit integer arithmetic).
45    /// - Internally uses [`hifitime::Duration::from_total_nanoseconds`], which
46    ///   automatically normalizes centuries/nanoseconds and saturates at
47    ///   [`Duration::MAX`] / [`Duration::MIN`] if outside hifitime's range
48    ///   (±32,768 centuries).
49    #[inline]
50    pub fn to_hifitime_duration(&self) -> Duration {
51        Duration::from_total_nanoseconds(self.to_attos() / 1_000_000_000i128)
52    }
53
54    /// Creates a [`Dt`] from a [`hifitime::Duration`] (nanosecond precision).
55    ///
56    /// Inverse of [`Dt::to_hifitime_duration`].
57    #[inline]
58    pub fn from_hifitime_duration(dur: Duration) -> Self {
59        Self::from_ns(dur.total_nanoseconds(), Scale::TAI)
60    }
61}