Skip to main content

deep_time/t_span/
constructors.rs

1use crate::{
2    ATTOS_PER_FS, ATTOS_PER_MS, ATTOS_PER_NS, ATTOS_PER_PS, ATTOS_PER_SEC, ATTOS_PER_SEC_I128,
3    ATTOS_PER_SECF, ATTOS_PER_US, Dt, Real, SEC_PER_DAYI64, SEC_PER_WEEK, Scale, TSpan, floor_f,
4};
5
6impl TSpan {
7    /// Zero duration (`0 s`).
8    pub const ZERO: Self = Self { sec: 0, attos: 0 };
9
10    /// Maximum representable duration (`i64::MAX` seconds + 999... attoseconds).
11    pub const MAX: Self = Self {
12        sec: i64::MAX,
13        attos: ATTOS_PER_SEC - 1,
14    };
15
16    /// Minimum (most negative) representable duration (`i64::MIN` seconds).
17    pub const MIN: Self = Self {
18        sec: i64::MIN,
19        attos: 0,
20    };
21
22    pub const SEC_19: Self = Self::from_sec(19);
23    pub const SEC_33: Self = Self::from_sec(33);
24    pub const SEC_37: Self = Self::from_sec(37);
25    pub const ONE_DAY: Self = Self::from_days(1);
26
27    /// Reconstruct `TSpan` from total attoseconds (exact, handles negative values correctly).
28    pub const fn from_attos(mut attos: i128) -> Self {
29        if attos > (i64::MAX as i128) * ATTOS_PER_SEC_I128 {
30            return Self::MAX;
31        }
32        if attos < (i64::MIN as i128) * ATTOS_PER_SEC_I128 {
33            return Self::MIN;
34        }
35
36        if attos >= 0 {
37            let sec = (attos / ATTOS_PER_SEC_I128) as i64;
38            let attos = (attos % ATTOS_PER_SEC_I128) as u64;
39            Self { sec, attos }
40        } else {
41            attos = -attos;
42            let sec_pos = (attos / ATTOS_PER_SEC_I128) as i64;
43            let rem = (attos % ATTOS_PER_SEC_I128) as u64;
44            if rem == 0 {
45                Self {
46                    sec: -sec_pos,
47                    attos: 0,
48                }
49            } else {
50                Self {
51                    sec: -sec_pos - 1,
52                    attos: ATTOS_PER_SEC - rem,
53                }
54            }
55        }
56    }
57
58    /// Creates a new `TSpan` from whole seconds and a subsecond part.
59    ///
60    /// The result is automatically normalized so `attos` lies in `[0, 10¹⁸)`.
61    #[inline]
62    pub const fn new(sec: i64, attos: u64) -> Self {
63        let mut dt = Self { sec, attos };
64        dt.carry_over();
65        dt
66    }
67
68    /// Creates a `TSpan` representing `s` seconds.
69    #[inline]
70    pub const fn from_sec(s: i64) -> Self {
71        Self::new(s, 0)
72    }
73
74    /// Creates a `TSpan` representing `ms` milliseconds.
75    #[inline]
76    pub const fn from_ms(ms: i128) -> Self {
77        Self::from_attos(ms.saturating_mul(ATTOS_PER_MS as i128))
78    }
79
80    /// Creates a `TSpan` representing `us` microseconds.
81    #[inline]
82    pub const fn from_us(us: i128) -> Self {
83        Self::from_attos(us.saturating_mul(ATTOS_PER_US as i128))
84    }
85
86    /// Creates a `TSpan` representing `ns` nanoseconds.
87    #[inline]
88    pub const fn from_ns(ns: i128) -> Self {
89        Self::from_attos(ns.saturating_mul(ATTOS_PER_NS as i128))
90    }
91
92    /// Creates a `TSpan` representing `ps` picoseconds.
93    #[inline]
94    pub const fn from_ps(ps: i128) -> Self {
95        Self::from_attos(ps.saturating_mul(ATTOS_PER_PS as i128))
96    }
97
98    /// Creates a `TSpan` representing `fs` femtoseconds.
99    #[inline]
100    pub const fn from_fs(fs: i128) -> Self {
101        Self::from_attos(fs.saturating_mul(ATTOS_PER_FS as i128))
102    }
103
104    /// Creates a `TSpan` representing `m` minutes.
105    #[inline]
106    pub const fn from_min(m: i64) -> Self {
107        Self::from_sec(m * 60)
108    }
109
110    /// Creates a `TSpan` representing `h` hours.
111    #[inline]
112    pub const fn from_hr(h: i64) -> Self {
113        Self::from_sec(h * 3600)
114    }
115
116    #[inline]
117    pub const fn from_days(d: i64) -> TSpan {
118        Self::from_sec(d.saturating_mul(SEC_PER_DAYI64))
119    }
120
121    #[inline]
122    pub const fn wk(wk: i64) -> TSpan {
123        TSpan::from_sec(wk.saturating_mul(SEC_PER_WEEK))
124    }
125
126    #[inline]
127    pub const fn yr(yr: i64) -> TSpan {
128        TSpan::from_sec(yr.saturating_mul(31_557_600))
129    }
130
131    /// Returns a `Dt` that is this duration ago from the given scale.
132    #[inline]
133    pub const fn ago(self, scale: Scale) -> Dt {
134        Dt::from(0, 0, scale).sub(self)
135    }
136
137    /// Returns a `Dt` that is this duration from now in the given scale.
138    #[inline]
139    pub const fn from_now(self, scale: Scale) -> Dt {
140        Dt::from(0, 0, scale).add(self)
141    }
142
143    /// Creates a `TSpan` from hours, minutes, seconds, milliseconds,
144    /// microseconds, and nanoseconds.
145    #[inline]
146    pub const fn from_hms(hr: i64, min: i64, sec: i64, ms: i64, us: i64, ns: i64) -> Self {
147        let total_secs = hr * 3600i64 + min * 60i64 + sec;
148
149        let sub_ns = ms * 1_000_000i64 + us * 1_000i64 + ns;
150
151        if sub_ns == 0 {
152            return Self::new(total_secs, 0);
153        }
154
155        let abs_ns = sub_ns.unsigned_abs();
156        let extra_secs = (abs_ns / 1_000_000_000u64) as i64;
157        let rem_ns = abs_ns % 1_000_000_000u64;
158        let frac = rem_ns * ATTOS_PER_NS;
159
160        let (final_secs, final_frac) = if sub_ns >= 0 {
161            (total_secs + extra_secs, frac)
162        } else if frac == 0 {
163            (total_secs - extra_secs, 0)
164        } else {
165            (total_secs - extra_secs - 1, ATTOS_PER_SEC - frac)
166        };
167
168        Self::new(final_secs, final_frac)
169    }
170
171    /// Returns the negation of this duration.
172    #[inline]
173    pub const fn neg(self) -> Self {
174        if self.attos == 0 {
175            Self {
176                sec: -self.sec,
177                attos: 0,
178            }
179        } else {
180            Self {
181                sec: -self.sec - 1,
182                attos: ATTOS_PER_SEC - self.attos,
183            }
184        }
185    }
186
187    /// Creates a `TSpan` from a floating-point number of seconds.
188    pub const fn from_sec_f(sec_f: Real) -> Self {
189        if sec_f.is_nan() {
190            return Self::ZERO;
191        }
192        if sec_f.is_infinite() {
193            return if sec_f.is_sign_positive() {
194                Self::MAX
195            } else {
196                Self::MIN
197            };
198        }
199
200        let floor_val = floor_f(sec_f);
201        let frac = sec_f - floor_val;
202        let attos_frac = (frac * ATTOS_PER_SECF) as i128;
203
204        let total = (floor_val as i128) * ATTOS_PER_SEC_I128 + attos_frac;
205        Self::from_attos(total)
206    }
207}