rs_std_ext/
time.rs

1//! [`std::time`] related extensions.
2
3use std::time::Duration;
4
5/// Extension methods for constructing [`std::time::Duration`] with numbers.
6///
7/// ## Note
8///
9/// This trait is not implemented for the `Duration`,
10/// for extension methods operating on a `Duration`,
11/// see [`crate::time::DurationExt`] for more information.
12pub trait DurationNumExt: DurationNumExtFallible {
13    /// Create a `Duration`, using the postfix syntax.
14    ///
15    /// ## Example
16    ///
17    /// ```rust
18    /// use rs_std_ext::time::DurationNumExt;
19    /// use std::time::Duration;
20    ///
21    /// assert_eq!(10u8.seconds(), Duration::from_secs(10u64));
22    /// ```
23    ///
24    /// ## Panics
25    ///
26    /// This function panics if the number is too large or negative.
27    fn seconds(&self) -> Duration {
28        DurationNumExtFallible::seconds(self).unwrap()
29    }
30
31    /// Create a `Duration`, using the postfix syntax.
32    ///
33    /// ## Example
34    ///
35    /// ```rust
36    /// use rs_std_ext::time::DurationNumExt;
37    /// use std::time::Duration;
38    ///
39    /// assert_eq!(10u8.milliseconds(), Duration::from_millis(10u64));
40    /// ```
41    ///
42    /// ## Panics
43    ///
44    /// This function panics if the number is too large or negative.
45    fn milliseconds(&self) -> Duration {
46        DurationNumExtFallible::milliseconds(self).unwrap()
47    }
48
49    /// Create a `Duration`, using the postfix syntax.
50    ///
51    /// ## Example
52    ///
53    /// ```rust
54    /// use rs_std_ext::time::DurationNumExt;
55    /// use std::time::Duration;
56    ///
57    /// assert_eq!(10u8.microseconds(), Duration::from_micros(10u64));
58    /// ```
59    ///
60    /// ## Panics
61    ///
62    /// This function panics if the number is too large or negative.
63    fn microseconds(&self) -> Duration {
64        DurationNumExtFallible::microseconds(self).unwrap()
65    }
66
67    /// Create a `Duration`, using the postfix syntax.
68    ///
69    /// ## Example
70    ///
71    /// ```rust
72    /// use rs_std_ext::time::DurationNumExt;
73    /// use std::time::Duration;
74    ///
75    /// assert_eq!(10u8.nanoseconds(), Duration::from_nanos(10u64));
76    /// ```
77    ///
78    /// ## Panics
79    ///
80    /// This function panics if the number is too large or negative.
81    fn nanoseconds(&self) -> Duration {
82        DurationNumExtFallible::nanoseconds(self).unwrap()
83    }
84
85    /// Create a `Duration`, using the postfix syntax.
86    ///
87    /// ## Example
88    ///
89    /// ```rust
90    /// use rs_std_ext::time::DurationNumExt;
91    /// use std::time::Duration;
92    ///
93    /// assert_eq!(10u128.minutes(), Duration::from_secs(10u64 * 60));
94    /// ```
95    fn minutes(&self) -> Duration {
96        DurationNumExtFallible::minutes(self).unwrap()
97    }
98
99    /// Create a `Duration`, using the postfix syntax.
100    ///
101    /// ## Example
102    ///
103    /// ```rust
104    /// use rs_std_ext::time::DurationNumExt;
105    /// use std::time::Duration;
106    ///
107    /// assert_eq!(10u128.hours(), Duration::from_secs(10u64 * 60 * 60));
108    /// ```
109    fn hours(&self) -> Duration {
110        DurationNumExtFallible::hours(self).unwrap()
111    }
112
113    /// Create a `Duration`, using the postfix syntax.
114    ///
115    /// ## Example
116    ///
117    /// ```rust
118    /// use rs_std_ext::time::DurationNumExt;
119    /// use std::time::Duration;
120    ///
121    /// assert_eq!(10u128.days(), Duration::from_secs(10u64 * 60 * 60 * 24));
122    /// ```
123    fn days(&self) -> Duration {
124        DurationNumExtFallible::days(self).unwrap()
125    }
126}
127
128/// Extension methods for constructing [`std::time::Duration`] with numbers.
129///
130/// ## Note
131///
132/// This trait is not implemented for the `Duration`,
133/// for extension methods operating on a `Duration`,
134/// see [`crate::time::DurationExt`] for more information.
135///
136/// ## Warning
137///
138/// Unlike the `DurationNumExt`, this trait's method returns an
139/// `Option<Duration>`.
140/// Numbers that may be too large to be represented like [`num::BigInt`]
141/// or may be negative like signed integers will implement this trait.
142pub trait DurationNumExtFallible {
143    /// Create a `Duration`, using the postfix syntax.
144    ///
145    /// ## Example
146    ///
147    /// ```rust
148    /// use rs_std_ext::time::DurationNumExtFallible;
149    /// use std::time::Duration;
150    ///
151    /// assert_eq!(10u128.seconds(), Some(Duration::from_secs(10u64)));
152    /// assert!(u128::MAX.seconds().is_none());
153    /// ```
154    fn seconds(&self) -> Option<Duration>;
155
156    /// Create a `Duration`, using the postfix syntax.
157    ///
158    /// ## Example
159    ///
160    /// ```rust
161    /// use rs_std_ext::time::DurationNumExtFallible;
162    /// use std::time::Duration;
163    ///
164    ///
165    /// assert_eq!(10u128.milliseconds(), Some(Duration::from_millis(10u64)));
166    /// assert!(u128::MAX.milliseconds().is_none());
167    /// ```
168    fn milliseconds(&self) -> Option<Duration>;
169
170    /// Create a `Duration`, using the postfix syntax.
171    ///
172    /// ## Example
173    ///
174    /// ```rust
175    /// use rs_std_ext::time::DurationNumExtFallible;
176    /// use std::time::Duration;
177    ///
178    /// assert_eq!(10u128.microseconds(), Some(Duration::from_micros(10u64)));
179    /// assert!(u128::MAX.microseconds().is_none());
180    /// ```
181    fn microseconds(&self) -> Option<Duration>;
182
183    /// Create a `Duration`, using the postfix syntax.
184    ///
185    /// ## Example
186    ///
187    /// ```rust
188    /// use rs_std_ext::time::DurationNumExtFallible;
189    /// use std::time::Duration;
190    ///
191    /// assert_eq!(10u128.nanoseconds(), Some(Duration::from_nanos(10u64)));
192    /// assert!(u128::MAX.nanoseconds().is_none());
193    /// ```
194    fn nanoseconds(&self) -> Option<Duration>;
195
196    /// Create a `Duration`, using the postfix syntax.
197    ///
198    /// ## Example
199    ///
200    /// ```rust
201    /// use rs_std_ext::time::DurationNumExtFallible;
202    /// use std::time::Duration;
203    ///
204    /// assert_eq!(10u128.minutes(), Some(Duration::from_secs(10u64 * 60)));
205    /// ```
206    fn minutes(&self) -> Option<Duration>;
207
208    /// Create a `Duration`, using the postfix syntax.
209    ///
210    /// ## Example
211    ///
212    /// ```rust
213    /// use rs_std_ext::time::DurationNumExtFallible;
214    /// use std::time::Duration;
215    ///
216    /// assert_eq!(10u128.hours(), Some(Duration::from_secs(10u64 * 60 * 60)));
217    /// ```
218    fn hours(&self) -> Option<Duration>;
219
220    /// Create a `Duration`, using the postfix syntax.
221    ///
222    /// ## Example
223    ///
224    /// ```rust
225    /// use rs_std_ext::time::DurationNumExtFallible;
226    /// use std::time::Duration;
227    ///
228    /// assert_eq!(10u128.days(), Some(Duration::from_secs(10u64 * 60 * 60 * 24)));
229    /// ```
230    fn days(&self) -> Option<Duration>;
231}
232
233pub const SECS_PER_MINUTE: u64 = 60;
234pub const SECS_PER_HOUR: u64 = 60 * 60;
235pub const SECS_PER_DAY: u64 = 60 * 60 * 24;
236
237#[cfg(feature = "crate-num")]
238mod __num_impl {
239    use std::time::Duration;
240
241    use num::{BigInt, BigUint, ToPrimitive};
242
243    use super::{extfn, DurationNumExtFallible, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MINUTE};
244
245    impl DurationNumExtFallible for BigUint {
246        #[inline]
247        fn seconds(&self) -> Option<Duration> {
248            self.to_u64().and_then(extfn::checked_from_secs)
249        }
250
251        #[inline]
252        fn microseconds(&self) -> Option<Duration> {
253            self.to_u64().and_then(extfn::checked_from_micros)
254        }
255
256        #[inline]
257        fn milliseconds(&self) -> Option<Duration> {
258            self.to_u64().and_then(extfn::checked_from_millis)
259        }
260
261        #[inline]
262        fn nanoseconds(&self) -> Option<Duration> {
263            self.to_u64().and_then(extfn::checked_from_nanos)
264        }
265
266        #[inline]
267        fn minutes(&self) -> Option<Duration> {
268            self.to_u64()
269                .and_then(|v| v.checked_mul(SECS_PER_MINUTE))
270                .and_then(extfn::checked_from_secs)
271        }
272
273        #[inline]
274        fn hours(&self) -> Option<Duration> {
275            self.to_u64()
276                .and_then(|v| v.checked_mul(SECS_PER_HOUR))
277                .and_then(extfn::checked_from_secs)
278        }
279
280        #[inline]
281        fn days(&self) -> Option<Duration> {
282            self.to_u64()
283                .and_then(|v| v.checked_mul(SECS_PER_DAY))
284                .and_then(extfn::checked_from_secs)
285        }
286    }
287
288    impl DurationNumExtFallible for BigInt {
289        #[inline]
290        fn seconds(&self) -> Option<Duration> {
291            self.to_u64().and_then(extfn::checked_from_secs)
292        }
293
294        #[inline]
295        fn microseconds(&self) -> Option<Duration> {
296            self.to_u64().and_then(extfn::checked_from_micros)
297        }
298
299        #[inline]
300        fn milliseconds(&self) -> Option<Duration> {
301            self.to_u64().and_then(extfn::checked_from_millis)
302        }
303
304        #[inline]
305        fn nanoseconds(&self) -> Option<Duration> {
306            self.to_u64().and_then(extfn::checked_from_nanos)
307        }
308
309        #[inline]
310        fn minutes(&self) -> Option<Duration> {
311            self.to_u64()
312                .and_then(|v| v.checked_mul(SECS_PER_MINUTE))
313                .and_then(extfn::checked_from_secs)
314        }
315
316        #[inline]
317        fn hours(&self) -> Option<Duration> {
318            self.to_u64()
319                .and_then(|v| v.checked_mul(SECS_PER_HOUR))
320                .and_then(extfn::checked_from_secs)
321        }
322
323        #[inline]
324        fn days(&self) -> Option<Duration> {
325            self.to_u64()
326                .and_then(|v| v.checked_mul(SECS_PER_DAY))
327                .and_then(extfn::checked_from_secs)
328        }
329    }
330}
331
332mod __std_impl {
333    use std::time::Duration;
334
335    use super::{
336        extfn, DurationNumExt, DurationNumExtFallible, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MINUTE,
337    };
338
339    macro_rules! __impl_non_fallible {
340        ($($ty:ty),*) => {
341            $(__impl_non_fallible!(@$ty);)*
342        };
343
344        (@$ty:ty) => {
345            impl DurationNumExt for $ty {
346                #[inline]
347                fn seconds(&self) -> Duration {
348                    Duration::from_secs(*self as u64)
349                }
350
351                #[inline]
352                fn microseconds(&self) -> Duration {
353                    Duration::from_micros(*self as u64)
354                }
355
356                #[inline]
357                fn milliseconds(&self) -> Duration {
358                    Duration::from_millis(*self as u64)
359                }
360
361                #[inline]
362                fn nanoseconds(&self) -> Duration {
363                    Duration::from_nanos(*self as u64)
364                }
365            }
366        };
367    }
368
369    __impl_non_fallible!(u8, u16, u32, u64);
370
371    macro_rules! __impl_fallible_int {
372        (with-unwrap $($ty:ty),*) => {
373            $(__impl_fallible_int!(@$ty);)*
374            $(impl DurationNumExt for $ty {})*
375        };
376
377        ($($ty:ty),*) => {
378            $(__impl_fallible_int!(@$ty);)*
379        };
380
381        (@$ty:ty) => {
382            impl DurationNumExtFallible for $ty {
383                #[inline]
384                fn seconds(&self) -> Option<Duration> {
385                    (*self).try_into().ok().and_then(extfn::checked_from_secs)
386                }
387
388                #[inline]
389                fn microseconds(&self) -> Option<Duration> {
390                    (*self).try_into().ok().and_then(extfn::checked_from_micros)
391                }
392
393                #[inline]
394                fn milliseconds(&self) -> Option<Duration> {
395                    (*self).try_into().ok().and_then(extfn::checked_from_millis)
396                }
397
398                #[inline]
399                fn nanoseconds(&self) -> Option<Duration> {
400                    (*self).try_into().ok().and_then(extfn::checked_from_nanos)
401                }
402
403                #[inline]
404                fn minutes(&self) -> Option<Duration> {
405                    (*self)
406                        .try_into()
407                        .ok()
408                        .and_then(|e: u64| e.checked_mul(SECS_PER_MINUTE))
409                        .and_then(extfn::checked_from_secs)
410                }
411
412                #[inline]
413                fn hours(&self) -> Option<Duration> {
414                    (*self)
415                        .try_into()
416                        .ok()
417                        .and_then(|e: u64| e.checked_mul(SECS_PER_HOUR))
418                        .and_then(extfn::checked_from_secs)
419                }
420
421                #[inline]
422                fn days(&self) -> Option<Duration> {
423                    (*self)
424                        .try_into()
425                        .ok()
426                        .and_then(|e: u64| e.checked_mul(SECS_PER_DAY))
427                        .and_then(extfn::checked_from_secs)
428                }
429            }
430        };
431    }
432
433    __impl_fallible_int!(with-unwrap i8, i16, i32, i64, i128, u128);
434    // theoretically infallible
435    __impl_fallible_int!(u8, u16, u32, u64);
436
437    macro_rules! __impl_fallible_float {
438        ($($ty:ty: $ident:ident),*) => {
439            $(__impl_fallible_float!(@$ty, $ident);)*
440        };
441
442        (@$ty:ty, $ident:ident) => {
443            impl DurationNumExtFallible for $ty {
444                #[inline]
445                fn seconds(&self) -> Option<Duration> {
446                    Duration::$ident(*self).ok()
447                }
448
449                #[inline]
450                fn microseconds(&self) -> Option<Duration> {
451                    Duration::$ident(*self / 1_000_000.0).ok()
452                }
453
454                #[inline]
455                fn milliseconds(&self) -> Option<Duration> {
456                    Duration::$ident(*self / 1_000.0).ok()
457                }
458
459                #[inline]
460                fn nanoseconds(&self) -> Option<Duration> {
461                    Duration::$ident(*self / 1_000_000_000.0).ok()
462                }
463
464                #[inline]
465                fn minutes(&self) -> Option<Duration> {
466                    Duration::$ident(*self * 60.0).ok()
467                }
468
469                #[inline]
470                fn hours(&self) -> Option<Duration> {
471                    Duration::$ident(*self * 3600.0).ok()
472                }
473
474                #[inline]
475                fn days(&self) -> Option<Duration> {
476                    Duration::$ident(*self * 86400.0).ok()
477                }
478            }
479
480            impl DurationNumExt for $ty {}
481        };
482    }
483
484    __impl_fallible_float!(f32: try_from_secs_f32, f64: try_from_secs_f64);
485}
486
487pub const NANOS_PER_SEC: u32 = 1_000_000_000;
488pub const NANOS_PER_MILLI: u32 = 1_000_000;
489pub const NANOS_PER_MICRO: u32 = 1_000;
490pub const MILLIS_PER_SEC: u64 = 1_000;
491pub const MICROS_PER_SEC: u64 = 1_000_000;
492
493/// Static functions for [`std::time::Duration`].
494pub mod extfn {
495    use std::time::Duration;
496
497    use super::{MICROS_PER_SEC, MILLIS_PER_SEC, NANOS_PER_MICRO, NANOS_PER_MILLI, NANOS_PER_SEC};
498
499    /// A checked version of [`Duration::from_secs`][duration_from_secs].
500    ///
501    /// [duration_from_secs]: std::time::Duration#method.from_secs
502    pub fn checked_from_secs(secs: u64) -> Option<Duration> {
503        checked_new(secs, 0)
504    }
505
506    /// A checked version of [`Duration::from_millis`][duration_from_millis].
507    ///
508    /// [duration_from_millis]: std::time::Duration#method.from_millis
509    pub fn checked_from_millis(millis: u64) -> Option<Duration> {
510        millis
511            .checked_div(MILLIS_PER_SEC)
512            .zip(
513                millis
514                    .checked_rem(MILLIS_PER_SEC)
515                    .and_then(|r| <u64 as TryInto<u32>>::try_into(r).ok())
516                    .and_then(|r| r.checked_mul(NANOS_PER_MILLI)),
517            )
518            .and_then(|(x, y)| checked_new(x, y))
519    }
520
521    /// A checked version of [`Duration::from_micros`][duration_from_micros].
522    ///
523    /// [duration_from_micros]: std::time::Duration#method.from_micros
524    pub fn checked_from_micros(micros: u64) -> Option<Duration> {
525        micros
526            .checked_div(MICROS_PER_SEC)
527            .zip(
528                micros
529                    .checked_rem(MICROS_PER_SEC)
530                    .and_then(|r| <u64 as TryInto<u32>>::try_into(r).ok())
531                    .and_then(|r| r.checked_mul(NANOS_PER_MICRO)),
532            )
533            .and_then(|(x, y)| checked_new(x, y))
534    }
535
536    /// A checked version of [`Duration::from_nanos`][duration_from_nanos].
537    ///
538    /// [duration_from_nanos]: std::time::Duration#method.from_nanos
539    pub fn checked_from_nanos(nanos: u64) -> Option<Duration> {
540        nanos
541            .checked_div(NANOS_PER_SEC as u64)
542            .zip(
543                nanos
544                    .checked_rem(NANOS_PER_SEC as u64)
545                    .and_then(|r| <u64 as TryInto<u32>>::try_into(r).ok()),
546            )
547            .and_then(|(x, y)| checked_new(x, y))
548    }
549
550    /// A pre-checked version of [`Duration::new`][duration_new].
551    ///
552    /// Note that `Duration` does not offer direct construction,
553    /// and this method internally calls `Duration::new`, which
554    /// might still panics. However, this method uplifts the check,
555    /// and theoretically never panics.
556    ///
557    /// [duration_new]: std::time::Duration#method.new
558    pub fn checked_new(secs: u64, nanos: u32) -> Option<Duration> {
559        let secs = match secs.checked_add((nanos / NANOS_PER_SEC) as u64) {
560            Some(secs) => secs,
561            None => return None,
562        };
563        let nanos = nanos % NANOS_PER_SEC;
564        Some(Duration::new(secs, nanos))
565    }
566}
567
568/// Extension methods for [`std::time::Duration`].
569pub trait DurationExt {}