statime/time/
duration.rs

1//! Implementation of the [Duration] type
2
3use 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/// A duration is a span of time that can also be negative.
20///
21/// For example, the difference between two instants is a duration.
22/// And an instant plus a duration is another instant.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
24pub struct Duration {
25    /// Time in nanos
26    inner: I96F32,
27}
28
29impl Duration {
30    /// A duration of zero seconds
31    pub const ZERO: Duration = Duration {
32        inner: I96F32::ZERO,
33    };
34
35    /// Create an instance with the given amount of seconds
36    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    /// Create an instance with the given amount of seconds
42    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    /// Create an instance with the given amount of milliseconds
48    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    /// Create an instance with the given amount of microseconds
53    pub fn from_micros(micros: i64) -> Self {
54        let inner = micros.to_fixed::<I96F32>() * 1_000.to_fixed::<I96F32>();
55        Self { inner }
56    }
57    /// Create an instance with the given amount of nanoseconds
58    pub fn from_nanos(nanos: i64) -> Self {
59        let inner = nanos.to_fixed::<I96F32>();
60        Self { inner }
61    }
62
63    /// Create an instance with the given amount of nanoseconds, using a fixed
64    /// point number so the subnanoseconds can be specified as well
65    pub fn from_fixed_nanos<F: ToFixed>(nanos: F) -> Self {
66        Self {
67            inner: nanos.to_fixed(),
68        }
69    }
70
71    /// Get the total amount of nanoseconds
72    pub fn nanos(&self) -> I96F32 {
73        self.inner
74    }
75
76    /// Get the total amount of nanoseconds dropping any sub nanosecond parts
77    pub fn nanos_rounded(&self) -> i128 {
78        self.nanos().lossy_into()
79    }
80
81    /// Get the total amount of nanoseconds, losing some precision
82    pub fn nanos_lossy(&self) -> f64 {
83        self.nanos().lossy_into()
84    }
85
86    /// Get the total amount of seconds
87    pub fn secs(&self) -> i64 {
88        (self.inner / 1_000_000_000.to_fixed::<I96F32>()).to_num()
89    }
90
91    /// Get the total amount of seconds
92    pub fn seconds(&self) -> f64 {
93        self.inner.az::<f64>() / 1e9
94    }
95
96    /// Converts a log interval (as defined by the PTP spec) to a duration
97    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    /// Converts a interval (as defined by the PTP spec) to a duration
104    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    /// Takes the absolute (non-negative) value of the duration
111    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}