1use crate::helpers::{div_round_nearest_u64, Helpers};
2use crate::NanosDurationU64;
3use crate::Rate;
4use crate::SecsDurationU64;
5use core::cmp::Ordering;
6use core::convert;
7use core::ops;
8
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
14#[cfg_attr(
15 feature = "postcard_max_size",
16 derive(postcard::experimental::max_size::MaxSize)
17)]
18#[derive(Clone, Copy, Debug)]
19pub struct Duration<T, const NOM: u64, const DENOM: u64> {
20 pub(crate) ticks: T,
21}
22
23macro_rules! const_checked {
27 ($e:expr, $msg:expr) => {
28 match $e {
29 Some(v) => v,
30 None => panic!("{}", $msg),
31 }
32 };
33}
34
35macro_rules! shorthand {
36 ($i:ty, $nom:literal, $denum:literal, $from_unit:ident, $as_unit:ident, $from_unital:ident, $unitstr:literal) => {
37 #[doc = concat!("Convert the Duration to an integer number of ", $unitstr, ".")]
38 #[doc = ""]
39 #[doc = concat!("**Compile-time error** if the conversion constants don't fit in `", stringify!($i), "`. **Panics** if the multiplication overflows `", stringify!($i), "`.")]
40 #[inline]
41 #[track_caller]
42 pub const fn $as_unit(&self) -> $i {
43 const {
44 assert!(
45 Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN <= <$i>::MAX as u64,
46 concat!("Conversion constant RD_TIMES_LN doesn't fit in ", stringify!($i), " for this Duration type")
47 );
48 assert!(
49 Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN <= <$i>::MAX as u64,
50 concat!("Conversion constant LD_TIMES_RN doesn't fit in ", stringify!($i), " for this Duration type")
51 );
52 }
53
54 let prod = const_checked!(
55 (Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i).checked_mul(self.ticks),
56 concat!("Duration::", stringify!($as_unit), ": multiplication overflowed storage type")
57 );
58 prod / Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i
59 }
60
61 #[doc = concat!("Create a duration from a number of ", $unitstr, ".")]
62 #[doc = ""]
63 #[doc = concat!("**Compile-time error** if the conversion constants don't fit in `", stringify!($i), "`. **Panics** if the multiplication overflows `", stringify!($i), "`.")]
64 #[inline]
65 #[track_caller]
66 pub const fn $from_unit(val: $i) -> Self {
67 const {
68 assert!(
69 Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN <= <$i>::MAX as u64,
70 concat!("Conversion constant RD_TIMES_LN doesn't fit in ", stringify!($i), " for this Duration type")
71 );
72 assert!(
73 Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN <= <$i>::MAX as u64,
74 concat!("Conversion constant LD_TIMES_RN doesn't fit in ", stringify!($i), " for this Duration type")
75 );
76 }
77
78 let prod = const_checked!(
79 (Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i).checked_mul(val),
80 concat!("Duration::", stringify!($from_unit), ": multiplication overflowed storage type")
81 );
82 Self::from_ticks(prod / Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i)
83 }
84
85 #[doc = concat!("Create a duration from a number of ", $unitstr, " (ceil rounded).")]
86 #[doc = ""]
87 #[doc = concat!("**Compile-time error** if the conversion constants don't fit in `", stringify!($i), "`. **Panics** if the multiplication overflows `", stringify!($i), "`.")]
88 #[inline]
89 #[track_caller]
90 pub const fn $from_unital(val: $i) -> Self {
91 const {
92 assert!(
93 Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN <= <$i>::MAX as u64,
94 concat!("Conversion constant RD_TIMES_LN doesn't fit in ", stringify!($i), " for this Duration type")
95 );
96 assert!(
97 Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN <= <$i>::MAX as u64,
98 concat!("Conversion constant LD_TIMES_RN doesn't fit in ", stringify!($i), " for this Duration type")
99 );
100 }
101
102 let mul = const_checked!(
103 (Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i).checked_mul(val),
104 concat!("Duration::", stringify!($from_unital), ": multiplication overflowed storage type")
105 );
106 let ld_times_rn = Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i;
107 Self::from_ticks(mul.div_ceil(ld_times_rn))
108 }
109 };
110}
111
112macro_rules! impl_duration_for_integer {
113 ($i:ty) => {
114 impl<const NOM: u64, const DENOM: u64> Duration<$i, NOM, DENOM> {
115 #[doc = concat!("let _d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
120 #[inline]
122 pub const fn from_ticks(ticks: $i) -> Self {
123 const { assert!(NOM > 0) };
124 const { assert!(DENOM > 0) };
125
126 Duration { ticks }
127 }
128
129 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
134 #[inline]
138 pub const fn as_ticks(&self) -> $i {
139 self.ticks
140 }
141
142 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::ZERO;")]
147 pub const ZERO: Self = Self::from_ticks(0);
152
153 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::MAX;")]
158 #[doc = concat!("assert_eq!(d.as_ticks(), ", stringify!($i), "::MAX);")]
160 pub const MAX: Self = Self::from_ticks(<$i>::MAX);
162
163 #[doc = concat!("let zero = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(0);")]
168 #[doc = concat!("let one = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
169 #[inline]
174 pub const fn is_zero(&self) -> bool {
175 self.ticks == 0
176 }
177
178 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
185 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
186 #[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
187 pub const fn checked_add<const O_NOM: u64, const O_DENOM: u64>(
192 self,
193 other: Duration<$i, O_NOM, O_DENOM>,
194 ) -> Option<Self> {
195 if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
196 if let Some(ticks) = self.ticks.checked_add(other.ticks) {
197 Some(Self::from_ticks(ticks))
198 } else {
199 None
200 }
201 } else {
202 if let Some(lh) = other
203 .ticks
204 .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
205 {
206 let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
207
208 if let Some(ticks) = self.ticks.checked_add(ticks) {
209 Some(Self::from_ticks(ticks))
210 } else {
211 None
212 }
213 } else {
214 None
215 }
216 }
217 }
218
219 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
226 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
227 #[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
228 pub const fn checked_sub<const O_NOM: u64, const O_DENOM: u64>(
233 self,
234 other: Duration<$i, O_NOM, O_DENOM>,
235 ) -> Option<Self> {
236 if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
237 if let Some(ticks) = self.ticks.checked_sub(other.ticks) {
238 Some(Self::from_ticks(ticks))
239 } else {
240 None
241 }
242 } else {
243 if let Some(lh) = other
244 .ticks
245 .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
246 {
247 let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
248
249 if let Some(ticks) = self.ticks.checked_sub(ticks) {
250 Some(Self::from_ticks(ticks))
251 } else {
252 None
253 }
254 } else {
255 None
256 }
257 }
258 }
259
260 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
265 #[doc = concat!("assert_eq!(Duration::<", stringify!($i), ", 1, 1_000>::MAX.checked_mul(2), None);")]
268 #[inline]
270 pub const fn checked_mul(self, rhs: $i) -> Option<Self> {
271 if let Some(ticks) = self.ticks.checked_mul(rhs) {
272 Some(Self::from_ticks(ticks))
273 } else {
274 None
275 }
276 }
277
278 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(30);")]
283 #[doc = concat!("assert_eq!(Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10).checked_div(0), None);")]
286 #[inline]
288 pub const fn checked_div(self, rhs: $i) -> Option<Self> {
289 if rhs == 0 {
290 None
291 } else {
292 Some(Self::from_ticks(self.ticks / rhs))
293 }
294 }
295
296 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
303 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(3);")]
304 pub const fn checked_rem<const O_NOM: u64, const O_DENOM: u64>(
308 self,
309 other: Duration<$i, O_NOM, O_DENOM>,
310 ) -> Option<Self> {
311 if other.ticks == 0 {
312 None
313 } else if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
314 Some(Self::from_ticks(self.ticks % other.ticks))
315 } else {
316 if let Some(lh) = other
317 .ticks
318 .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
319 {
320 let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
321
322 if ticks > 0 {
323 Some(Self::from_ticks(self.ticks % ticks))
324 } else {
325 None
326 }
327 } else {
328 None
329 }
330 }
331 }
332
333 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(31);")]
338 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(30);")]
341 #[inline]
348 #[track_caller]
349 pub const fn div_ceil(self, rhs: $i) -> Self {
350 Self::from_ticks(self.ticks.div_ceil(rhs))
351 }
352
353 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
358 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
359 #[doc = concat!("assert_eq!(Duration::<", stringify!($i), ", 1, 1_000>::MAX.saturating_add(d1).as_ticks(), ", stringify!($i), "::MAX);")]
362 pub const fn saturating_add<const O_NOM: u64, const O_DENOM: u64>(
364 self,
365 other: Duration<$i, O_NOM, O_DENOM>,
366 ) -> Self {
367 if let Some(result) = self.checked_add(other) {
368 result
369 } else {
370 Self::MAX
371 }
372 }
373
374 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
379 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
380 pub const fn saturating_sub<const O_NOM: u64, const O_DENOM: u64>(
385 self,
386 other: Duration<$i, O_NOM, O_DENOM>,
387 ) -> Self {
388 if let Some(result) = self.checked_sub(other) {
389 result
390 } else {
391 Self::ZERO
392 }
393 }
394
395 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
400 #[doc = concat!("assert_eq!(Duration::<", stringify!($i), ", 1, 1_000>::MAX.saturating_mul(2).as_ticks(), ", stringify!($i), "::MAX);")]
403 #[inline]
405 pub const fn saturating_mul(self, rhs: $i) -> Self {
406 if let Some(result) = self.checked_mul(rhs) {
407 result
408 } else {
409 Self::MAX
410 }
411 }
412
413 #[doc = concat!("Const `cmp` for ", stringify!($i))]
414 #[inline(always)]
415 const fn _const_cmp(a: $i, b: $i) -> Ordering {
416 if a < b {
417 Ordering::Less
418 } else if a > b {
419 Ordering::Greater
420 } else {
421 Ordering::Equal
422 }
423 }
424
425 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 100>::from_ticks(1);")]
433 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
434 #[inline]
438 pub const fn const_partial_cmp<const R_NOM: u64, const R_DENOM: u64>(
439 self,
440 other: Duration<$i, R_NOM, R_DENOM>
441 ) -> Option<Ordering> {
442 if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
458 Some(Self::_const_cmp(self.ticks, other.ticks))
460 } else {
461 let lh = self
462 .ticks
463 .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
464 let rh = other
465 .ticks
466 .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
467
468 if let (Some(lh), Some(rh)) = (lh, rh) {
469 Some(Self::_const_cmp(lh, rh))
470 } else {
471 None
472 }
473 }
474 }
475
476 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
484 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
485 #[inline]
489 pub const fn const_eq<const R_NOM: u64, const R_DENOM: u64>(
490 self,
491 other: Duration<$i, R_NOM, R_DENOM>
492 ) -> bool {
493 if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
494 self.ticks == other.ticks
496 } else {
497 let lh = self
498 .ticks
499 .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
500 let rh = other
501 .ticks
502 .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
503
504 if let (Some(lh), Some(rh)) = (lh, rh) {
505 lh == rh
506 } else {
507 false
508 }
509 }
510 }
511
512 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
517 #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::const_try_from(d1);")]
518 pub const fn const_try_from<const I_NOM: u64, const I_DENOM: u64>(
522 duration: Duration<$i, I_NOM, I_DENOM>,
523 ) -> Option<Self> {
524 if Helpers::<I_NOM, I_DENOM, NOM, DENOM>::SAME_BASE {
525 Some(Self::from_ticks(duration.ticks))
526 } else {
527 if let Some(lh) = (duration.ticks as u64)
528 .checked_mul(Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RD_TIMES_LN)
529 {
530 let ticks = div_round_nearest_u64(
531 lh,
532 Helpers::<I_NOM, I_DENOM, NOM, DENOM>::LD_TIMES_RN,
533 );
534
535 if ticks <= <$i>::MAX as u64 {
536 Some(Self::from_ticks(ticks as $i))
537 } else {
538 None
539 }
540 } else {
541 None
542 }
543 }
544 }
545
546 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
551 #[doc = concat!("let d2: Option<Duration::<", stringify!($i), ", 1, 1_000>> = d1.const_try_into();")]
552 #[inline]
556 pub const fn const_try_into<const O_NOM: u64, const O_DENOM: u64>(
557 self,
558 ) -> Option<Duration<$i, O_NOM, O_DENOM>> {
559 Duration::<$i, O_NOM, O_DENOM>::const_try_from(self)
560 }
561
562 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
567 #[doc = concat!("let r1: Option<Rate::<", stringify!($i), ", 1, 1>> = d1.try_to_rate();")]
568 #[inline]
572 pub const fn try_to_rate<const O_NOM: u64, const O_DENOM: u64>(
573 self,
574 ) -> Option<Rate<$i, O_NOM, O_DENOM>> {
575 Rate::<$i, O_NOM, O_DENOM>::try_from_duration(self)
576 }
577
578 #[inline]
580 #[track_caller]
581 pub const fn to_rate<const O_NOM: u64, const O_DENOM: u64>(
582 self,
583 ) -> Rate<$i, O_NOM, O_DENOM> {
584 if let Some(v) = self.try_to_rate() {
585 v
586 } else {
587 panic!("Into rate failed, divide-by-zero!");
588 }
589 }
590
591 #[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::from_raw(1);")]
596 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::try_from_rate(r1);")]
597 #[inline]
601 pub const fn try_from_rate<const I_NOM: u64, const I_DENOM: u64>(
602 rate: Rate<$i, I_NOM, I_DENOM>,
603 ) -> Option<Self> {
604 const {
605 assert!(
606 Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR <= <$i>::MAX as u64,
607 concat!("RATE_TO_DURATION_NUMERATOR doesn't fit in ", stringify!($i), " for this Rate/Duration combination")
608 );
609 }
610 if rate.raw > 0 {
611 Some(Self::from_ticks(
612 Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR as $i
613 / rate.raw
614 ))
615 } else {
616 None
617 }
618 }
619
620 #[inline]
622 #[track_caller]
623 pub const fn from_rate<const I_NOM: u64, const I_DENOM: u64>(
624 rate: Rate<$i, I_NOM, I_DENOM>,
625 ) -> Self {
626 if let Some(v) = Self::try_from_rate(rate) {
627 v
628 } else {
629 panic!("From rate failed, divide-by-zero!");
630 }
631 }
632
633 #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 100>::from_ticks(1);")]
640 #[doc = concat!("let d2: Duration::<", stringify!($i), ", 1, 1_000> = d1.convert();")]
641 #[doc = concat!("const TICKS: ", stringify!($i), "= ", stringify!($i), "::MAX - 10;")]
648 #[doc = concat!("const D1: Duration::<", stringify!($i), ", 1, 100> = Duration::<", stringify!($i), ", 1, 100>::from_ticks(TICKS);")]
649 #[doc = concat!("const D2: Duration::<", stringify!($i), ", 1, 200> = D1.convert();")]
651 #[inline]
652 #[track_caller]
653 pub const fn convert<const O_NOM: u64, const O_DENOM: u64>(
654 self,
655 ) -> Duration<$i, O_NOM, O_DENOM> {
656 if let Some(v) = self.const_try_into() {
657 v
658 } else {
659 panic!("Convert failed!");
660 }
661 }
662
663 shorthand!($i, 1, 1_000_000_000_000, from_picos, as_picos, from_picos_at_least, "picoseconds");
664 shorthand!($i, 1, 1_000_000_000, from_nanos, as_nanos, from_nanos_at_least, "nanoseconds");
665 shorthand!($i, 1, 1_000_000, from_micros, as_micros, from_micros_at_least, "microseconds");
666 shorthand!($i, 1, 1_000, from_millis, as_millis, from_millis_at_least, "milliseconds");
667 shorthand!($i, 1, 1, from_secs, as_secs, from_secs_at_least, "seconds");
668 shorthand!($i, 60, 1, from_minutes, as_minutes, from_minutes_at_least, "minutes");
669 shorthand!($i, 3600, 1, from_hours, as_hours, from_hours_at_least, "hours");
670
671 #[inline]
673 pub const fn as_secs_f32(&self) -> f32 {
674 let factor = const { NOM as f32 / DENOM as f32 };
675 factor * self.ticks as f32
676 }
677
678 #[inline]
680 pub const fn as_secs_f64(&self) -> f64 {
681 let factor = const { NOM as f64 / DENOM as f64 };
682 factor * self.ticks as f64
683 }
684
685 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_secs_f32(1.5);")]
692 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_secs_f32(1.5005);")]
696 #[inline]
699 pub const fn from_secs_f32(secs: f32) -> Self {
700 let factor = const { DENOM as f32 / NOM as f32 };
701 Self::from_ticks((secs * factor + 0.5) as $i)
702 }
703
704 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_secs_f64(1.5);")]
711 #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_secs_f64(1.5005);")]
715 #[inline]
718 pub const fn from_secs_f64(secs: f64) -> Self {
719 let factor = const { DENOM as f64 / NOM as f64 };
720 Self::from_ticks((secs * factor + 0.5) as $i)
721 }
722
723 #[inline]
725 #[track_caller]
726 #[allow(non_snake_case)]
727 pub const fn Hz(val: $i) -> Self {
728 Self::from_rate(crate::Hertz::<$i>::from_raw(val))
729 }
730
731 #[inline]
733 #[track_caller]
734 #[allow(non_snake_case)]
735 pub const fn kHz(val: $i) -> Self {
736 Self::from_rate(crate::Kilohertz::<$i>::from_raw(val))
737 }
738
739 #[inline]
741 #[track_caller]
742 #[allow(non_snake_case)]
743 pub const fn MHz(val: $i) -> Self {
744 Self::from_rate(crate::Megahertz::<$i>::from_raw(val))
745 }
746 }
747
748 impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64>
749 PartialOrd<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
750 {
751 #[inline]
752 fn partial_cmp(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> Option<Ordering> {
753 self.const_partial_cmp(*other)
754 }
755 }
756
757 impl<const NOM: u64, const DENOM: u64> Ord for Duration<$i, NOM, DENOM> {
758 #[inline]
759 fn cmp(&self, other: &Self) -> Ordering {
760 Self::_const_cmp(self.ticks, other.ticks)
761 }
762 }
763
764 impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64>
765 PartialEq<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
766 {
767 #[inline]
768 fn eq(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> bool {
769 self.const_eq(*other)
770 }
771 }
772
773 impl<const NOM: u64, const DENOM: u64> Eq for Duration<$i, NOM, DENOM> {}
774
775 impl<const NOM: u64, const DENOM: u64> ops::Sub for Duration<$i, NOM, DENOM>
778 {
779 type Output = Self;
780
781 #[inline]
782 #[track_caller]
783 fn sub(self, other: Self) -> Self::Output {
784 if let Some(v) = self.checked_sub(other) {
785 v
786 } else {
787 panic!("Sub failed!");
788 }
789 }
790 }
791
792 impl<const NOM: u64, const DENOM: u64> ops::SubAssign for Duration<$i, NOM, DENOM>
794 {
795 #[inline]
796 #[track_caller]
797 fn sub_assign(&mut self, other: Self) {
798 *self = *self - other;
799 }
800 }
801
802 impl<const NOM: u64, const DENOM: u64> ops::Add for Duration<$i, NOM, DENOM>
805 {
806 type Output = Self;
807
808 #[inline]
809 #[track_caller]
810 fn add(self, other: Self) -> Self::Output {
811 if let Some(v) = self.checked_add(other) {
812 v
813 } else {
814 panic!("Add failed!");
815 }
816 }
817 }
818
819 impl<const NOM: u64, const DENOM: u64> ops::AddAssign for Duration<$i, NOM, DENOM>
821 {
822 #[inline]
823 #[track_caller]
824 fn add_assign(&mut self, other: Self) {
825 *self = *self + other;
826 }
827 }
828
829 impl<const NOM: u64, const DENOM: u64> ops::Mul<Duration<$i, NOM, DENOM>> for u32 {
831 type Output = Duration<$i, NOM, DENOM>;
832
833 #[inline]
834 #[track_caller]
835 fn mul(self, mut other: Duration<$i, NOM, DENOM>) -> Self::Output {
836 other.ticks *= self as $i;
837 other
838 }
839 }
840
841 impl<const NOM: u64, const DENOM: u64> ops::Mul<u32> for Duration<$i, NOM, DENOM> {
843 type Output = Self;
844
845 #[inline]
846 #[track_caller]
847 fn mul(mut self, other: u32) -> Self::Output {
848 self.ticks *= other as $i;
849 self
850 }
851 }
852
853 impl<const NOM: u64, const DENOM: u64> ops::MulAssign<u32>
855 for Duration<$i, NOM, DENOM>
856 {
857 #[inline]
858 #[track_caller]
859 fn mul_assign(&mut self, other: u32) {
860 *self = *self * other;
861 }
862 }
863
864 impl<const NOM: u64, const DENOM: u64> ops::Div<u32> for Duration<$i, NOM, DENOM> {
866 type Output = Self;
867
868 #[inline]
869 #[track_caller]
870 fn div(mut self, other: u32) -> Self::Output {
871 self.ticks /= other as $i;
872 self
873 }
874 }
875
876 impl<const NOM: u64, const DENOM: u64> ops::DivAssign<u32>
878 for Duration<$i, NOM, DENOM>
879 {
880 #[inline]
881 #[track_caller]
882 fn div_assign(&mut self, other: u32) {
883 *self = *self / other;
884 }
885 }
886
887 impl<const NOM: u64, const DENOM: u64> ops::Rem for Duration<$i, NOM, DENOM>
890 {
891 type Output = Self;
892
893 #[inline]
894 #[track_caller]
895 fn rem(self, other: Self) -> Self::Output {
896 if let Some(v) = self.checked_rem(other) {
897 v
898 } else {
899 panic!("Rem failed!");
900 }
901 }
902 }
903
904 impl<const NOM: u64, const DENOM: u64> ops::RemAssign for Duration<$i, NOM, DENOM>
906 {
907 #[inline]
908 #[track_caller]
909 fn rem_assign(&mut self, other: Self) {
910 *self = *self % other;
911 }
912 }
913
914 impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64> ops::Div<Duration<$i, R_NOM, R_DENOM>>
916 for Duration<$i, L_NOM, L_DENOM>
917 {
918 type Output = $i;
919
920 #[inline]
921 #[track_caller]
922 fn div(self, other: Duration<$i, R_NOM, R_DENOM>) -> Self::Output {
923 let conv: Duration<$i, R_NOM, R_DENOM> = self.convert();
924 conv.ticks / other.ticks
925 }
926 }
927
928 #[cfg(feature = "defmt")]
929 impl<const NOM: u64, const DENOM: u64> defmt::Format for Duration<$i, NOM, DENOM>
930 {
931 fn format(&self, f: defmt::Formatter) {
932 if NOM == 3_600 && DENOM == 1 {
933 defmt::write!(f, "{} h", self.ticks)
934 } else if NOM == 60 && DENOM == 1 {
935 defmt::write!(f, "{} min", self.ticks)
936 } else if NOM == 1 && DENOM == 1 {
937 defmt::write!(f, "{} s", self.ticks)
938 } else if NOM == 1 && DENOM == 1_000 {
939 defmt::write!(f, "{} ms", self.ticks)
940 } else if NOM == 1 && DENOM == 1_000_000 {
941 defmt::write!(f, "{} us", self.ticks)
942 } else if NOM == 1 && DENOM == 1_000_000_000 {
943 defmt::write!(f, "{} ns", self.ticks)
944 } else {
945 defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
946 }
947 }
948 }
949
950 impl<const NOM: u64, const DENOM: u64> core::fmt::Display for Duration<$i, NOM, DENOM> {
951 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
952 if NOM == 3_600 && DENOM == 1 {
953 write!(f, "{} h", self.ticks)
954 } else if NOM == 60 && DENOM == 1 {
955 write!(f, "{} min", self.ticks)
956 } else if NOM == 1 && DENOM == 1 {
957 write!(f, "{} s", self.ticks)
958 } else if NOM == 1 && DENOM == 1_000 {
959 write!(f, "{} ms", self.ticks)
960 } else if NOM == 1 && DENOM == 1_000_000 {
961 write!(f, "{} us", self.ticks)
962 } else if NOM == 1 && DENOM == 1_000_000_000 {
963 write!(f, "{} ns", self.ticks)
964 } else {
965 write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
966 }
967 }
968 }
969 };
970}
971
972impl_duration_for_integer!(u32);
973impl_duration_for_integer!(u64);
974
975impl<const NOM: u64, const DENOM: u64> convert::TryFrom<core::time::Duration>
980 for Duration<u32, NOM, DENOM>
981{
982 type Error = ();
983
984 #[inline]
985 fn try_from(val: core::time::Duration) -> Result<Self, Self::Error> {
986 Duration::<u64, NOM, DENOM>::try_from(val)?.try_into()
987 }
988}
989
990impl<const NOM: u64, const DENOM: u64> convert::TryFrom<core::time::Duration>
991 for Duration<u64, NOM, DENOM>
992{
993 type Error = ();
994
995 #[inline]
996 fn try_from(val: core::time::Duration) -> Result<Self, Self::Error> {
997 let secs_duration = SecsDurationU64::from_ticks(val.as_secs());
998 let nanos_duration = NanosDurationU64::from_ticks(val.subsec_nanos() as u64);
999
1000 let secs_converted: Self = secs_duration.const_try_into().ok_or(())?;
1001 let nanos_converted: Self = nanos_duration.const_try_into().ok_or(())?;
1002
1003 secs_converted.checked_add(nanos_converted).ok_or(())
1004 }
1005}
1006
1007impl<const NOM: u64, const DENOM: u64> From<Duration<u32, NOM, DENOM>> for core::time::Duration {
1012 #[inline]
1013 fn from(val: Duration<u32, NOM, DENOM>) -> Self {
1014 let val_u64: Duration<u64, NOM, DENOM> = val.into();
1015 core::time::Duration::from(val_u64)
1016 }
1017}
1018
1019impl<const NOM: u64, const DENOM: u64> From<Duration<u64, NOM, DENOM>> for core::time::Duration {
1020 #[inline]
1021 fn from(val: Duration<u64, NOM, DENOM>) -> Self {
1022 let secs = val.as_secs();
1023 let secs_duration = Duration::<u64, NOM, DENOM>::from_secs(secs);
1024 let remainder = val.saturating_sub(secs_duration);
1025 let nanos = remainder.as_nanos() as u32;
1026
1027 core::time::Duration::new(secs, nanos)
1028 }
1029}
1030
1031impl<const NOM: u64, const DENOM: u64> From<Duration<u32, NOM, DENOM>>
1036 for Duration<u64, NOM, DENOM>
1037{
1038 #[inline]
1039 fn from(val: Duration<u32, NOM, DENOM>) -> Self {
1040 Duration::<u64, NOM, DENOM>::from_ticks(val.as_ticks() as u64)
1041 }
1042}
1043
1044impl<const NOM: u64, const DENOM: u64> convert::TryFrom<Duration<u64, NOM, DENOM>>
1045 for Duration<u32, NOM, DENOM>
1046{
1047 type Error = ();
1048
1049 #[inline]
1050 fn try_from(val: Duration<u64, NOM, DENOM>) -> Result<Self, ()> {
1051 Ok(Duration::<u32, NOM, DENOM>::from_ticks(
1052 val.as_ticks().try_into().map_err(|_| ())?,
1053 ))
1054 }
1055}
1056
1057impl<const NOM: u64, const DENOM: u64> ops::Sub<Duration<u32, NOM, DENOM>>
1060 for Duration<u64, NOM, DENOM>
1061{
1062 type Output = Self;
1063
1064 #[inline]
1065 #[track_caller]
1066 fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
1067 if let Some(v) = self.checked_sub(Self::from_ticks(other.as_ticks() as u64)) {
1068 v
1069 } else {
1070 panic!("Sub failed!");
1071 }
1072 }
1073}
1074
1075impl<const NOM: u64, const DENOM: u64> ops::SubAssign<Duration<u32, NOM, DENOM>>
1077 for Duration<u64, NOM, DENOM>
1078{
1079 #[inline]
1080 #[track_caller]
1081 fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
1082 *self = *self - other;
1083 }
1084}
1085
1086impl<const NOM: u64, const DENOM: u64> ops::Add<Duration<u32, NOM, DENOM>>
1089 for Duration<u64, NOM, DENOM>
1090{
1091 type Output = Self;
1092
1093 #[inline]
1094 #[track_caller]
1095 fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
1096 if let Some(v) = self.checked_add(Self::from_ticks(other.as_ticks() as u64)) {
1097 v
1098 } else {
1099 panic!("Add failed!");
1100 }
1101 }
1102}
1103
1104impl<const NOM: u64, const DENOM: u64> ops::AddAssign<Duration<u32, NOM, DENOM>>
1106 for Duration<u64, NOM, DENOM>
1107{
1108 #[inline]
1109 #[track_caller]
1110 fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
1111 *self = *self + other;
1112 }
1113}
1114
1115impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64>
1116 PartialOrd<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
1117{
1118 #[inline]
1119 fn partial_cmp(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> Option<Ordering> {
1120 self.partial_cmp(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
1121 other.as_ticks() as u64,
1122 ))
1123 }
1124}
1125
1126impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64>
1127 PartialEq<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
1128{
1129 #[inline]
1130 fn eq(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> bool {
1131 self.eq(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
1132 other.as_ticks() as u64,
1133 ))
1134 }
1135}
1136
1137impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64>
1138 PartialOrd<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
1139{
1140 #[inline]
1141 fn partial_cmp(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> Option<Ordering> {
1142 Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).partial_cmp(other)
1143 }
1144}
1145
1146impl<const L_NOM: u64, const L_DENOM: u64, const R_NOM: u64, const R_DENOM: u64>
1147 PartialEq<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
1148{
1149 #[inline]
1150 fn eq(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> bool {
1151 Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).eq(other)
1152 }
1153}
1154
1155pub trait ExtU32 {
1157 fn picos<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1159
1160 fn nanos<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1162
1163 fn micros<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1165
1166 fn millis<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1168
1169 fn secs<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1171
1172 fn minutes<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1174
1175 fn hours<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1177}
1178
1179impl ExtU32 for u32 {
1180 #[inline]
1181 fn picos<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1182 Duration::<u32, NOM, DENOM>::from_picos(self)
1183 }
1184
1185 #[inline]
1186 fn nanos<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1187 Duration::<u32, NOM, DENOM>::from_nanos(self)
1188 }
1189
1190 #[inline]
1191 fn micros<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1192 Duration::<u32, NOM, DENOM>::from_micros(self)
1193 }
1194
1195 #[inline]
1196 fn millis<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1197 Duration::<u32, NOM, DENOM>::from_millis(self)
1198 }
1199
1200 #[inline]
1201 fn secs<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1202 Duration::<u32, NOM, DENOM>::from_secs(self)
1203 }
1204
1205 #[inline]
1206 fn minutes<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1207 Duration::<u32, NOM, DENOM>::from_minutes(self)
1208 }
1209
1210 #[inline]
1211 fn hours<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1212 Duration::<u32, NOM, DENOM>::from_hours(self)
1213 }
1214}
1215
1216pub trait ExtU32Ceil {
1218 fn picos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1220
1221 fn nanos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1223
1224 fn micros_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1226
1227 fn millis_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1229
1230 fn secs_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1232
1233 fn minutes_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1235
1236 fn hours_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM>;
1238}
1239
1240impl ExtU32Ceil for u32 {
1241 #[inline]
1242 fn picos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1243 Duration::<u32, NOM, DENOM>::from_picos_at_least(self)
1244 }
1245
1246 #[inline]
1247 fn nanos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1248 Duration::<u32, NOM, DENOM>::from_nanos_at_least(self)
1249 }
1250
1251 #[inline]
1252 fn micros_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1253 Duration::<u32, NOM, DENOM>::from_micros_at_least(self)
1254 }
1255
1256 #[inline]
1257 fn millis_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1258 Duration::<u32, NOM, DENOM>::from_millis_at_least(self)
1259 }
1260
1261 #[inline]
1262 fn secs_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1263 Duration::<u32, NOM, DENOM>::from_secs_at_least(self)
1264 }
1265
1266 #[inline]
1267 fn minutes_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1268 Duration::<u32, NOM, DENOM>::from_minutes_at_least(self)
1269 }
1270
1271 #[inline]
1272 fn hours_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u32, NOM, DENOM> {
1273 Duration::<u32, NOM, DENOM>::from_hours_at_least(self)
1274 }
1275}
1276
1277pub trait ExtU64 {
1279 fn picos<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1281
1282 fn nanos<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1284
1285 fn micros<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1287
1288 fn millis<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1290
1291 fn secs<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1293
1294 fn minutes<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1296
1297 fn hours<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1299}
1300
1301impl ExtU64 for u64 {
1302 #[inline]
1303 fn picos<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1304 Duration::<u64, NOM, DENOM>::from_picos(self)
1305 }
1306
1307 #[inline]
1308 fn nanos<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1309 Duration::<u64, NOM, DENOM>::from_nanos(self)
1310 }
1311
1312 #[inline]
1313 fn micros<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1314 Duration::<u64, NOM, DENOM>::from_micros(self)
1315 }
1316
1317 #[inline]
1318 fn millis<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1319 Duration::<u64, NOM, DENOM>::from_millis(self)
1320 }
1321
1322 #[inline]
1323 fn secs<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1324 Duration::<u64, NOM, DENOM>::from_secs(self)
1325 }
1326
1327 #[inline]
1328 fn minutes<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1329 Duration::<u64, NOM, DENOM>::from_minutes(self)
1330 }
1331
1332 #[inline]
1333 fn hours<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1334 Duration::<u64, NOM, DENOM>::from_hours(self)
1335 }
1336}
1337
1338pub trait ExtU64Ceil {
1340 fn picos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1342
1343 fn nanos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1345
1346 fn micros_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1348
1349 fn millis_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1351
1352 fn secs_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1354
1355 fn minutes_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1357
1358 fn hours_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM>;
1360}
1361
1362impl ExtU64Ceil for u64 {
1363 #[inline]
1364 fn picos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1365 Duration::<u64, NOM, DENOM>::from_picos_at_least(self)
1366 }
1367
1368 #[inline]
1369 fn nanos_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1370 Duration::<u64, NOM, DENOM>::from_nanos_at_least(self)
1371 }
1372
1373 #[inline]
1374 fn micros_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1375 Duration::<u64, NOM, DENOM>::from_micros_at_least(self)
1376 }
1377
1378 #[inline]
1379 fn millis_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1380 Duration::<u64, NOM, DENOM>::from_millis_at_least(self)
1381 }
1382
1383 #[inline]
1384 fn secs_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1385 Duration::<u64, NOM, DENOM>::from_secs_at_least(self)
1386 }
1387
1388 #[inline]
1389 fn minutes_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1390 Duration::<u64, NOM, DENOM>::from_minutes_at_least(self)
1391 }
1392
1393 #[inline]
1394 fn hours_at_least<const NOM: u64, const DENOM: u64>(self) -> Duration<u64, NOM, DENOM> {
1395 Duration::<u64, NOM, DENOM>::from_hours_at_least(self)
1396 }
1397}