Skip to main content

deep_time/dt/
helpers.rs

1use crate::{
2    ATTOS_PER_DAY, ATTOS_PER_FS_I128, ATTOS_PER_HOUR, ATTOS_PER_MIN, ATTOS_PER_MS_I128,
3    ATTOS_PER_NS_I128, ATTOS_PER_PS_I128, ATTOS_PER_SEC_I128, ATTOS_PER_SECF, ATTOS_PER_US_I128,
4    ATTOS_PER_WEEK, Dt, Real, SEC_PER_DAY_F,
5};
6
7impl Dt {
8    /// Clamps `value` to the range `[min, max]`.
9    ///
10    /// This is a `const fn`, so it can be used in const contexts
11    /// (e.g. const generics, statics, const evaluation, etc.).
12    ///
13    /// If `min > max`, the result is equivalent to clamping to `[max, min]`.
14    pub const fn clamp_u8(value: u8, min: u8, max: u8) -> u8 {
15        if value < min {
16            min
17        } else if value > max {
18            max
19        } else {
20            value
21        }
22    }
23
24    /// Clamps `value` to the range `[min, max]`.
25    ///
26    /// This is a `const fn`, so it can be used in const contexts
27    /// (e.g. const generics, statics, const evaluation, etc.).
28    ///
29    /// If `min > max`, the result is equivalent to clamping to `[max, min]`.
30    pub const fn clamp_u64(value: u64, min: u64, max: u64) -> u64 {
31        if value < min {
32            min
33        } else if value > max {
34            max
35        } else {
36            value
37        }
38    }
39
40    /// Clamps an `i128` to the representable range of `i64`.
41    pub const fn to_i64(n: i128) -> i64 {
42        let y = n as i64;
43        if n == y as i128 {
44            y
45        } else if n > 0 {
46            i64::MAX
47        } else {
48            i64::MIN
49        }
50    }
51
52    /// Clamps an `i128` to the representable range of `u64`.
53    pub const fn to_u64(n: i128) -> u64 {
54        if n > u64::MAX as i128 {
55            u64::MAX
56        } else if n < u64::MIN as i128 {
57            u64::MIN
58        } else {
59            n as u64
60        }
61    }
62
63    /// Converts a `u128` to `i128`, saturating at [`i128::MAX`] when the value would wrap.
64    ///
65    /// A bare `x as i128` is wrapping: values above `i128::MAX` become negative
66    /// (e.g. `u128::MAX as i128 == -1`). Prefer this helper whenever a non-negative
67    /// attosecond (or other) magnitude stored as `u128` is fed into signed `i128` arithmetic.
68    pub const fn to_i128(n: u128) -> i128 {
69        if n > i128::MAX as u128 {
70            i128::MAX
71        } else {
72            n as i128
73        }
74    }
75
76    /// Combines a whole unit count and an **attoseconds** remainder
77    /// into total attoseconds.
78    ///
79    /// Used by constructors such as
80    /// [`from_ms`](../struct.Dt.html#method.from_ms).
81    #[inline(always)]
82    pub const fn unit_to_total_attos(whole: i128, attos: i128, unit_attos: i128) -> i128 {
83        whole.saturating_mul(unit_attos).saturating_add(attos)
84    }
85
86    /// Converts whole femtoseconds → total attoseconds.
87    #[inline(always)]
88    pub const fn fs_to_attos(fs: i128) -> i128 {
89        fs.saturating_mul(ATTOS_PER_FS_I128)
90    }
91
92    /// Converts whole picoseconds → total attoseconds.
93    #[inline(always)]
94    pub const fn ps_to_attos(ps: i128) -> i128 {
95        ps.saturating_mul(ATTOS_PER_PS_I128)
96    }
97
98    /// Converts whole nanoseconds → total attoseconds.
99    #[inline(always)]
100    pub const fn ns_to_attos(ns: i128) -> i128 {
101        ns.saturating_mul(ATTOS_PER_NS_I128)
102    }
103
104    /// Converts whole microseconds → total attoseconds.
105    #[inline(always)]
106    pub const fn us_to_attos(us: i128) -> i128 {
107        us.saturating_mul(ATTOS_PER_US_I128)
108    }
109
110    /// Converts whole milliseconds → total attoseconds.
111    #[inline(always)]
112    pub const fn ms_to_attos(ms: i128) -> i128 {
113        ms.saturating_mul(ATTOS_PER_MS_I128)
114    }
115
116    /// Converts whole seconds → total attoseconds.
117    #[inline(always)]
118    pub const fn sec_to_attos(sec: i128) -> i128 {
119        sec.saturating_mul(ATTOS_PER_SEC_I128)
120    }
121
122    /// Converts whole minutes → total attoseconds.
123    #[inline(always)]
124    pub const fn mins_to_attos(mins: i128) -> i128 {
125        mins.saturating_mul(ATTOS_PER_MIN)
126    }
127
128    /// Converts whole hours → total attoseconds.
129    #[inline(always)]
130    pub const fn hours_to_attos(hours: i128) -> i128 {
131        hours.saturating_mul(ATTOS_PER_HOUR)
132    }
133
134    /// Converts whole days → total attoseconds.
135    #[inline(always)]
136    pub const fn days_to_attos(days: i128) -> i128 {
137        days.saturating_mul(ATTOS_PER_DAY)
138    }
139
140    /// Converts a floating-point day count → total attoseconds.
141    ///
142    /// Uses the same high-precision path as
143    /// [`sec_f_to_attos`](../struct.Dt.html#method.sec_f_to_attos)
144    /// (`days × 86_400` seconds).
145    #[inline(always)]
146    pub const fn days_f_to_attos(days: Real) -> i128 {
147        Self::sec_f_to_attos(days * SEC_PER_DAY_F)
148    }
149
150    /// Converts whole weeks → total attoseconds.
151    #[inline(always)]
152    pub const fn weeks_to_attos(weeks: i128) -> i128 {
153        weeks.saturating_mul(ATTOS_PER_WEEK)
154    }
155
156    /// Converts a floating-point week count → total attoseconds.
157    ///
158    /// Uses the same path as
159    /// [`sec_f_to_attos`](../struct.Dt.html#method.sec_f_to_attos)
160    /// (`weeks × 604_800` seconds).
161    #[inline(always)]
162    pub const fn weeks_f_to_attos(weeks: Real) -> i128 {
163        Self::sec_f_to_attos(weeks * SEC_PER_DAY_F * 7.0)
164    }
165
166    /// Converts total attoseconds → whole femtoseconds.
167    #[inline(always)]
168    pub const fn attos_to_fs(attos: i128) -> i128 {
169        attos / ATTOS_PER_FS_I128
170    }
171
172    /// Converts total attoseconds → whole picoseconds.
173    #[inline(always)]
174    pub const fn attos_to_ps(attos: i128) -> i128 {
175        attos / ATTOS_PER_PS_I128
176    }
177
178    /// Converts total attoseconds → whole nanoseconds.
179    #[inline(always)]
180    pub const fn attos_to_ns(attos: i128) -> i128 {
181        attos / ATTOS_PER_NS_I128
182    }
183
184    /// Converts total attoseconds → whole microseconds.
185    #[inline(always)]
186    pub const fn attos_to_us(attos: i128) -> i128 {
187        attos / ATTOS_PER_US_I128
188    }
189
190    /// Converts total attoseconds → whole milliseconds.
191    #[inline(always)]
192    pub const fn attos_to_ms(attos: i128) -> i128 {
193        attos / ATTOS_PER_MS_I128
194    }
195
196    /// Converts total attoseconds → whole seconds.
197    #[inline(always)]
198    pub const fn attos_to_sec(attos: i128) -> i128 {
199        attos / ATTOS_PER_SEC_I128
200    }
201
202    /// Converts total attoseconds → whole seconds as i64, clamped to [`i64::MIN`]/[`i64::MAX`].
203    #[inline(always)]
204    pub const fn attos_to_sec_i64(attos: i128) -> i64 {
205        Self::to_i64(Self::attos_to_sec(attos))
206    }
207
208    /// **Lossy** conversion of i128 attoseconds to → float seconds (s).
209    #[inline(always)]
210    pub const fn attos_to_sec_f(attos: i128) -> Real {
211        f!(attos) / ATTOS_PER_SECF
212    }
213
214    /// Converts total attoseconds → whole minutes.
215    #[inline(always)]
216    pub const fn attos_to_mins(attos: i128) -> i128 {
217        attos / ATTOS_PER_MIN
218    }
219
220    /// Converts total attoseconds → whole hours.
221    #[inline(always)]
222    pub const fn attos_to_hours(attos: i128) -> i128 {
223        attos / ATTOS_PER_HOUR
224    }
225
226    /// Converts total attoseconds → whole days.
227    #[inline(always)]
228    pub const fn attos_to_days(attos: i128) -> i128 {
229        attos / ATTOS_PER_DAY
230    }
231
232    /// **Lossy** conversion of total attoseconds → floating-point days.
233    #[inline(always)]
234    pub const fn attos_to_days_f(attos: i128) -> Real {
235        Self::attos_to_sec_f(attos) / SEC_PER_DAY_F
236    }
237
238    /// Converts total attoseconds → whole weeks.
239    #[inline(always)]
240    pub const fn attos_to_weeks(attos: i128) -> i128 {
241        attos / ATTOS_PER_WEEK
242    }
243
244    /// **Lossy** conversion of total attoseconds → floating-point weeks.
245    #[inline(always)]
246    pub const fn attos_to_weeks_f(attos: i128) -> Real {
247        Self::attos_to_sec_f(attos) / (SEC_PER_DAY_F * 7.0)
248    }
249}