Skip to main content

deep_time/dt/
constructors.rs

1use crate::{
2    ATTOS_PER_FS, ATTOS_PER_MS, ATTOS_PER_NS, ATTOS_PER_PS, ATTOS_PER_SEC, ATTOS_PER_US,
3    ClockDrift, ClockModel, Dt, FS_PER_SEC, MS_PER_SEC, NS_PER_SEC, PS_PER_SEC, Scale,
4    TT_TAI_OFFSET_SPAN, UNIX_EPOCH_TO_J2000_NOON_UTC, US_PER_SEC,
5};
6
7impl Dt {
8    /// The library’s reference zero instant: exactly **2000-01-01 12:00:00 TAI**.
9    pub const ZERO: Self = Self::new(0, 0);
10
11    /// The TAI instant that corresponds to the conventional **J2000.0 epoch**
12    /// (2000-01-01 12:00:00 **TT**, JD 2451545.0 TT).
13    pub const J2000_TAI: Self = Self::ZERO.sub(TT_TAI_OFFSET_SPAN);
14
15    /// The J1900.0 epoch expressed in TAI (1900-01-01 12:00:00 TAI).
16    pub const J1900_TAI: Self = Self::new(-3_155_760_000, 0);
17
18    /// Library zero points (same physical instant as ZERO, different tags)
19    pub const GPS_ZERO: Self = Self::new(19, 0);
20    pub const GST_ZERO: Self = Self::new(19, 0);
21    pub const QZSS_ZERO: Self = Self::new(19, 0);
22    pub const BDT_ZERO: Self = Self::new(33, 0);
23
24    /// TAI time between 1970-01-01 midnight and 2000-01-01 noon
25    pub const UNIX_EPOCH: Self = Self::new(-UNIX_EPOCH_TO_J2000_NOON_UTC, 0);
26
27    /// Traditional GNSS epochs
28    pub const GPS_EPOCH: Self = Self::new(-630_763_200 + 19, 0);
29    pub const GALEX_EPOCH: Self = Self::GPS_EPOCH;
30    pub const GALILEO_EPOCH: Self = Self::new(-11_448_000 + 19, 0);
31    pub const BDT_EPOCH: Self = Self::new(189_345_600 + 33, 0);
32
33    /// Creates a new `Dt` from whole seconds, a subsecond part in attoseconds,
34    /// and a scale, automatically normalizing the representation.
35    #[inline]
36    pub const fn new(sec: i64, attos: u64) -> Self {
37        let mut tp = Self { sec, attos };
38        tp.carry_over();
39        tp
40    }
41
42    /// Creates a new custom clock model using this exact instant as the reference epoch.
43    ///
44    /// The supplied `ClockDrift` defines the relativistic model for the new clock.
45    /// The resulting `ClockModel` can be used to convert to or from the custom timescale
46    /// even after the observer has left the original reference frame.
47    #[inline]
48    pub const fn new_custom_clock(self, drift: ClockDrift) -> ClockModel {
49        ClockModel::new(Scale::Custom, self, drift)
50    }
51
52    #[inline]
53    pub const fn from_attos(attos: i128, scale: Scale) -> Self {
54        let sec = (attos / ATTOS_PER_SEC as i128) as i64;
55        let subsec = (attos % ATTOS_PER_SEC as i128) as u64;
56        Self::from(sec, subsec, scale)
57    }
58
59    #[inline]
60    pub const fn from_sec(sec: i64, scale: Scale) -> Self {
61        Self::from(sec, 0, scale)
62    }
63
64    #[inline]
65    pub const fn from_ms(ms: i128, scale: Scale) -> Self {
66        let sec = ms.div_euclid(MS_PER_SEC) as i64;
67        let remaining_ms = ms.rem_euclid(MS_PER_SEC);
68        let subsec = (remaining_ms as u64) * ATTOS_PER_MS;
69        Self::from(sec, subsec, scale)
70    }
71
72    #[inline]
73    pub const fn from_us(us: i128, scale: Scale) -> Self {
74        let sec = us.div_euclid(US_PER_SEC) as i64;
75        let remaining_us = us.rem_euclid(US_PER_SEC);
76        let subsec = (remaining_us as u64) * ATTOS_PER_US;
77        Self::from(sec, subsec, scale)
78    }
79
80    #[inline]
81    pub const fn from_ns(ns: i128, scale: Scale) -> Self {
82        let sec = ns.div_euclid(NS_PER_SEC) as i64;
83        let remaining_ns = ns.rem_euclid(NS_PER_SEC);
84        let subsec = (remaining_ns as u64) * ATTOS_PER_NS;
85        Self::from(sec, subsec, scale)
86    }
87
88    #[inline]
89    pub const fn from_ps(ps: i128, scale: Scale) -> Self {
90        let sec = ps.div_euclid(PS_PER_SEC) as i64;
91        let remaining_ps = ps.rem_euclid(PS_PER_SEC);
92        let subsec = (remaining_ps as u64) * ATTOS_PER_PS;
93        Self::from(sec, subsec, scale)
94    }
95
96    #[inline]
97    pub const fn from_fs(fs: i128, scale: Scale) -> Self {
98        let sec = fs.div_euclid(FS_PER_SEC) as i64;
99        let remaining_fs = fs.rem_euclid(FS_PER_SEC);
100        let subsec = (remaining_fs as u64) * ATTOS_PER_FS;
101        Self::from(sec, subsec, scale)
102    }
103
104    #[inline]
105    pub const fn from_min(m: i64, scale: Scale) -> Self {
106        Self::from(m * 60, 0, scale)
107    }
108
109    #[inline]
110    pub const fn from_hr(h: i64, scale: Scale) -> Self {
111        Self::from(h * 3600, 0, scale)
112    }
113
114    /// Creates a `Dt` from hours, minutes, seconds, milliseconds, microseconds,
115    /// and nanoseconds on the supplied scale.
116    pub const fn from_hms(
117        hr: i64,
118        min: i64,
119        sec: i64,
120        ms: i128,
121        us: i128,
122        ns: i128,
123        scale: Scale,
124    ) -> Self {
125        let total_sec = hr * 3600i64 + min * 60i64 + sec;
126
127        let sub_ns = ms * 1_000_000i128 + us * 1_000i128 + ns;
128
129        if sub_ns == 0 {
130            return Self::from(total_sec, 0, scale);
131        }
132
133        let abs_ns = sub_ns.unsigned_abs();
134        let extra_sec = (abs_ns / 1_000_000_000u128) as i64;
135        let rem_ns = abs_ns % 1_000_000_000u128;
136        let frac = (rem_ns as u64) * ATTOS_PER_NS;
137
138        let (final_sec, final_frac) = if sub_ns >= 0 {
139            (total_sec + extra_sec, frac)
140        } else if frac == 0 {
141            (total_sec - extra_sec, 0)
142        } else {
143            (total_sec - extra_sec - 1, ATTOS_PER_SEC - frac)
144        };
145
146        Self::from(final_sec, final_frac, scale)
147    }
148
149    /// Returns the current system time as TAI from 2000-01-01 noon.
150    ///
151    /// This method is only available when the `std` feature is enabled and the target
152    /// is not WASM with the `js` feature.
153    #[cfg(all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))))]
154    #[inline]
155    pub fn now() -> Self {
156        use crate::TSpan;
157
158        let now = std::time::SystemTime::now();
159        let (secs, nanos) = match now.duration_since(std::time::UNIX_EPOCH) {
160            Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos() as i64),
161            Err(_) => {
162                // System time is before Unix epoch — support negative time
163                let dur = std::time::SystemTime::UNIX_EPOCH
164                    .duration_since(now)
165                    .unwrap();
166                (-(dur.as_secs() as i64), -(dur.subsec_nanos() as i64))
167            }
168        };
169        crate::Dt::from_epoch(TSpan::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
170            .add(crate::TSpan::from_ns(nanos as i128))
171    }
172
173    /// Returns the current system time as TAI from 2000-01-01 noon.
174    /// (browser WASM version using JavaScript’s `Date.now()`).
175    #[cfg(all(target_arch = "wasm32", feature = "js"))]
176    #[inline]
177    pub fn now() -> Self {
178        let millis = js_sys::Date::now() as i64;
179        let secs = millis / 1000;
180        let nanos = (millis % 1000) * 1_000_000;
181        crate::Dt::from_epoch(TSpan::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
182            .add(crate::TSpan::from_ns(nanos))
183    }
184}