1use core::{
4 fmt::Display,
5 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
6};
7
8use az::Az;
9use fixed::{
10 traits::{LossyInto, ToFixed},
11 types::I96F32,
12};
13
14use super::Interval;
15use crate::datastructures::common::TimeInterval;
16#[allow(unused_imports)]
17use crate::float_polyfill::FloatPolyfill;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
24pub struct Duration {
25 inner: I96F32,
27}
28
29impl Duration {
30 pub const ZERO: Duration = Duration {
32 inner: I96F32::ZERO,
33 };
34
35 pub fn from_seconds(secs: f64) -> Self {
37 let inner = secs.az::<I96F32>() * 1_000_000_000.to_fixed::<I96F32>();
38 Self { inner }
39 }
40
41 pub fn from_secs(secs: i64) -> Self {
43 let inner = secs.to_fixed::<I96F32>() * 1_000_000_000.to_fixed::<I96F32>();
44 Self { inner }
45 }
46
47 pub fn from_millis(millis: i64) -> Self {
49 let inner = millis.to_fixed::<I96F32>() * 1_000_000.to_fixed::<I96F32>();
50 Self { inner }
51 }
52 pub fn from_micros(micros: i64) -> Self {
54 let inner = micros.to_fixed::<I96F32>() * 1_000.to_fixed::<I96F32>();
55 Self { inner }
56 }
57 pub fn from_nanos(nanos: i64) -> Self {
59 let inner = nanos.to_fixed::<I96F32>();
60 Self { inner }
61 }
62
63 pub fn from_fixed_nanos<F: ToFixed>(nanos: F) -> Self {
66 Self {
67 inner: nanos.to_fixed(),
68 }
69 }
70
71 pub fn nanos(&self) -> I96F32 {
73 self.inner
74 }
75
76 pub fn nanos_rounded(&self) -> i128 {
78 self.nanos().lossy_into()
79 }
80
81 pub fn nanos_lossy(&self) -> f64 {
83 self.nanos().lossy_into()
84 }
85
86 pub fn secs(&self) -> i64 {
88 (self.inner / 1_000_000_000.to_fixed::<I96F32>()).to_num()
89 }
90
91 pub fn seconds(&self) -> f64 {
93 self.inner.az::<f64>() / 1e9
94 }
95
96 pub fn from_log_interval(log_interval: i8) -> Self {
98 let seconds = 2.0f64.powi(log_interval as i32);
99 let nanos = seconds * 1_000_000_000.0;
100 Self::from_fixed_nanos(nanos)
101 }
102
103 pub fn from_interval(interval: Interval) -> Self {
105 let seconds = interval.seconds();
106 let nanos = seconds * 1_000_000_000.0;
107 Self::from_fixed_nanos(nanos)
108 }
109
110 pub fn abs(self) -> Duration {
112 Duration::from_fixed_nanos(self.nanos().abs())
113 }
114}
115
116#[cfg(feature = "serde")]
117impl<'de> serde::Deserialize<'de> for Duration {
118 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119 where
120 D: serde::Deserializer<'de>,
121 {
122 Ok(Duration {
123 inner: I96F32::from_bits(i128::deserialize(deserializer)?),
124 })
125 }
126}
127
128#[cfg(feature = "serde")]
129impl serde::Serialize for Duration {
130 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131 where
132 S: serde::Serializer,
133 {
134 serializer.serialize_i128(self.inner.to_bits())
135 }
136}
137
138impl From<TimeInterval> for Duration {
139 fn from(interval: TimeInterval) -> Self {
140 Self::from_fixed_nanos(interval.0)
141 }
142}
143
144impl From<Duration> for core::time::Duration {
145 fn from(value: Duration) -> Self {
146 core::time::Duration::from_nanos(value.nanos().saturating_to_num())
147 }
148}
149
150impl Neg for Duration {
151 type Output = Duration;
152
153 fn neg(self) -> Self::Output {
154 Self::from_fixed_nanos(-self.nanos())
155 }
156}
157
158impl Add for Duration {
159 type Output = Duration;
160
161 fn add(self, rhs: Duration) -> Self::Output {
162 Duration {
163 inner: self.nanos() + rhs.nanos(),
164 }
165 }
166}
167
168impl AddAssign for Duration {
169 fn add_assign(&mut self, rhs: Duration) {
170 *self = *self + rhs;
171 }
172}
173
174impl Sub for Duration {
175 type Output = Duration;
176
177 fn sub(self, rhs: Duration) -> Self::Output {
178 self + -rhs
179 }
180}
181
182impl SubAssign for Duration {
183 fn sub_assign(&mut self, rhs: Duration) {
184 *self = *self - rhs;
185 }
186}
187
188impl<TF: ToFixed> Mul<TF> for Duration {
189 type Output = Duration;
190
191 fn mul(self, rhs: TF) -> Self::Output {
192 Duration::from_fixed_nanos(self.nanos() * rhs.to_fixed::<I96F32>())
193 }
194}
195
196impl<TF: ToFixed> MulAssign<TF> for Duration {
197 fn mul_assign(&mut self, rhs: TF) {
198 *self = *self * rhs
199 }
200}
201
202impl<TF: ToFixed> Div<TF> for Duration {
203 type Output = Duration;
204
205 fn div(self, rhs: TF) -> Self::Output {
206 Duration::from_fixed_nanos(self.nanos() / rhs.to_fixed::<I96F32>())
207 }
208}
209
210impl<TF: ToFixed> DivAssign<TF> for Duration {
211 fn div_assign(&mut self, rhs: TF) {
212 *self = *self / rhs
213 }
214}
215
216impl Rem for Duration {
217 type Output = Duration;
218
219 fn rem(self, rhs: Self) -> Self::Output {
220 Duration::from_fixed_nanos(self.nanos() % rhs.nanos())
221 }
222}
223
224impl RemAssign for Duration {
225 fn rem_assign(&mut self, rhs: Self) {
226 *self = *self % rhs
227 }
228}
229
230impl Display for Duration {
231 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232 write!(f, "{}", self.inner)
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn values() {
242 assert_eq!(
243 Duration::from_secs(10).nanos(),
244 10_000_000_000u64.to_fixed::<I96F32>()
245 );
246 assert_eq!(
247 Duration::from_secs(-10).nanos(),
248 -(10_000_000_000u64.to_fixed::<I96F32>())
249 );
250 assert_eq!(
251 Duration::from_millis(10).nanos(),
252 10_000_000u64.to_fixed::<I96F32>()
253 );
254 assert_eq!(
255 Duration::from_micros(10).nanos(),
256 10_000u64.to_fixed::<I96F32>()
257 );
258 assert_eq!(Duration::from_nanos(10).nanos(), 10u64.to_fixed::<I96F32>());
259 assert_eq!(
260 Duration::from_fixed_nanos(10.123f64).nanos(),
261 10.123f64.to_fixed::<I96F32>()
262 );
263 assert_eq!(Duration::from_secs(10).secs(), 10);
264 assert_eq!(Duration::from_millis(10).secs(), 0);
265 assert_eq!(Duration::from_millis(1001).secs(), 1);
266 }
267
268 #[test]
269 fn log_interval() {
270 assert_eq!(Duration::from_log_interval(0), Duration::from_secs(1));
271 assert_eq!(Duration::from_log_interval(-1), Duration::from_millis(500));
272 assert_eq!(Duration::from_log_interval(1), Duration::from_secs(2));
273 }
274
275 #[test]
276 fn interval() {
277 assert_eq!(
278 Duration::from_fixed_nanos(2.25f64),
279 Duration::from(TimeInterval(2.25f64.to_fixed()))
280 );
281 assert_eq!(
282 TimeInterval(2.25f64.to_fixed()),
283 Duration::from_fixed_nanos(2.25f64).into()
284 );
285 }
286}