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 from_ticks(ticks: u64) -> Duration {
42 Duration { ticks }
43 }
44
45 pub const fn from_secs(secs: u64) -> Duration {
47 Duration { ticks: secs * TICK_HZ }
48 }
49
50 pub const fn from_millis(millis: u64) -> Duration {
52 Duration {
53 ticks: div_ceil(millis * (TICK_HZ / GCD_1K), 1000 / GCD_1K),
54 }
55 }
56
57 pub const fn from_micros(micros: u64) -> Duration {
60 Duration {
61 ticks: div_ceil(micros * (TICK_HZ / GCD_1M), 1_000_000 / GCD_1M),
62 }
63 }
64
65 pub const fn from_nanos(nanoseconds: u64) -> Duration {
68 Duration {
69 ticks: div_ceil(nanoseconds * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G),
70 }
71 }
72
73 pub const fn from_secs_floor(secs: u64) -> Duration {
75 Duration { ticks: secs * TICK_HZ }
76 }
77
78 pub const fn from_millis_floor(millis: u64) -> Duration {
80 Duration {
81 ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),
82 }
83 }
84
85 pub const fn from_micros_floor(micros: u64) -> Duration {
88 Duration {
89 ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),
90 }
91 }
92
93 pub const fn try_from_secs(secs: u64) -> Option<Duration> {
96 let Some(ticks) = secs.checked_mul(TICK_HZ) else {
97 return None;
98 };
99 Some(Duration { ticks })
100 }
101
102 pub const fn try_from_millis(millis: u64) -> Option<Duration> {
105 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
106 return None;
107 };
108 Some(Duration {
109 ticks: div_ceil(value, 1000 / GCD_1K),
110 })
111 }
112
113 pub const fn try_from_micros(micros: u64) -> Option<Duration> {
117 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
118 return None;
119 };
120 Some(Duration {
121 ticks: div_ceil(value, 1_000_000 / GCD_1M),
122 })
123 }
124
125 pub const fn try_from_nanos(nanoseconds: u64) -> Option<Duration> {
129 let Some(value) = nanoseconds.checked_mul(TICK_HZ / GCD_1G) else {
130 return None;
131 };
132 Some(Duration {
133 ticks: div_ceil(value, 1_000_000_000 / GCD_1G),
134 })
135 }
136
137 pub const fn try_from_secs_floor(secs: u64) -> Option<Duration> {
140 let Some(ticks) = secs.checked_mul(TICK_HZ) else {
141 return None;
142 };
143 Some(Duration { ticks })
144 }
145
146 pub const fn try_from_millis_floor(millis: u64) -> Option<Duration> {
149 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
150 return None;
151 };
152 Some(Duration {
153 ticks: value / (1000 / GCD_1K),
154 })
155 }
156
157 pub const fn try_from_micros_floor(micros: u64) -> Option<Duration> {
161 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
162 return None;
163 };
164 Some(Duration {
165 ticks: value / (1_000_000 / GCD_1M),
166 })
167 }
168
169 pub const fn from_hz(hz: u64) -> Duration {
173 let ticks = {
174 if hz >= TICK_HZ {
175 1
176 } else {
177 (TICK_HZ + hz / 2) / hz
178 }
179 };
180 Duration { ticks }
181 }
182
183 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
185 self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
186 }
187
188 pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
190 self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })
191 }
192
193 pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
195 self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })
196 }
197
198 pub fn checked_div(self, rhs: u32) -> Option<Duration> {
200 self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })
201 }
202}
203
204impl Add for Duration {
205 type Output = Duration;
206
207 fn add(self, rhs: Duration) -> Duration {
208 self.checked_add(rhs).expect("overflow when adding durations")
209 }
210}
211
212impl AddAssign for Duration {
213 fn add_assign(&mut self, rhs: Duration) {
214 *self = *self + rhs;
215 }
216}
217
218impl Sub for Duration {
219 type Output = Duration;
220
221 fn sub(self, rhs: Duration) -> Duration {
222 self.checked_sub(rhs).expect("overflow when subtracting durations")
223 }
224}
225
226impl SubAssign for Duration {
227 fn sub_assign(&mut self, rhs: Duration) {
228 *self = *self - rhs;
229 }
230}
231
232impl Mul<u32> for Duration {
233 type Output = Duration;
234
235 fn mul(self, rhs: u32) -> Duration {
236 self.checked_mul(rhs)
237 .expect("overflow when multiplying duration by scalar")
238 }
239}
240
241impl Mul<Duration> for u32 {
242 type Output = Duration;
243
244 fn mul(self, rhs: Duration) -> Duration {
245 rhs * self
246 }
247}
248
249impl MulAssign<u32> for Duration {
250 fn mul_assign(&mut self, rhs: u32) {
251 *self = *self * rhs;
252 }
253}
254
255impl Div<u32> for Duration {
256 type Output = Duration;
257
258 fn div(self, rhs: u32) -> Duration {
259 self.checked_div(rhs)
260 .expect("divide by zero error when dividing duration by scalar")
261 }
262}
263
264impl DivAssign<u32> for Duration {
265 fn div_assign(&mut self, rhs: u32) {
266 *self = *self / rhs;
267 }
268}
269
270impl<'a> fmt::Display for Duration {
271 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272 write!(f, "{} ticks", self.ticks)
273 }
274}
275
276#[inline]
277const fn div_ceil(num: u64, den: u64) -> u64 {
278 (num + den - 1) / den
279}
280
281impl TryFrom<core::time::Duration> for Duration {
282 type Error = <u64 as TryFrom<u128>>::Error;
283
284 fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {
286 Ok(Self::from_micros(value.as_micros().try_into()?))
287 }
288}
289
290impl From<Duration> for core::time::Duration {
291 fn from(value: Duration) -> Self {
293 core::time::Duration::from_micros(value.as_micros())
294 }
295}