Skip to main content

deep_time/dt/
time_units.rs

1//! Ergonomic time-unit constructors (optional import).
2//!
3//! ```
4//! use deep_time::{Scale, TimeUnits};
5//!
6//! let span = 5.sec() + 250.ms() + 123_456.ns();
7//! let stamp = 3.days().ago(Scale::UTC);
8//! ```
9
10use crate::{
11    ATTOS_PER_FS_I128, ATTOS_PER_MS_I128, ATTOS_PER_NS_I128, ATTOS_PER_PS_I128, ATTOS_PER_SEC_I128,
12    ATTOS_PER_SECF, ATTOS_PER_US_I128, Dt, SEC_PER_DAY, SEC_PER_DAY_F, SEC_PER_DAYI64, Scale,
13};
14
15pub trait AttosUnits: Copy + Sized {
16    /// attoseconds → seconds (s)
17    fn to_sec(self) -> i64;
18
19    /// attoseconds → milliseconds (ms)
20    fn to_ms(self) -> i128;
21
22    /// attoseconds → microseconds (us)
23    fn to_us(self) -> i128;
24
25    /// attoseconds → nanoseconds (ns)
26    fn to_ns(self) -> i128;
27
28    /// attoseconds → picoseconds (ps)
29    fn to_ps(self) -> i128;
30
31    /// attoseconds → femtoseconds (fs)
32    fn to_fs(self) -> i128;
33
34    /// attoseconds → float seconds (s)
35    fn to_sec_f(self) -> f64;
36}
37
38impl AttosUnits for i128 {
39    #[inline(always)]
40    fn to_sec_f(self) -> f64 {
41        self as f64 / ATTOS_PER_SECF
42    }
43
44    #[inline(always)]
45    fn to_sec(self) -> i64 {
46        (self / ATTOS_PER_SEC_I128) as i64
47    }
48
49    #[inline(always)]
50    fn to_ms(self) -> i128 {
51        self / ATTOS_PER_MS_I128
52    }
53
54    #[inline(always)]
55    fn to_us(self) -> i128 {
56        self / ATTOS_PER_US_I128
57    }
58
59    #[inline(always)]
60    fn to_ns(self) -> i128 {
61        self / ATTOS_PER_NS_I128
62    }
63
64    #[inline(always)]
65    fn to_ps(self) -> i128 {
66        self / ATTOS_PER_PS_I128
67    }
68
69    #[inline(always)]
70    fn to_fs(self) -> i128 {
71        self / ATTOS_PER_FS_I128
72    }
73}
74
75/// Trait that adds ergonomic time-unit methods to integers and floats.
76///
77/// Import it explicitly to create `Dt`s directly from rust ints and floats:
78/// `use deep_time::TimeUnits;`
79pub trait TimeUnits: Copy + Sized {
80    // ── Dt constructors ─────────────────────────────────────
81    fn ns(self) -> Dt;
82    fn us(self) -> Dt;
83    fn ms(self) -> Dt;
84    fn sec(self) -> Dt;
85    fn min(self) -> Dt;
86    fn hr(self) -> Dt;
87    fn days(self) -> Dt; // 86400 s (civil day, not leap-second aware)
88    fn wk(self) -> Dt;
89    fn yr(self) -> Dt; // 365.25 days (standard approximation)
90
91    // ── Dt constructors (anchored at "now" in the chosen scale) ──
92    fn ago(self, scale: Scale) -> Dt;
93    fn from_now(self, scale: Scale) -> Dt;
94}
95
96// Integer implementations (all common signed/unsigned types)
97macro_rules! impl_time_units_int {
98    ($($ty:ty),* $(,)?) => {
99        $(
100            impl TimeUnits for $ty {
101                #[inline]
102                fn ns(self) -> Dt { Dt::from_ns(self as i128, Scale::TAI) }
103
104                #[inline]
105                fn us(self) -> Dt { Dt::from_us(self as i128, Scale::TAI) }
106
107                #[inline]
108                fn ms(self) -> Dt { Dt::from_ms(self as i128, Scale::TAI) }
109
110                #[inline]
111                fn sec(self) -> Dt { Dt::from_sec(self as i64, Scale::TAI) }
112
113                #[inline]
114                fn min(self) -> Dt { Dt::from_min(self as i64, Scale::TAI) }
115
116                #[inline]
117                fn hr(self) -> Dt { Dt::from_hr(self as i64, Scale::TAI) }
118
119                #[inline]
120                fn days(self) -> Dt { Dt::from_sec((self as i64).saturating_mul(SEC_PER_DAYI64), Scale::TAI) }
121
122                #[inline]
123                fn wk(self) -> Dt { Dt::from_sec((self as i64).saturating_mul(604_800), Scale::TAI) }
124
125                #[inline]
126                fn yr(self) -> Dt { Dt::from_sec((self as i64).saturating_mul(31_557_600), Scale::TAI) }
127
128                #[inline]
129                fn ago(self, scale: Scale) -> Dt {
130                    Dt::from(0, 0, scale).sub(self.sec())
131                }
132
133                #[inline]
134                fn from_now(self, scale: Scale) -> Dt {
135                    Dt::from(0, 0, scale).add(self.sec())
136                }
137            }
138        )*
139    };
140}
141
142impl_time_units_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
143
144// f64 support (most useful for fractional units)
145impl TimeUnits for f64 {
146    #[inline]
147    fn ns(self) -> Dt {
148        Dt::from_ns(self as i128, Scale::TAI)
149    }
150
151    #[inline]
152    fn us(self) -> Dt {
153        Dt::from_us(self as i128, Scale::TAI)
154    }
155
156    #[inline]
157    fn ms(self) -> Dt {
158        Dt::from_ms(self as i128, Scale::TAI)
159    }
160
161    #[inline]
162    fn sec(self) -> Dt {
163        Dt::from_sec(self as i64, Scale::TAI)
164    }
165
166    #[inline]
167    fn min(self) -> Dt {
168        (self * 60.0).sec()
169    }
170
171    #[inline]
172    fn hr(self) -> Dt {
173        (self * 3600.0).sec()
174    }
175
176    #[inline]
177    fn days(self) -> Dt {
178        (self * SEC_PER_DAY_F).sec()
179    }
180
181    #[inline]
182    fn wk(self) -> Dt {
183        (self * 604_800.0).sec()
184    }
185
186    #[inline]
187    fn yr(self) -> Dt {
188        (self * 31_557_600.0).sec()
189    }
190
191    #[inline]
192    fn ago(self, scale: Scale) -> Dt {
193        Dt::from(0, 0, scale).sub(self.sec())
194    }
195
196    #[inline]
197    fn from_now(self, scale: Scale) -> Dt {
198        Dt::from(0, 0, scale).add(self.sec())
199    }
200}
201
202impl TimeUnits for f32 {
203    #[inline]
204    fn ns(self) -> Dt {
205        Dt::from_ns(self as i128, Scale::TAI)
206    }
207
208    #[inline]
209    fn us(self) -> Dt {
210        Dt::from_us(self as i128, Scale::TAI)
211    }
212
213    #[inline]
214    fn ms(self) -> Dt {
215        Dt::from_ms(self as i128, Scale::TAI)
216    }
217
218    #[inline]
219    fn sec(self) -> Dt {
220        Dt::from_sec(self as i64, Scale::TAI)
221    }
222
223    #[inline]
224    fn min(self) -> Dt {
225        (self * 60.0f32).sec()
226    }
227
228    #[inline]
229    fn hr(self) -> Dt {
230        (self * 3600.0f32).sec()
231    }
232
233    #[inline]
234    fn days(self) -> Dt {
235        (self * SEC_PER_DAY as f32).sec()
236    }
237
238    #[inline]
239    fn wk(self) -> Dt {
240        (self * 604_800.0f32).sec()
241    }
242
243    #[inline]
244    fn yr(self) -> Dt {
245        (self * 31_557_600.0f32).sec()
246    }
247
248    #[inline]
249    fn ago(self, scale: Scale) -> Dt {
250        Dt::from(0, 0, scale).sub(self.sec())
251    }
252
253    #[inline]
254    fn from_now(self, scale: Scale) -> Dt {
255        Dt::from(0, 0, scale).add(self.sec())
256    }
257}