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