Skip to main content

bacnet_time/
duration.rs

1//! [`Duration`]: a length of time in nanoseconds.
2
3use core::ops::{Add, AddAssign, Sub, SubAssign};
4
5use crate::divide::div_rem;
6use crate::{NANOS_PER_MICRO, NANOS_PER_MILLI, NANOS_PER_SEC};
7
8/// A length of time, in nanoseconds on a `u64`: at most about 584 years.
9///
10/// `+` and `-` saturate at [`Duration::MAX`] and at zero. The default is
11/// [`Duration::ZERO`].
12///
13/// The `try_from_*` constructors return `None` where the others saturate:
14/// the `try_` prefix on an `Option`, as chrono's `TimeDelta::try_seconds`
15/// has it.
16#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Duration(u64);
18
19impl Duration {
20    /// No time.
21    pub const ZERO: Self = Self(0);
22    /// The longest representable duration, `u64::MAX` nanoseconds: about
23    /// 584 years.
24    pub const MAX: Self = Self(u64::MAX);
25
26    /// `nanos` nanoseconds.
27    #[must_use]
28    pub const fn from_nanos(nanos: u64) -> Self {
29        Self(nanos)
30    }
31
32    /// `micros` microseconds, saturating at [`Duration::MAX`].
33    #[must_use]
34    pub const fn from_micros(micros: u64) -> Self {
35        Self(micros.saturating_mul(NANOS_PER_MICRO))
36    }
37
38    /// `millis` milliseconds, saturating at [`Duration::MAX`].
39    #[must_use]
40    pub const fn from_millis(millis: u64) -> Self {
41        Self(millis.saturating_mul(NANOS_PER_MILLI))
42    }
43
44    /// `secs` seconds, saturating at [`Duration::MAX`].
45    #[must_use]
46    pub const fn from_secs(secs: u64) -> Self {
47        Self(secs.saturating_mul(NANOS_PER_SEC))
48    }
49
50    /// `micros` microseconds, or `None` past [`Duration::MAX`].
51    #[must_use]
52    pub const fn try_from_micros(micros: u64) -> Option<Self> {
53        match micros.checked_mul(NANOS_PER_MICRO) {
54            Some(nanos) => Some(Self(nanos)),
55            None => None,
56        }
57    }
58
59    /// `millis` milliseconds, or `None` past [`Duration::MAX`].
60    #[must_use]
61    pub const fn try_from_millis(millis: u64) -> Option<Self> {
62        match millis.checked_mul(NANOS_PER_MILLI) {
63            Some(nanos) => Some(Self(nanos)),
64            None => None,
65        }
66    }
67
68    /// `secs` seconds, or `None` past [`Duration::MAX`].
69    #[must_use]
70    pub const fn try_from_secs(secs: u64) -> Option<Self> {
71        match secs.checked_mul(NANOS_PER_SEC) {
72            Some(nanos) => Some(Self(nanos)),
73            None => None,
74        }
75    }
76
77    /// The length in nanoseconds.
78    #[must_use]
79    pub const fn as_nanos(self) -> u64 {
80        self.0
81    }
82
83    /// Whether the length is zero.
84    #[must_use]
85    pub const fn is_zero(self) -> bool {
86        self.0 == 0
87    }
88
89    /// The whole microseconds in the length, rounded toward zero.
90    #[must_use]
91    pub const fn as_micros(self) -> u64 {
92        div_rem(self.0, NANOS_PER_MICRO).0
93    }
94
95    /// The whole milliseconds in the length, rounded toward zero.
96    #[must_use]
97    pub const fn as_millis(self) -> u64 {
98        div_rem(self.0, NANOS_PER_MILLI).0
99    }
100
101    /// The whole seconds in the length, rounded toward zero.
102    #[must_use]
103    pub const fn as_secs(self) -> u64 {
104        div_rem(self.0, NANOS_PER_SEC).0
105    }
106
107    /// `self + other`, or `None` past [`Duration::MAX`].
108    #[must_use]
109    pub const fn checked_add(self, other: Self) -> Option<Self> {
110        match self.0.checked_add(other.0) {
111            Some(nanos) => Some(Self(nanos)),
112            None => None,
113        }
114    }
115
116    /// `self + other`, clamped at [`Duration::MAX`].
117    #[must_use]
118    pub const fn saturating_add(self, other: Self) -> Self {
119        Self(self.0.saturating_add(other.0))
120    }
121
122    /// `self - other`, or `None` below zero.
123    #[must_use]
124    pub const fn checked_sub(self, other: Self) -> Option<Self> {
125        match self.0.checked_sub(other.0) {
126            Some(nanos) => Some(Self(nanos)),
127            None => None,
128        }
129    }
130
131    /// `self - other`, clamped at zero.
132    #[must_use]
133    pub const fn saturating_sub(self, other: Self) -> Self {
134        Self(self.0.saturating_sub(other.0))
135    }
136
137    /// `self` times `factor`, or `None` past [`Duration::MAX`].
138    #[must_use]
139    pub const fn checked_mul(self, factor: u32) -> Option<Self> {
140        match self.0.checked_mul(factor as u64) {
141            Some(nanos) => Some(Self(nanos)),
142            None => None,
143        }
144    }
145
146    /// `self` times `factor`, clamped at [`Duration::MAX`].
147    #[must_use]
148    pub const fn saturating_mul(self, factor: u32) -> Self {
149        Self(self.0.saturating_mul(factor as u64))
150    }
151
152    /// The shorter of `self` and `other`: [`Ord::min`] for a `const`
153    /// context.
154    #[must_use]
155    pub const fn min(self, other: Self) -> Self {
156        if self.0 <= other.0 {
157            self
158        } else {
159            other
160        }
161    }
162
163    /// The longer of `self` and `other`: [`Ord::max`] for a `const`
164    /// context.
165    #[must_use]
166    pub const fn max(self, other: Self) -> Self {
167        if self.0 >= other.0 {
168            self
169        } else {
170            other
171        }
172    }
173
174    /// What is left of `self` after taking out as many whole `divisor`s as
175    /// fit, or `None` for a zero `divisor`: the offset into a repeating
176    /// cycle, say.
177    ///
178    /// It takes one step per bit of the quotient, without a 64-bit division
179    /// routine (see the [crate docs](crate#no-64-bit-division)).
180    #[must_use]
181    pub const fn checked_rem(self, divisor: Self) -> Option<Self> {
182        if divisor.0 == 0 {
183            None
184        } else {
185            Some(Self(div_rem(self.0, divisor.0).1))
186        }
187    }
188}
189
190impl Add for Duration {
191    type Output = Self;
192
193    /// Saturating, as [`Duration::saturating_add`].
194    fn add(self, rhs: Self) -> Self {
195        self.saturating_add(rhs)
196    }
197}
198
199impl AddAssign for Duration {
200    /// Saturating, as [`Duration::saturating_add`].
201    fn add_assign(&mut self, rhs: Self) {
202        *self = self.saturating_add(rhs);
203    }
204}
205
206impl Sub for Duration {
207    type Output = Self;
208
209    /// Saturating, as [`Duration::saturating_sub`].
210    fn sub(self, rhs: Self) -> Self {
211        self.saturating_sub(rhs)
212    }
213}
214
215impl SubAssign for Duration {
216    /// Saturating, as [`Duration::saturating_sub`].
217    fn sub_assign(&mut self, rhs: Self) {
218        *self = self.saturating_sub(rhs);
219    }
220}