bacnet_time/convert.rs
1//! Conversions to and from `core::time::Duration`.
2
3use core::fmt;
4
5use crate::divide::div_rem;
6use crate::{Duration, NANOS_PER_SEC};
7
8/// A `core::time::Duration` too long for a [`Duration`]: more than
9/// `u64::MAX` nanoseconds, about 584 years. What
10/// `Duration::try_from(core::time::Duration)` returns for one;
11/// [`Duration::saturating_from_core`] clamps it instead.
12///
13/// Only the conversion builds one, as with the standard library's
14/// `TryFromIntError`, so the type can carry more detail later.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct DurationTooLong(());
17
18impl fmt::Display for DurationTooLong {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 f.write_str("duration longer than u64::MAX nanoseconds (about 584 years)")
21 }
22}
23
24impl core::error::Error for DurationTooLong {}
25
26impl Duration {
27 /// `duration` in nanoseconds, or `None` past [`Duration::MAX`].
28 const fn checked_from_core(duration: core::time::Duration) -> Option<Self> {
29 match duration.as_secs().checked_mul(NANOS_PER_SEC) {
30 Some(whole) => match whole.checked_add(duration.subsec_nanos() as u64) {
31 Some(nanos) => Some(Self::from_nanos(nanos)),
32 None => None,
33 },
34 None => None,
35 }
36 }
37
38 /// `duration`, clamped at [`Duration::MAX`], about 584 years: for a
39 /// host mapping its own clock onto the timeline, where a time that far
40 /// out means never. A length that must fit, from configuration say,
41 /// goes through `Duration::try_from` instead, which refuses one too long.
42 #[must_use]
43 pub const fn saturating_from_core(duration: core::time::Duration) -> Self {
44 match Self::checked_from_core(duration) {
45 Some(duration) => duration,
46 None => Self::MAX,
47 }
48 }
49}
50
51impl TryFrom<core::time::Duration> for Duration {
52 type Error = DurationTooLong;
53
54 /// The same length, or [`DurationTooLong`] for one past
55 /// [`Duration::MAX`]: a `core::time::Duration` reaches about 584
56 /// billion years.
57 fn try_from(duration: core::time::Duration) -> Result<Self, DurationTooLong> {
58 Self::checked_from_core(duration).ok_or(DurationTooLong(()))
59 }
60}
61
62impl From<Duration> for core::time::Duration {
63 /// The same length, exactly. Splitting it into seconds and nanoseconds
64 /// takes no 64-bit division routine (see the
65 /// [crate docs](crate#no-64-bit-division)).
66 fn from(duration: Duration) -> Self {
67 let (secs, nanos) = div_rem(duration.as_nanos(), NANOS_PER_SEC);
68 // The remainder is under one second, so it fits a u32.
69 Self::new(secs, nanos as u32)
70 }
71}