fugit/
instant.rs

1use crate::duration::Duration;
2use crate::helpers::{self, Helpers};
3use core::cmp::Ordering;
4use core::ops;
5
6/// Represents an instant in time.
7///
8/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
9/// ticks contained within the instant: `instant in seconds = NOM / DENOM * ticks`
10#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
11#[cfg_attr(
12    feature = "postcard_max_size",
13    derive(postcard::experimental::max_size::MaxSize)
14)]
15#[derive(Clone, Copy, Debug)]
16pub struct Instant<T, const NOM: u32, const DENOM: u32> {
17    ticks: T,
18}
19
20macro_rules! impl_instant_for_integer {
21    ($i:ty) => {
22        impl<const NOM: u32, const DENOM: u32> Instant<$i, NOM, DENOM> {
23            /// Create an `Instant` from a ticks value.
24            ///
25            /// ```
26            /// # use fugit::*;
27            #[doc = concat!("let _i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
28            /// ```
29            #[inline]
30            pub const fn from_ticks(ticks: $i) -> Self {
31                helpers::greater_than_0::<NOM>();
32                helpers::greater_than_0::<DENOM>();
33
34                Instant { ticks }
35            }
36
37            /// Extract the ticks from an `Instant`.
38            ///
39            /// ```
40            /// # use fugit::*;
41            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
42            ///
43            /// assert_eq!(i.ticks(), 234);
44            /// ```
45            #[inline]
46            pub const fn ticks(&self) -> $i {
47                self.ticks
48            }
49
50            /// Const comparison of `Instant`s.
51            ///
52            /// ```
53            /// # use fugit::*;
54            #[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
55            #[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
56            ///
57            /// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
58            /// ```
59            ///
60            /// This function takes into account that ticks might wrap around. If the absolute
61            /// values of `self` and `other` differ by more than half the possible range, it is
62            /// assumed that an overflow occured and the result is reversed:
63            ///
64            /// ```
65            /// # use fugit::*;
66            #[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i),"::MAX);")]
67            #[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
68            ///
69            /// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
70            /// ```
71            #[inline]
72            pub const fn const_cmp(self, other: Self) -> Ordering {
73                if self.ticks == other.ticks {
74                    Ordering::Equal
75                } else {
76                    let v = self.ticks.wrapping_sub(other.ticks);
77
78                    // not using `v.cmp(<$i>::MAX / 2).reverse()` due to `cmp` being non-const
79                    if v > <$i>::MAX / 2 {
80                        Ordering::Less
81                    } else if v < <$i>::MAX / 2 {
82                        Ordering::Greater
83                    } else {
84                        Ordering::Equal
85                    }
86                }
87            }
88
89            /// Duration between since the start of the `Instant`. This assumes an instant which
90            /// won't wrap within the execution of the program.
91            ///
92            /// ```
93            /// # use fugit::*;
94            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(11);")]
95            ///
96            /// assert_eq!(i.duration_since_epoch().ticks(), 11);
97            /// ```
98            #[inline]
99            pub const fn duration_since_epoch(self) -> Duration<$i, NOM, DENOM> {
100                Duration::<$i, NOM, DENOM>::from_ticks(self.ticks())
101            }
102
103            /// Duration between `Instant`s.
104            ///
105            /// ```
106            /// # use fugit::*;
107            #[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
108            #[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
109            ///
110            /// assert_eq!(i1.checked_duration_since(i2), None);
111            /// assert_eq!(i2.checked_duration_since(i1).unwrap().ticks(), 1);
112            /// ```
113            #[inline]
114            pub const fn checked_duration_since(
115                self,
116                other: Self,
117            ) -> Option<Duration<$i, NOM, DENOM>> {
118                match self.const_cmp(other) {
119                    Ordering::Greater | Ordering::Equal => {
120                        Some(Duration::<$i, NOM, DENOM>::from_ticks(
121                            self.ticks.wrapping_sub(other.ticks),
122                        ))
123                    }
124                    Ordering::Less => None,
125                }
126            }
127
128            /// Subtract a `Duration` from an `Instant` while checking for overflow.
129            ///
130            /// ```
131            /// # use fugit::*;
132            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
133            #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
134            ///
135            /// assert_eq!(i.checked_sub_duration(d).unwrap().ticks(), 0);
136            /// ```
137            pub const fn checked_sub_duration<const O_NOM: u32, const O_DENOM: u32>(
138                self,
139                other: Duration<$i, O_NOM, O_DENOM>,
140            ) -> Option<Self> {
141                if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
142                    Some(Instant::<$i, NOM, DENOM>::from_ticks(
143                        self.ticks.wrapping_sub(other.ticks()),
144                    ))
145                } else {
146                    if let Some(lh) = other
147                        .ticks()
148                        .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
149                    {
150                        let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
151
152                        Some(Instant::<$i, NOM, DENOM>::from_ticks(
153                            self.ticks.wrapping_sub(ticks),
154                        ))
155                    } else {
156                        None
157                    }
158                }
159            }
160
161            /// Add a `Duration` to an `Instant` while checking for overflow.
162            ///
163            /// ```
164            /// # use fugit::*;
165            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
166            #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
167            ///
168            /// assert_eq!(i.checked_add_duration(d).unwrap().ticks(), 2);
169            /// ```
170            pub const fn checked_add_duration<const O_NOM: u32, const O_DENOM: u32>(
171                self,
172                other: Duration<$i, O_NOM, O_DENOM>,
173            ) -> Option<Self> {
174                if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
175                    Some(Instant::<$i, NOM, DENOM>::from_ticks(
176                        self.ticks.wrapping_add(other.ticks()),
177                    ))
178                } else {
179                    if let Some(lh) = other
180                        .ticks()
181                        .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
182                    {
183                        let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
184
185                        Some(Instant::<$i, NOM, DENOM>::from_ticks(
186                            self.ticks.wrapping_add(ticks),
187                        ))
188                    } else {
189                        None
190                    }
191                }
192            }
193        }
194
195        impl<const NOM: u32, const DENOM: u32> PartialOrd for Instant<$i, NOM, DENOM> {
196            /// This implementation deviates from the definition of
197            /// [PartialOrd::partial_cmp](core::cmp::PartialOrd::partial_cmp):
198            ///
199            /// It takes into account that ticks might wrap around. If the absolute
200            /// values of `self` and `other` differ by more than half the possible range, it is
201            /// assumed that an overflow occured and the result is reversed.
202            ///
203            /// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
204            #[inline]
205            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
206                Some(self.const_cmp(*other))
207            }
208        }
209
210        impl<const NOM: u32, const DENOM: u32> Ord for Instant<$i, NOM, DENOM> {
211            /// This implementation deviates from the definition of
212            /// [Ord::cmp](core::cmp::Ord::cmp):
213            ///
214            /// It takes into account that ticks might wrap around. If the absolute
215            /// values of `self` and `other` differ by more than half the possible range, it is
216            /// assumed that an overflow occured and the result is reversed.
217            ///
218            /// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
219            #[inline]
220            fn cmp(&self, other: &Self) -> Ordering {
221                self.const_cmp(*other)
222            }
223        }
224
225        impl<const NOM: u32, const DENOM: u32> PartialEq for Instant<$i, NOM, DENOM> {
226            #[inline]
227            fn eq(&self, other: &Self) -> bool {
228                self.ticks.eq(&other.ticks)
229            }
230        }
231
232        impl<const NOM: u32, const DENOM: u32> Eq for Instant<$i, NOM, DENOM> {}
233
234        // Instant - Instant = Duration
235        // We have limited this to use same numerator and denominator in both left and right hand sides,
236        // this allows for the extension traits to work. For usage with different fraction, use
237        // `checked_duration_since`.
238        impl<const NOM: u32, const DENOM: u32> ops::Sub<Instant<$i, NOM, DENOM>>
239            for Instant<$i, NOM, DENOM>
240        {
241            type Output = Duration<$i, NOM, DENOM>;
242
243            #[inline]
244            fn sub(self, other: Self) -> Self::Output {
245                if let Some(v) = self.checked_duration_since(other) {
246                    v
247                } else {
248                    panic!("Sub failed! Other > self");
249                }
250            }
251        }
252
253        // Instant - Duration = Instant
254        // We have limited this to use same numerator and denominator in both left and right hand sides,
255        // this allows for the extension traits to work. For usage with different fraction, use
256        // `checked_sub_duration`.
257        impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
258            for Instant<$i, NOM, DENOM>
259        {
260            type Output = Instant<$i, NOM, DENOM>;
261
262            #[inline]
263            fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
264                if let Some(v) = self.checked_sub_duration(other) {
265                    v
266                } else {
267                    panic!("Sub failed! Overflow");
268                }
269            }
270        }
271
272        // Instant -= Duration
273        // We have limited this to use same numerator and denominator in both left and right hand sides,
274        // this allows for the extension traits to work. For usage with different fraction, use
275        // `checked_sub_duration`.
276        impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
277            for Instant<$i, NOM, DENOM>
278        {
279            #[inline]
280            fn sub_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
281                *self = *self - other;
282            }
283        }
284
285        // Instant + Duration = Instant
286        // We have limited this to use same numerator and denominator in both left and right hand sides,
287        // this allows for the extension traits to work. For usage with different fraction, use
288        // `checked_add_duration`.
289        impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
290            for Instant<$i, NOM, DENOM>
291        {
292            type Output = Instant<$i, NOM, DENOM>;
293
294            #[inline]
295            fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
296                if let Some(v) = self.checked_add_duration(other) {
297                    v
298                } else {
299                    panic!("Add failed! Overflow");
300                }
301            }
302        }
303
304        // Instant += Duration
305        // We have limited this to use same numerator and denominator in both left and right hand sides,
306        // this allows for the extension traits to work. For usage with different fraction, use
307        // `checked_add_duration`.
308        impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
309            for Instant<$i, NOM, DENOM>
310        {
311            #[inline]
312            fn add_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
313                *self = *self + other;
314            }
315        }
316
317        #[cfg(feature = "defmt")]
318        impl<const NOM: u32, const DENOM: u32> defmt::Format for Instant<$i, NOM, DENOM> {
319            fn format(&self, f: defmt::Formatter) {
320                if NOM == 3_600 && DENOM == 1 {
321                    defmt::write!(f, "{} h", self.ticks)
322                } else if NOM == 60 && DENOM == 1 {
323                    defmt::write!(f, "{} min", self.ticks)
324                } else if NOM == 1 && DENOM == 1 {
325                    defmt::write!(f, "{} s", self.ticks)
326                } else if NOM == 1 && DENOM == 1_000 {
327                    defmt::write!(f, "{} ms", self.ticks)
328                } else if NOM == 1 && DENOM == 1_000_000 {
329                    defmt::write!(f, "{} us", self.ticks)
330                } else if NOM == 1 && DENOM == 1_000_000_000 {
331                    defmt::write!(f, "{} ns", self.ticks)
332                } else {
333                    defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
334                }
335            }
336        }
337
338        impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Instant<$i, NOM, DENOM> {
339            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
340                if NOM == 3_600 && DENOM == 1 {
341                    write!(f, "{} h", self.ticks)
342                } else if NOM == 60 && DENOM == 1 {
343                    write!(f, "{} min", self.ticks)
344                } else if NOM == 1 && DENOM == 1 {
345                    write!(f, "{} s", self.ticks)
346                } else if NOM == 1 && DENOM == 1_000 {
347                    write!(f, "{} ms", self.ticks)
348                } else if NOM == 1 && DENOM == 1_000_000 {
349                    write!(f, "{} us", self.ticks)
350                } else if NOM == 1 && DENOM == 1_000_000_000 {
351                    write!(f, "{} ns", self.ticks)
352                } else {
353                    write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
354                }
355            }
356        }
357    };
358}
359
360impl_instant_for_integer!(u32);
361impl_instant_for_integer!(u64);
362
363//
364// Operations between u32 Duration and u64 Instant
365//
366
367// Instant - Duration = Instant
368// We have limited this to use same numerator and denominator in both left and right hand sides,
369// this allows for the extension traits to work. For usage with different fraction, use
370// `checked_sub_duration`.
371impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
372    for Instant<u64, NOM, DENOM>
373{
374    type Output = Instant<u64, NOM, DENOM>;
375
376    #[inline]
377    fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
378        if let Some(v) = self.checked_sub_duration(other.into()) {
379            v
380        } else {
381            panic!("Sub failed! Overflow");
382        }
383    }
384}
385
386// Instant -= Duration
387// We have limited this to use same numerator and denominator in both left and right hand sides,
388// this allows for the extension traits to work. For usage with different fraction, use
389// `checked_sub_duration`.
390impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
391    for Instant<u64, NOM, DENOM>
392{
393    #[inline]
394    fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
395        *self = *self - other;
396    }
397}
398
399// Instant + Duration = Instant
400// We have limited this to use same numerator and denominator in both left and right hand sides,
401// this allows for the extension traits to work. For usage with different fraction, use
402// `checked_add_duration`.
403impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
404    for Instant<u64, NOM, DENOM>
405{
406    type Output = Instant<u64, NOM, DENOM>;
407
408    #[inline]
409    fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
410        if let Some(v) = self.checked_add_duration(other.into()) {
411            v
412        } else {
413            panic!("Add failed! Overflow");
414        }
415    }
416}
417
418// Instant += Duration
419// We have limited this to use same numerator and denominator in both left and right hand sides,
420// this allows for the extension traits to work. For usage with different fraction, use
421// `checked_add_duration`.
422impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
423    for Instant<u64, NOM, DENOM>
424{
425    #[inline]
426    fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
427        *self = *self + other;
428    }
429}
430
431// impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
432//     ops::Add<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
433// {
434//     type Output = Duration<u64, L_NOM, L_DENOM>;
435//
436//     #[inline]
437//     fn add(self, other: Duration<u32, R_NOM, R_DENOM>) -> Self::Output {
438//         self.add(Duration::<u64, L_NOM, L_DENOM>::from_ticks(
439//             other.ticks() as u64
440//         ))
441//     }
442// }