Skip to main content

time_core/
unit.rs

1//! Conversion between units of time.
2
3use self::sealed::{DefaultOutput, MultipleOf};
4
5/// Given the list of types, stringify them as a list.
6macro_rules! stringify_outputs {
7    (@inner $first:ty) => {
8        concat!("or `", stringify!($first), "`")
9    };
10    (@inner $first:ty, $($t:ty),+) => {
11        concat!(stringify_outputs!($first), ", ", stringify_outputs!(@inner $($t),+))
12    };
13    ($first:ty) => {
14        concat!("`", stringify!($first), "`")
15    };
16    ($($t:ty),+) => {
17        stringify_outputs!(@inner $($t),+)
18    };
19}
20
21/// Declare all unit types.
22macro_rules! declare_types {
23    ($($t:ident ($str:literal))+) => {$(
24        #[doc = concat!("A unit of time representing exactly `N` ", $str, "s.")]
25        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26        pub struct $t<const N: u128 = 1>;
27    )+};
28}
29
30/// Implement `Per` for all relevant types. Identity implementations are automatic.
31macro_rules! impl_per {
32    ($($t:ident per {$(
33        $larger:ident : [$default_output:ty]
34
35        $($int_output:ty)|+ = $int_value:expr;
36        $($float_output:ty)|+ = $float_value:expr;
37    )+})*) => {$(
38        impl $t<1> {
39            #[doc = concat!("Obtain the number of times `", stringify!($t), "` can fit into `T`.")]
40            #[doc = concat!("If `T` is smaller than `", stringify!($t), "`, the code will fail to")]
41            /// compile. The return type is the smallest unsigned integer type that can represent
42            /// the value.
43            ///
44            /// Valid calls:
45            ///
46            $(#[doc = concat!(
47                "  - `", stringify!($t), "::per(", stringify!($larger), ")` (returns `",
48                stringify!($default_output), "`)"
49            )])+
50            #[inline]
51            pub const fn per<T>(_larger: T) -> <T as DefaultOutput<Self>>::Output
52            where
53                T: MultipleOf<Self, T::Output> + DefaultOutput<Self> + Copy,
54            {
55                T::VALUE
56            }
57
58            #[doc = concat!("Obtain the number of times `", stringify!($t), "` can fit into `T`.")]
59            #[doc = concat!("If `T` is smaller than `", stringify!($t), "`, the code will fail to")]
60            /// compile. The return type is any primitive numeric type that can represent the value.
61            ///
62            /// Valid calls:
63            ///
64            $(#[doc = concat!(
65                "  - `", stringify!($t), "::per(", stringify!($larger), ")` (returns ",
66                stringify_outputs!($($int_output),+ , $($float_output),+), ")"
67            )])+
68            #[inline]
69            pub const fn per_t<Output>(larger: impl MultipleOf<Self, Output> + Copy) -> Output {
70                multiple_of_value(larger)
71            }
72        }
73
74        $(
75            $(impl MultipleOf<$t, $int_output> for $larger {
76                const VALUE: $int_output = $int_value;
77            })+
78
79            $(impl MultipleOf<$t, $float_output> for $larger {
80                const VALUE: $float_output = $float_value;
81            })+
82
83            impl DefaultOutput<$t> for $larger {
84                type Output = $default_output;
85            }
86        )+
87    )*};
88}
89
90/// Implement `PartialEq` and `PartialOrd` between unit types.
91macro_rules! impl_partial_eq_ord {
92    ($($self:ident {
93        $(> $($bigger:ident)+;)?
94        $(< $($smaller:ident)+;)?
95    })*) => {$(
96        $($(
97            impl<const N: u128> PartialEq<$bigger<N>> for $self<N> {
98                #[inline]
99                fn eq(&self, _: &$bigger<N>) -> bool {
100                    false
101                }
102            }
103
104            impl<const N: u128> PartialOrd<$bigger<N>> for $self<N> {
105                #[inline]
106                fn partial_cmp(&self, _: &$bigger<N>) -> Option<core::cmp::Ordering> {
107                    Some(core::cmp::Ordering::Greater)
108                }
109            }
110        )+)?
111
112        $($(
113            impl<const N: u128> PartialEq<$smaller<N>> for $self<N> {
114                #[inline]
115                fn eq(&self, _: &$smaller<N>) -> bool {
116                    false
117                }
118            }
119
120            impl<const N: u128> PartialOrd<$smaller<N>> for $self<N> {
121                #[inline]
122                fn partial_cmp(&self, _: &$smaller<N>) -> Option<core::cmp::Ordering> {
123                    Some(core::cmp::Ordering::Less)
124                }
125            }
126        )+)?
127    )*};
128}
129
130/// Symmetrically implement `PartialEq` and `PartialOrd` between `Unit` and all unit types.
131macro_rules! impl_partial_eq_ord_for_unit {
132    ($($ty:ident)+) => {$(
133        impl<const N: u128> PartialEq<$ty<N>> for Unit<N> {
134            #[inline]
135            fn eq(&self, _: &$ty<N>) -> bool {
136                *self == Self::$ty
137            }
138        }
139
140        impl<const N: u128> PartialEq<Unit<N>> for $ty<N> {
141            #[inline]
142            fn eq(&self, other: &Unit<N>) -> bool {
143                *other == Unit::$ty
144            }
145        }
146
147        impl<const N: u128> PartialOrd<$ty<N>> for Unit<N> {
148            #[inline]
149            fn partial_cmp(&self, _: &$ty<N>) -> Option<core::cmp::Ordering> {
150                self.partial_cmp(&Unit::$ty)
151            }
152        }
153
154        impl<const N: u128> PartialOrd<Unit<N>> for $ty<N> {
155            #[inline]
156            fn partial_cmp(&self, other: &Unit<N>) -> Option<core::cmp::Ordering> {
157                Unit::$ty.partial_cmp(other)
158            }
159        }
160    )+};
161}
162
163mod sealed {
164    /// A trait for defining the ratio of two units of time.
165    ///
166    /// This trait is used to implement the `per` method on the various structs.
167    #[diagnostic::on_unimplemented(message = "`{Self}` is not an integer multiple of `{T}`")]
168    pub trait MultipleOf<T, Output> {
169        /// The number of one unit of time in the other.
170        const VALUE: Output;
171    }
172
173    /// A trait for defining the default output type for the `per` method.
174    pub trait DefaultOutput<T> {
175        /// The default output type for the `per` method.
176        type Output;
177    }
178}
179
180// Split this out to a separate function to permit naming `T` while also using `impl Trait` as a
181// parameter in the public API.`
182const fn multiple_of_value<T, U, Output>(_: T) -> Output
183where
184    T: MultipleOf<U, Output> + Copy,
185{
186    T::VALUE
187}
188
189declare_types! {
190    Nanosecond ("nanosecond")
191    Microsecond ("microsecond")
192    Millisecond ("millisecond")
193    Second ("second")
194    Minute ("minute")
195    Hour ("hour")
196    Day ("day")
197    Week ("week")
198}
199
200impl_per! {
201    Nanosecond per {
202        Nanosecond: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
203        Microsecond: [u16] u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 1_000; f32|f64 = 1_000.;
204        Millisecond: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 1_000_000; f32|f64 = 1_000_000.;
205        Second:
206            [u32] u32|u64|u128|usize|i32|i64|i128|isize = 1_000_000_000; f32|f64 = 1_000_000_000.;
207        Minute: [u64] u64|u128|i64|i128 = 60_000_000_000; f32|f64 = 60_000_000_000.;
208        Hour: [u64] u64|u128|i64|i128 = 3_600_000_000_000; f32|f64 = 3_600_000_000_000.;
209        Day: [u64] u64|u128|i64|i128 = 86_400_000_000_000; f32|f64 = 86_400_000_000_000.;
210        Week: [u64] u64|u128|i64|i128 = 604_800_000_000_000; f32|f64 = 604_800_000_000_000.;
211    }
212    Microsecond per {
213        Microsecond: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
214        Millisecond: [u16] u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 1_000; f32|f64 = 1_000.;
215        Second: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 1_000_000; f32|f64 = 1_000_000.;
216        Minute: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 60_000_000; f32|f64 = 60_000_000.;
217        Hour: [u32] u32|u64|u128|i64|i128 = 3_600_000_000; f32|f64 = 3_600_000_000.;
218        Day: [u64] u64|u128|i64|i128 = 86_400_000_000; f32|f64 = 86_400_000_000.;
219        Week: [u64] u64|u128|i64|i128 = 604_800_000_000; f32|f64 = 604_800_000_000.;
220    }
221    Millisecond per {
222        Millisecond: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
223        Second: [u16] u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 1_000; f32|f64 = 1_000.;
224        Minute: [u16] u16|u32|u64|u128|usize|i32|i64|i128|isize = 60_000; f32|f64 = 60_000.;
225        Hour: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 3_600_000; f32|f64 = 3_600_000.;
226        Day: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 86_400_000; f32|f64 = 86_400_000.;
227        Week: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 604_800_000; f32|f64 = 604_800_000.;
228    }
229    Second per {
230        Second: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
231        Minute: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 60; f32|f64 = 60.;
232        Hour: [u16] u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 3_600; f32|f64 = 3_600.;
233        Day: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 86_400; f32|f64 = 86_400.;
234        Week: [u32] u32|u64|u128|usize|i32|i64|i128|isize = 604_800; f32|f64 = 604_800.;
235    }
236    Minute per {
237        Minute: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
238        Hour: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 60; f32|f64 = 60.;
239        Day: [u16] u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 1_440; f32|f64 = 1_440.;
240        Week: [u16] u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 10_080; f32|f64 = 10_080.;
241    }
242    Hour per {
243        Hour: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
244        Day: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 24; f32|f64 = 24.;
245        Week: [u8] u8|u16|u32|u64|u128|usize|i16|i32|i64|i128|isize = 168; f32|f64 = 168.;
246    }
247    Day per {
248        Day: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
249        Week: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 7; f32|f64 = 7.;
250    }
251    Week per {
252        Week: [u8] u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize = 1; f32|f64 = 1.;
253    }
254}
255
256impl_partial_eq_ord! {
257    Nanosecond {
258        < Microsecond Millisecond Second Minute Hour Day Week;
259    }
260    Microsecond {
261        > Nanosecond;
262        < Millisecond Second Minute Hour Day Week;
263    }
264    Millisecond {
265        > Nanosecond Microsecond;
266        < Second Minute Hour Day Week;
267    }
268    Second {
269        > Nanosecond Microsecond Millisecond;
270        < Minute Hour Day Week;
271    }
272    Minute {
273        > Nanosecond Microsecond Millisecond Second;
274        < Hour Day Week;
275    }
276    Hour {
277        > Nanosecond Microsecond Millisecond Second Minute;
278        < Day Week;
279    }
280    Day {
281        > Nanosecond Microsecond Millisecond Second Minute Hour;
282        < Week;
283    }
284    Week {
285        > Nanosecond Microsecond Millisecond Second Minute Hour Day;
286    }
287}
288
289/// A statically-known multiple of a given unit of time.
290#[non_exhaustive]
291#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
292pub enum Unit<const N: u128 = 1> {
293    #[expect(missing_docs)]
294    Nanosecond,
295    #[expect(missing_docs)]
296    Microsecond,
297    #[expect(missing_docs)]
298    Millisecond,
299    #[expect(missing_docs)]
300    Second,
301    #[expect(missing_docs)]
302    Minute,
303    #[expect(missing_docs)]
304    Hour,
305    #[expect(missing_docs)]
306    Day,
307    #[expect(missing_docs)]
308    Week,
309}
310
311impl_partial_eq_ord_for_unit![Nanosecond Microsecond Millisecond Second Minute Hour Day Week];