1use core::fmt;
2use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
3
4use super::{GCD_1K, GCD_1M, TICK_HZ};
5use crate::GCD_1G;
6
7#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub struct Duration {
11 pub(crate) ticks: u64,
12}
13
14impl Duration {
15 pub const MIN: Duration = Duration { ticks: u64::MIN };
17 pub const MAX: Duration = Duration { ticks: u64::MAX };
19
20 pub const fn as_ticks(&self) -> u64 {
22 self.ticks
23 }
24
25 pub const fn as_secs(&self) -> u64 {
27 self.ticks / TICK_HZ
28 }
29
30 pub const fn as_millis(&self) -> u64 {
32 self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K)
33 }
34
35 pub const fn as_micros(&self) -> u64 {
37 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)
38 }
39
40 pub const fn as_nanos(&self) -> u64 {
42 self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G)
43 }
44
45 pub const fn from_ticks(ticks: u64) -> Duration {
47 Duration { ticks }
48 }
49
50 pub const fn from_secs(secs: u64) -> Duration {
52 Duration { ticks: secs * TICK_HZ }
53 }
54
55 pub const fn from_millis(millis: u64) -> Duration {
57 Duration {
58 ticks: div_ceil(millis * (TICK_HZ / GCD_1K), 1000 / GCD_1K),
59 }
60 }
61
62 pub const fn from_micros(micros: u64) -> Duration {
65 Duration {
66 ticks: div_ceil(micros * (TICK_HZ / GCD_1M), 1_000_000 / GCD_1M),
67 }
68 }
69
70 pub const fn from_nanos(nanoseconds: u64) -> Duration {
73 Duration {
74 ticks: div_ceil(nanoseconds * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G),
75 }
76 }
77
78 pub const fn from_secs_floor(secs: u64) -> Duration {
80 Duration { ticks: secs * TICK_HZ }
81 }
82
83 pub const fn from_millis_floor(millis: u64) -> Duration {
85 Duration {
86 ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),
87 }
88 }
89
90 pub const fn from_micros_floor(micros: u64) -> Duration {
93 Duration {
94 ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),
95 }
96 }
97
98 pub const fn try_from_secs(secs: u64) -> Option<Duration> {
101 let Some(ticks) = secs.checked_mul(TICK_HZ) else {
102 return None;
103 };
104 Some(Duration { ticks })
105 }
106
107 pub const fn try_from_millis(millis: u64) -> Option<Duration> {
110 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
111 return None;
112 };
113 Some(Duration {
114 ticks: div_ceil(value, 1000 / GCD_1K),
115 })
116 }
117
118 pub const fn try_from_micros(micros: u64) -> Option<Duration> {
122 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
123 return None;
124 };
125 Some(Duration {
126 ticks: div_ceil(value, 1_000_000 / GCD_1M),
127 })
128 }
129
130 pub const fn try_from_nanos(nanoseconds: u64) -> Option<Duration> {
134 let Some(value) = nanoseconds.checked_mul(TICK_HZ / GCD_1G) else {
135 return None;
136 };
137 Some(Duration {
138 ticks: div_ceil(value, 1_000_000_000 / GCD_1G),
139 })
140 }
141
142 pub const fn try_from_secs_floor(secs: u64) -> Option<Duration> {
145 let Some(ticks) = secs.checked_mul(TICK_HZ) else {
146 return None;
147 };
148 Some(Duration { ticks })
149 }
150
151 pub const fn try_from_millis_floor(millis: u64) -> Option<Duration> {
154 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
155 return None;
156 };
157 Some(Duration {
158 ticks: value / (1000 / GCD_1K),
159 })
160 }
161
162 pub const fn try_from_micros_floor(micros: u64) -> Option<Duration> {
166 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
167 return None;
168 };
169 Some(Duration {
170 ticks: value / (1_000_000 / GCD_1M),
171 })
172 }
173
174 pub const fn from_hz(hz: u64) -> Duration {
178 let ticks = { if hz >= TICK_HZ { 1 } else { (TICK_HZ + hz / 2) / hz } };
179 Duration { ticks }
180 }
181
182 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
184 self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
185 }
186
187 pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
189 self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })
190 }
191
192 pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
194 self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })
195 }
196
197 pub fn checked_div(self, rhs: u32) -> Option<Duration> {
199 self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })
200 }
201}
202
203impl Add for Duration {
204 type Output = Duration;
205
206 fn add(self, rhs: Duration) -> Duration {
207 self.checked_add(rhs).expect("overflow when adding durations")
208 }
209}
210
211impl AddAssign for Duration {
212 fn add_assign(&mut self, rhs: Duration) {
213 *self = *self + rhs;
214 }
215}
216
217impl Sub for Duration {
218 type Output = Duration;
219
220 fn sub(self, rhs: Duration) -> Duration {
221 self.checked_sub(rhs).expect("overflow when subtracting durations")
222 }
223}
224
225impl SubAssign for Duration {
226 fn sub_assign(&mut self, rhs: Duration) {
227 *self = *self - rhs;
228 }
229}
230
231impl Mul<u32> for Duration {
232 type Output = Duration;
233
234 fn mul(self, rhs: u32) -> Duration {
235 self.checked_mul(rhs)
236 .expect("overflow when multiplying duration by scalar")
237 }
238}
239
240impl Mul<Duration> for u32 {
241 type Output = Duration;
242
243 fn mul(self, rhs: Duration) -> Duration {
244 rhs * self
245 }
246}
247
248impl MulAssign<u32> for Duration {
249 fn mul_assign(&mut self, rhs: u32) {
250 *self = *self * rhs;
251 }
252}
253
254impl Div<u32> for Duration {
255 type Output = Duration;
256
257 fn div(self, rhs: u32) -> Duration {
258 self.checked_div(rhs)
259 .expect("divide by zero error when dividing duration by scalar")
260 }
261}
262
263impl DivAssign<u32> for Duration {
264 fn div_assign(&mut self, rhs: u32) {
265 *self = *self / rhs;
266 }
267}
268
269impl<'a> fmt::Display for Duration {
270 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
271 write!(f, "{} ticks", self.ticks)
272 }
273}
274
275#[inline]
276const fn div_ceil(num: u64, den: u64) -> u64 {
277 (num + den - 1) / den
278}
279
280impl TryFrom<core::time::Duration> for Duration {
281 type Error = <u64 as TryFrom<u128>>::Error;
282
283 fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {
285 Ok(Self::from_micros(value.as_micros().try_into()?))
286 }
287}
288
289impl From<Duration> for core::time::Duration {
290 fn from(value: Duration) -> Self {
292 core::time::Duration::from_micros(value.as_micros())
293 }
294}
295
296impl core::iter::Sum for Duration {
297 fn sum<I>(iter: I) -> Self
298 where
299 I: Iterator<Item = Duration>,
300 {
301 Duration::from_ticks(iter.map(|d| d.as_ticks()).sum())
302 }
303}