Skip to main content

smoltcp/
time.rs

1/*! Time structures.
2
3The `time` module contains structures used to represent both
4absolute and relative time.
5
6 - [Instant] is used to represent absolute time.
7 - [Duration] is used to represent relative time.
8
9[Instant]: struct.Instant.html
10[Duration]: struct.Duration.html
11*/
12
13use core::{fmt, ops};
14
15/// A representation of an absolute time value.
16///
17/// The `Instant` type is a wrapper around a `i64` value that
18/// represents a number of microseconds, monotonically increasing
19/// since an arbitrary moment in time, such as system startup.
20///
21/// * A value of `0` is inherently arbitrary.
22/// * A value less than `0` indicates a time before the starting
23///   point.
24#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct Instant {
26    micros: i64,
27}
28
29impl Instant {
30    pub const ZERO: Instant = Instant::from_micros_const(0);
31
32    /// Create a new `Instant` from a number of microseconds.
33    pub fn from_micros<T: Into<i64>>(micros: T) -> Instant {
34        Instant {
35            micros: micros.into(),
36        }
37    }
38
39    pub const fn from_micros_const(micros: i64) -> Instant {
40        Instant { micros }
41    }
42
43    /// Create a new `Instant` from a number of milliseconds.
44    pub fn from_millis<T: Into<i64>>(millis: T) -> Instant {
45        Instant {
46            micros: millis.into() * 1000,
47        }
48    }
49
50    /// Create a new `Instant` from a number of milliseconds.
51    pub const fn from_millis_const(millis: i64) -> Instant {
52        Instant {
53            micros: millis * 1000,
54        }
55    }
56
57    /// Create a new `Instant` from a number of seconds.
58    pub fn from_secs<T: Into<i64>>(secs: T) -> Instant {
59        Instant {
60            micros: secs.into() * 1000000,
61        }
62    }
63
64    /// Create a new `Instant` from the current [std::time::SystemTime].
65    ///
66    /// See [std::time::SystemTime::now]
67    ///
68    /// [std::time::SystemTime]: https://doc.rust-lang.org/std/time/struct.SystemTime.html
69    /// [std::time::SystemTime::now]: https://doc.rust-lang.org/std/time/struct.SystemTime.html#method.now
70    #[cfg(feature = "std")]
71    pub fn now() -> Instant {
72        Self::from(::std::time::SystemTime::now())
73    }
74
75    /// The fractional number of milliseconds that have passed
76    /// since the beginning of time.
77    pub const fn millis(&self) -> i64 {
78        self.micros % 1000000 / 1000
79    }
80
81    /// The fractional number of microseconds that have passed
82    /// since the beginning of time.
83    pub const fn micros(&self) -> i64 {
84        self.micros % 1000000
85    }
86
87    /// The number of whole seconds that have passed since the
88    /// beginning of time.
89    pub const fn secs(&self) -> i64 {
90        self.micros / 1000000
91    }
92
93    /// The total number of milliseconds that have passed since
94    /// the beginning of time.
95    pub const fn total_millis(&self) -> i64 {
96        self.micros / 1000
97    }
98    /// The total number of milliseconds that have passed since
99    /// the beginning of time.
100    pub const fn total_micros(&self) -> i64 {
101        self.micros
102    }
103}
104
105#[cfg(feature = "std")]
106impl From<::std::time::Instant> for Instant {
107    fn from(other: ::std::time::Instant) -> Instant {
108        static REFERENTIAL: ::std::sync::LazyLock<::std::time::Instant> =
109            ::std::sync::LazyLock::new(::std::time::Instant::now);
110
111        let n = other.saturating_duration_since(*REFERENTIAL);
112        Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64)
113    }
114}
115
116#[cfg(feature = "std")]
117impl From<::std::time::SystemTime> for Instant {
118    fn from(other: ::std::time::SystemTime) -> Instant {
119        let n = other
120            .duration_since(::std::time::UNIX_EPOCH)
121            .expect("start time must not be before the unix epoch");
122        Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64)
123    }
124}
125
126#[cfg(feature = "std")]
127impl From<Instant> for ::std::time::SystemTime {
128    fn from(val: Instant) -> Self {
129        ::std::time::UNIX_EPOCH + ::std::time::Duration::from_micros(val.micros as u64)
130    }
131}
132
133impl fmt::Display for Instant {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        write!(f, "{}.{:0>3}s", self.secs(), self.millis())
136    }
137}
138
139#[cfg(feature = "defmt")]
140impl defmt::Format for Instant {
141    fn format(&self, f: defmt::Formatter) {
142        defmt::write!(f, "{}.{:03}s", self.secs(), self.millis());
143    }
144}
145
146impl ops::Add<Duration> for Instant {
147    type Output = Instant;
148
149    fn add(self, rhs: Duration) -> Instant {
150        Instant::from_micros(self.micros + rhs.total_micros() as i64)
151    }
152}
153
154impl ops::AddAssign<Duration> for Instant {
155    fn add_assign(&mut self, rhs: Duration) {
156        self.micros += rhs.total_micros() as i64;
157    }
158}
159
160impl ops::Sub<Duration> for Instant {
161    type Output = Instant;
162
163    fn sub(self, rhs: Duration) -> Instant {
164        Instant::from_micros(self.micros - rhs.total_micros() as i64)
165    }
166}
167
168impl ops::SubAssign<Duration> for Instant {
169    fn sub_assign(&mut self, rhs: Duration) {
170        self.micros -= rhs.total_micros() as i64;
171    }
172}
173
174impl ops::Sub<Instant> for Instant {
175    type Output = Duration;
176
177    fn sub(self, rhs: Instant) -> Duration {
178        Duration::from_micros((self.micros - rhs.micros).unsigned_abs())
179    }
180}
181
182/// A relative amount of time.
183#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
184pub struct Duration {
185    micros: u64,
186}
187
188impl Duration {
189    pub const ZERO: Duration = Duration::from_micros(0);
190    /// The longest possible duration we can encode.
191    pub const MAX: Duration = Duration::from_micros(u64::MAX);
192    /// Create a new `Duration` from a number of microseconds.
193    pub const fn from_micros(micros: u64) -> Duration {
194        Duration { micros }
195    }
196
197    /// Create a new `Duration` from a number of milliseconds.
198    pub const fn from_millis(millis: u64) -> Duration {
199        Duration {
200            micros: millis * 1000,
201        }
202    }
203
204    /// Create a new `Duration` from a number of seconds.
205    pub const fn from_secs(secs: u64) -> Duration {
206        Duration {
207            micros: secs * 1000000,
208        }
209    }
210
211    /// The fractional number of milliseconds in this `Duration`.
212    pub const fn millis(&self) -> u64 {
213        self.micros / 1000 % 1000
214    }
215
216    /// The fractional number of milliseconds in this `Duration`.
217    pub const fn micros(&self) -> u64 {
218        self.micros % 1000000
219    }
220
221    /// The number of whole seconds in this `Duration`.
222    pub const fn secs(&self) -> u64 {
223        self.micros / 1000000
224    }
225
226    /// The total number of milliseconds in this `Duration`.
227    pub const fn total_millis(&self) -> u64 {
228        self.micros / 1000
229    }
230
231    /// The total number of microseconds in this `Duration`.
232    pub const fn total_micros(&self) -> u64 {
233        self.micros
234    }
235}
236
237impl fmt::Display for Duration {
238    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239        write!(f, "{}.{:03}s", self.secs(), self.millis())
240    }
241}
242
243#[cfg(feature = "defmt")]
244impl defmt::Format for Duration {
245    fn format(&self, f: defmt::Formatter) {
246        defmt::write!(f, "{}.{:03}s", self.secs(), self.millis());
247    }
248}
249
250impl ops::Add<Duration> for Duration {
251    type Output = Duration;
252
253    fn add(self, rhs: Duration) -> Duration {
254        Duration::from_micros(self.micros + rhs.total_micros())
255    }
256}
257
258impl ops::AddAssign<Duration> for Duration {
259    fn add_assign(&mut self, rhs: Duration) {
260        self.micros += rhs.total_micros();
261    }
262}
263
264impl ops::Sub<Duration> for Duration {
265    type Output = Duration;
266
267    fn sub(self, rhs: Duration) -> Duration {
268        Duration::from_micros(
269            self.micros
270                .checked_sub(rhs.total_micros())
271                .expect("overflow when subtracting durations"),
272        )
273    }
274}
275
276impl ops::SubAssign<Duration> for Duration {
277    fn sub_assign(&mut self, rhs: Duration) {
278        self.micros = self
279            .micros
280            .checked_sub(rhs.total_micros())
281            .expect("overflow when subtracting durations");
282    }
283}
284
285impl ops::Mul<u32> for Duration {
286    type Output = Duration;
287
288    fn mul(self, rhs: u32) -> Duration {
289        Duration::from_micros(self.micros * rhs as u64)
290    }
291}
292
293impl ops::MulAssign<u32> for Duration {
294    fn mul_assign(&mut self, rhs: u32) {
295        self.micros *= rhs as u64;
296    }
297}
298
299impl ops::Div<u32> for Duration {
300    type Output = Duration;
301
302    fn div(self, rhs: u32) -> Duration {
303        Duration::from_micros(self.micros / rhs as u64)
304    }
305}
306
307impl ops::DivAssign<u32> for Duration {
308    fn div_assign(&mut self, rhs: u32) {
309        self.micros /= rhs as u64;
310    }
311}
312
313impl ops::Shl<u32> for Duration {
314    type Output = Duration;
315
316    fn shl(self, rhs: u32) -> Duration {
317        Duration::from_micros(self.micros << rhs)
318    }
319}
320
321impl ops::ShlAssign<u32> for Duration {
322    fn shl_assign(&mut self, rhs: u32) {
323        self.micros <<= rhs;
324    }
325}
326
327impl ops::Shr<u32> for Duration {
328    type Output = Duration;
329
330    fn shr(self, rhs: u32) -> Duration {
331        Duration::from_micros(self.micros >> rhs)
332    }
333}
334
335impl ops::ShrAssign<u32> for Duration {
336    fn shr_assign(&mut self, rhs: u32) {
337        self.micros >>= rhs;
338    }
339}
340
341impl From<::core::time::Duration> for Duration {
342    fn from(other: ::core::time::Duration) -> Duration {
343        Duration::from_micros(other.as_secs() * 1000000 + other.subsec_micros() as u64)
344    }
345}
346
347impl From<Duration> for ::core::time::Duration {
348    fn from(val: Duration) -> Self {
349        ::core::time::Duration::from_micros(val.total_micros())
350    }
351}
352
353#[cfg(test)]
354mod test {
355    use super::*;
356
357    #[test]
358    fn test_instant_ops() {
359        // std::ops::Add
360        assert_eq!(
361            Instant::from_millis(4) + Duration::from_millis(6),
362            Instant::from_millis(10)
363        );
364        // std::ops::Sub
365        assert_eq!(
366            Instant::from_millis(7) - Duration::from_millis(5),
367            Instant::from_millis(2)
368        );
369    }
370
371    #[test]
372    fn test_instant_getters() {
373        let instant = Instant::from_millis(5674);
374        assert_eq!(instant.secs(), 5);
375        assert_eq!(instant.millis(), 674);
376        assert_eq!(instant.total_millis(), 5674);
377    }
378
379    #[test]
380    fn test_instant_display() {
381        assert_eq!(format!("{}", Instant::from_millis(74)), "0.074s");
382        assert_eq!(format!("{}", Instant::from_millis(5674)), "5.674s");
383        assert_eq!(format!("{}", Instant::from_millis(5000)), "5.000s");
384    }
385
386    #[test]
387    #[cfg(feature = "std")]
388    fn test_instant_conversions() {
389        let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into();
390        assert_eq!(
391            Instant::from(::std::time::UNIX_EPOCH),
392            Instant::from_millis(0)
393        );
394        assert_eq!(epoc, ::std::time::UNIX_EPOCH);
395        epoc = Instant::from_millis(2085955200i64 * 1000).into();
396        assert_eq!(
397            epoc,
398            ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)
399        );
400    }
401
402    #[test]
403    #[cfg(feature = "std")]
404    fn test_instant_conversions_from_std_instant() {
405        let std_now = ::std::time::Instant::now();
406
407        let before = Instant::from(std_now);
408        ::std::thread::sleep(::std::time::Duration::from_millis(5));
409        let after = Instant::from(std_now);
410
411        assert_eq!(
412            before, after,
413            "converting the same std Instant twice should yield the same result"
414        );
415    }
416
417    #[test]
418    fn test_duration_ops() {
419        // std::ops::Add
420        assert_eq!(
421            Duration::from_millis(40) + Duration::from_millis(2),
422            Duration::from_millis(42)
423        );
424        // std::ops::Sub
425        assert_eq!(
426            Duration::from_millis(555) - Duration::from_millis(42),
427            Duration::from_millis(513)
428        );
429        // std::ops::Mul
430        assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286));
431        // std::ops::Div
432        assert_eq!(Duration::from_millis(53) / 4, Duration::from_micros(13250));
433    }
434
435    #[test]
436    fn test_duration_assign_ops() {
437        let mut duration = Duration::from_millis(4735);
438        duration += Duration::from_millis(1733);
439        assert_eq!(duration, Duration::from_millis(6468));
440        duration -= Duration::from_millis(1234);
441        assert_eq!(duration, Duration::from_millis(5234));
442        duration *= 4;
443        assert_eq!(duration, Duration::from_millis(20936));
444        duration /= 5;
445        assert_eq!(duration, Duration::from_micros(4187200));
446    }
447
448    #[test]
449    #[should_panic(expected = "overflow when subtracting durations")]
450    fn test_sub_from_zero_overflow() {
451        let _ = Duration::from_millis(0) - Duration::from_millis(1);
452    }
453
454    #[test]
455    #[should_panic(expected = "attempt to divide by zero")]
456    fn test_div_by_zero() {
457        let _ = Duration::from_millis(4) / 0;
458    }
459
460    #[test]
461    fn test_duration_getters() {
462        let instant = Duration::from_millis(4934);
463        assert_eq!(instant.secs(), 4);
464        assert_eq!(instant.millis(), 934);
465        assert_eq!(instant.total_millis(), 4934);
466    }
467
468    #[test]
469    fn test_duration_conversions() {
470        let mut std_duration = ::core::time::Duration::from_millis(4934);
471        let duration: Duration = std_duration.into();
472        assert_eq!(duration, Duration::from_millis(4934));
473        assert_eq!(Duration::from(std_duration), Duration::from_millis(4934));
474
475        std_duration = duration.into();
476        assert_eq!(std_duration, ::core::time::Duration::from_millis(4934));
477    }
478}