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