Skip to main content

deep_time/dt/
numbers_traits.rs

1//! Ergonomic time-unit constructors (optional import).
2//!
3//! ```
4//! use deep_time::{Scale, TimeTraits};
5//!
6//! let span = 5.sec() + 250.ms() + 123_456.ns();
7//! let stamp = 3.days().before_zero(Scale::UTC);
8//!
9//! // Wall-clock relative (requires the `std` feature):
10//! // let past = 3.days().ago();
11//! // let future = 3.days().from_now();
12//! ```
13
14use crate::{
15    ATTOS_PER_FS_I128, ATTOS_PER_MS_I128, ATTOS_PER_NS_I128, ATTOS_PER_PS_I128, ATTOS_PER_SEC_I128,
16    ATTOS_PER_SECF, ATTOS_PER_US_I128, Dt, SEC_PER_DAY, SEC_PER_DAY_F, Scale,
17};
18
19/// Trait that adds ergonomic attosecond conversions on integer values.
20///
21/// Covers both directions:
22/// - `attos_to_*` — total attoseconds → whole units (truncating division)
23/// - `*_to_attos` — whole units → total attoseconds (saturating multiply)
24///
25/// ## Examples
26///
27/// ```rust
28/// use deep_time::AttosTraits;
29///
30/// let attos: i128 = -5_600_000_000_000_000_000;
31/// let seconds = attos.attos_to_sec();
32/// assert_eq!(seconds, -5);
33///
34/// assert_eq!(5_i128.ns_to_attos(), 5_000_000_000);
35/// assert_eq!(1_i128.ms_to_attos().attos_to_ms(), 1);
36/// ```
37pub trait AttosTraits: Copy + Sized {
38    /// attoseconds → seconds (s)
39    fn attos_to_sec(self) -> i128;
40
41    /// attoseconds → milliseconds (ms)
42    fn attos_to_ms(self) -> i128;
43
44    /// attoseconds → microseconds (us)
45    fn attos_to_us(self) -> i128;
46
47    /// attoseconds → nanoseconds (ns)
48    fn attos_to_ns(self) -> i128;
49
50    /// attoseconds → picoseconds (ps)
51    fn attos_to_ps(self) -> i128;
52
53    /// attoseconds → femtoseconds (fs)
54    fn attos_to_fs(self) -> i128;
55
56    /// attoseconds → float seconds (s)
57    fn attos_to_sec_f(self) -> f64;
58
59    /// femtoseconds → attoseconds (`× 10³`)
60    fn fs_to_attos(self) -> i128;
61
62    /// picoseconds → attoseconds (`× 10⁶`)
63    fn ps_to_attos(self) -> i128;
64
65    /// nanoseconds → attoseconds (`× 10⁹`)
66    fn ns_to_attos(self) -> i128;
67
68    /// microseconds → attoseconds (`× 10¹²`)
69    fn us_to_attos(self) -> i128;
70
71    /// milliseconds → attoseconds (`× 10¹⁵`)
72    fn ms_to_attos(self) -> i128;
73
74    /// seconds → attoseconds (`× 10¹⁸`)
75    fn sec_to_attos(self) -> i128;
76
77    /// minutes → attoseconds (`× 60 × 10¹⁸`)
78    fn mins_to_attos(self) -> i128;
79
80    /// hours → attoseconds (`× 3600 × 10¹⁸`)
81    fn hours_to_attos(self) -> i128;
82}
83
84impl AttosTraits for i128 {
85    #[inline]
86    fn attos_to_sec_f(self) -> f64 {
87        self as f64 / ATTOS_PER_SECF
88    }
89
90    #[inline]
91    fn attos_to_sec(self) -> i128 {
92        self / ATTOS_PER_SEC_I128
93    }
94
95    #[inline]
96    fn attos_to_ms(self) -> i128 {
97        self / ATTOS_PER_MS_I128
98    }
99
100    #[inline]
101    fn attos_to_us(self) -> i128 {
102        self / ATTOS_PER_US_I128
103    }
104
105    #[inline]
106    fn attos_to_ns(self) -> i128 {
107        self / ATTOS_PER_NS_I128
108    }
109
110    #[inline]
111    fn attos_to_ps(self) -> i128 {
112        self / ATTOS_PER_PS_I128
113    }
114
115    #[inline]
116    fn attos_to_fs(self) -> i128 {
117        self / ATTOS_PER_FS_I128
118    }
119
120    #[inline]
121    fn fs_to_attos(self) -> i128 {
122        Dt::fs_to_attos(self)
123    }
124
125    #[inline]
126    fn ps_to_attos(self) -> i128 {
127        Dt::ps_to_attos(self)
128    }
129
130    #[inline]
131    fn ns_to_attos(self) -> i128 {
132        Dt::ns_to_attos(self)
133    }
134
135    #[inline]
136    fn us_to_attos(self) -> i128 {
137        Dt::us_to_attos(self)
138    }
139
140    #[inline]
141    fn ms_to_attos(self) -> i128 {
142        Dt::ms_to_attos(self)
143    }
144
145    #[inline]
146    fn sec_to_attos(self) -> i128 {
147        Dt::sec_to_attos(self)
148    }
149
150    #[inline]
151    fn mins_to_attos(self) -> i128 {
152        Dt::mins_to_attos(self)
153    }
154
155    #[inline]
156    fn hours_to_attos(self) -> i128 {
157        Dt::hours_to_attos(self)
158    }
159}
160
161/// Trait that adds ergonomic time-unit methods to integers and floats.
162///
163/// ## Examples
164///
165/// ```rust
166/// use deep_time::TimeTraits;
167///
168/// let dt = 5.days();
169/// ```
170pub trait TimeTraits: Copy + Sized {
171    fn ns(self) -> Dt;
172    fn us(self) -> Dt;
173    fn ms(self) -> Dt;
174    fn sec(self) -> Dt;
175    fn mins(self) -> Dt;
176    fn hours(self) -> Dt;
177    fn days(self) -> Dt; // 86400 s (civil day, not leap-second aware)
178    fn weeks(self) -> Dt;
179    fn years(self) -> Dt; // 365.25 days (standard approximation)
180}
181
182// Integer implementations (all common signed/unsigned types)
183macro_rules! impl_time_units_int {
184    ($($ty:ty),* $(,)?) => {
185        $(
186            impl TimeTraits for $ty {
187                #[inline]
188                fn ns(self) -> Dt { Dt::from_ns(self as i128, 0, Scale::TAI, Scale::TAI) }
189
190                #[inline]
191                fn us(self) -> Dt { Dt::from_us(self as i128, 0, Scale::TAI, Scale::TAI) }
192
193                #[inline]
194                fn ms(self) -> Dt { Dt::from_ms(self as i128, 0, Scale::TAI, Scale::TAI) }
195
196                #[inline]
197                fn sec(self) -> Dt { Dt::from_sec(self as i128, Scale::TAI, Scale::TAI) }
198
199                #[inline]
200                fn mins(self) -> Dt { Dt::from_mins(self as i128, 0, Scale::TAI, Scale::TAI) }
201
202                #[inline]
203                fn hours(self) -> Dt { Dt::from_hours(self as i128, 0, Scale::TAI, Scale::TAI) }
204
205                #[inline]
206                fn days(self) -> Dt { Dt::from_sec((self as i128).saturating_mul(SEC_PER_DAY), Scale::TAI, Scale::TAI) }
207
208                #[inline]
209                fn weeks(self) -> Dt { Dt::from_sec((self  as i128).saturating_mul(604_800), Scale::TAI, Scale::TAI) }
210
211                #[inline]
212                fn years(self) -> Dt { Dt::from_sec((self  as i128).saturating_mul(31_557_600), Scale::TAI, Scale::TAI) }
213            }
214        )*
215    };
216}
217
218impl_time_units_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64);
219
220// `u128` alone among the integer impls can exceed `i128::MAX`; saturate instead of wrapping.
221impl TimeTraits for u128 {
222    #[inline]
223    fn ns(self) -> Dt {
224        Dt::from_ns(Dt::to_i128(self), 0, Scale::TAI, Scale::TAI)
225    }
226
227    #[inline]
228    fn us(self) -> Dt {
229        Dt::from_us(Dt::to_i128(self), 0, Scale::TAI, Scale::TAI)
230    }
231
232    #[inline]
233    fn ms(self) -> Dt {
234        Dt::from_ms(Dt::to_i128(self), 0, Scale::TAI, Scale::TAI)
235    }
236
237    #[inline]
238    fn sec(self) -> Dt {
239        Dt::from_sec(Dt::to_i128(self), Scale::TAI, Scale::TAI)
240    }
241
242    #[inline]
243    fn mins(self) -> Dt {
244        Dt::from_mins(Dt::to_i128(self), 0, Scale::TAI, Scale::TAI)
245    }
246
247    #[inline]
248    fn hours(self) -> Dt {
249        Dt::from_hours(Dt::to_i128(self), 0, Scale::TAI, Scale::TAI)
250    }
251
252    #[inline]
253    fn days(self) -> Dt {
254        Dt::from_sec(
255            Dt::to_i128(self).saturating_mul(SEC_PER_DAY),
256            Scale::TAI,
257            Scale::TAI,
258        )
259    }
260
261    #[inline]
262    fn weeks(self) -> Dt {
263        Dt::from_sec(
264            Dt::to_i128(self).saturating_mul(604_800),
265            Scale::TAI,
266            Scale::TAI,
267        )
268    }
269
270    #[inline]
271    fn years(self) -> Dt {
272        Dt::from_sec(
273            Dt::to_i128(self).saturating_mul(31_557_600),
274            Scale::TAI,
275            Scale::TAI,
276        )
277    }
278}
279
280impl TimeTraits for f64 {
281    #[inline]
282    fn ns(self) -> Dt {
283        crate::from_sec_f!(self * 1e-9)
284    }
285
286    #[inline]
287    fn us(self) -> Dt {
288        crate::from_sec_f!(self * 1e-6)
289    }
290
291    #[inline]
292    fn ms(self) -> Dt {
293        crate::from_sec_f!(self * 1e-3)
294    }
295
296    #[inline]
297    fn sec(self) -> Dt {
298        crate::from_sec_f!(self)
299    }
300
301    #[inline]
302    fn mins(self) -> Dt {
303        (self * 60.0).sec()
304    }
305
306    #[inline]
307    fn hours(self) -> Dt {
308        (self * 3600.0).sec()
309    }
310
311    #[inline]
312    fn days(self) -> Dt {
313        (self * SEC_PER_DAY_F).sec()
314    }
315
316    #[inline]
317    fn weeks(self) -> Dt {
318        (self * 604_800.0).sec()
319    }
320
321    #[inline]
322    fn years(self) -> Dt {
323        (self * 31_557_600.0).sec()
324    }
325}
326
327impl TimeTraits for f32 {
328    #[inline]
329    fn ns(self) -> Dt {
330        crate::from_sec_f!(self as f64 * 1e-9)
331    }
332
333    #[inline]
334    fn us(self) -> Dt {
335        crate::from_sec_f!(self as f64 * 1e-6)
336    }
337
338    #[inline]
339    fn ms(self) -> Dt {
340        crate::from_sec_f!(self as f64 * 1e-3)
341    }
342
343    #[inline]
344    fn sec(self) -> Dt {
345        crate::from_sec_f!(self as f64)
346    }
347
348    #[inline]
349    fn mins(self) -> Dt {
350        (self * 60.0f32).sec()
351    }
352
353    #[inline]
354    fn hours(self) -> Dt {
355        (self * 3600.0f32).sec()
356    }
357
358    #[inline]
359    fn days(self) -> Dt {
360        (self * SEC_PER_DAY as f32).sec()
361    }
362
363    #[inline]
364    fn weeks(self) -> Dt {
365        (self * 604_800.0f32).sec()
366    }
367
368    #[inline]
369    fn years(self) -> Dt {
370        (self * 31_557_600.0f32).sec()
371    }
372}