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// }