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        let elapsed = other.elapsed();
109        Instant::from_micros((elapsed.as_secs() * 1_000000) as i64 + elapsed.subsec_micros() as i64)
110    }
111}
112
113#[cfg(feature = "std")]
114impl From<::std::time::SystemTime> for Instant {
115    fn from(other: ::std::time::SystemTime) -> Instant {
116        let n = other
117            .duration_since(::std::time::UNIX_EPOCH)
118            .expect("start time must not be before the unix epoch");
119        Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64)
120    }
121}
122
123#[cfg(feature = "std")]
124impl From<Instant> for ::std::time::SystemTime {
125    fn from(val: Instant) -> Self {
126        ::std::time::UNIX_EPOCH + ::std::time::Duration::from_micros(val.micros as u64)
127    }
128}
129
130impl fmt::Display for Instant {
131    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132        write!(f, "{}.{:0>3}s", self.secs(), self.millis())
133    }
134}
135
136#[cfg(feature = "defmt")]
137impl defmt::Format for Instant {
138    fn format(&self, f: defmt::Formatter) {
139        defmt::write!(f, "{}.{:03}s", self.secs(), self.millis());
140    }
141}
142
143impl ops::Add<Duration> for Instant {
144    type Output = Instant;
145
146    fn add(self, rhs: Duration) -> Instant {
147        Instant::from_micros(self.micros + rhs.total_micros() as i64)
148    }
149}
150
151impl ops::AddAssign<Duration> for Instant {
152    fn add_assign(&mut self, rhs: Duration) {
153        self.micros += rhs.total_micros() as i64;
154    }
155}
156
157impl ops::Sub<Duration> for Instant {
158    type Output = Instant;
159
160    fn sub(self, rhs: Duration) -> Instant {
161        Instant::from_micros(self.micros - rhs.total_micros() as i64)
162    }
163}
164
165impl ops::SubAssign<Duration> for Instant {
166    fn sub_assign(&mut self, rhs: Duration) {
167        self.micros -= rhs.total_micros() as i64;
168    }
169}
170
171impl ops::Sub<Instant> for Instant {
172    type Output = Duration;
173
174    fn sub(self, rhs: Instant) -> Duration {
175        Duration::from_micros((self.micros - rhs.micros).unsigned_abs())
176    }
177}
178
179/// A relative amount of time.
180#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
181pub struct Duration {
182    micros: u64,
183}
184
185impl Duration {
186    pub const ZERO: Duration = Duration::from_micros(0);
187    /// The longest possible duration we can encode.
188    pub const MAX: Duration = Duration::from_micros(u64::MAX);
189    /// Create a new `Duration` from a number of microseconds.
190    pub const fn from_micros(micros: u64) -> Duration {
191        Duration { micros }
192    }
193
194    /// Create a new `Duration` from a number of milliseconds.
195    pub const fn from_millis(millis: u64) -> Duration {
196        Duration {
197            micros: millis * 1000,
198        }
199    }
200
201    /// Create a new `Duration` from a number of seconds.
202    pub const fn from_secs(secs: u64) -> Duration {
203        Duration {
204            micros: secs * 1000000,
205        }
206    }
207
208    /// The fractional number of milliseconds in this `Duration`.
209    pub const fn millis(&self) -> u64 {
210        self.micros / 1000 % 1000
211    }
212
213    /// The fractional number of milliseconds in this `Duration`.
214    pub const fn micros(&self) -> u64 {
215        self.micros % 1000000
216    }
217
218    /// The number of whole seconds in this `Duration`.
219    pub const fn secs(&self) -> u64 {
220        self.micros / 1000000
221    }
222
223    /// The total number of milliseconds in this `Duration`.
224    pub const fn total_millis(&self) -> u64 {
225        self.micros / 1000
226    }
227
228    /// The total number of microseconds in this `Duration`.
229    pub const fn total_micros(&self) -> u64 {
230        self.micros
231    }
232}
233
234impl fmt::Display for Duration {
235    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236        write!(f, "{}.{:03}s", self.secs(), self.millis())
237    }
238}
239
240#[cfg(feature = "defmt")]
241impl defmt::Format for Duration {
242    fn format(&self, f: defmt::Formatter) {
243        defmt::write!(f, "{}.{:03}s", self.secs(), self.millis());
244    }
245}
246
247impl ops::Add<Duration> for Duration {
248    type Output = Duration;
249
250    fn add(self, rhs: Duration) -> Duration {
251        Duration::from_micros(self.micros + rhs.total_micros())
252    }
253}
254
255impl ops::AddAssign<Duration> for Duration {
256    fn add_assign(&mut self, rhs: Duration) {
257        self.micros += rhs.total_micros();
258    }
259}
260
261impl ops::Sub<Duration> for Duration {
262    type Output = Duration;
263
264    fn sub(self, rhs: Duration) -> Duration {
265        Duration::from_micros(
266            self.micros
267                .checked_sub(rhs.total_micros())
268                .expect("overflow when subtracting durations"),
269        )
270    }
271}
272
273impl ops::SubAssign<Duration> for Duration {
274    fn sub_assign(&mut self, rhs: Duration) {
275        self.micros = self
276            .micros
277            .checked_sub(rhs.total_micros())
278            .expect("overflow when subtracting durations");
279    }
280}
281
282impl ops::Mul<u32> for Duration {
283    type Output = Duration;
284
285    fn mul(self, rhs: u32) -> Duration {
286        Duration::from_micros(self.micros * rhs as u64)
287    }
288}
289
290impl ops::MulAssign<u32> for Duration {
291    fn mul_assign(&mut self, rhs: u32) {
292        self.micros *= rhs as u64;
293    }
294}
295
296impl ops::Div<u32> for Duration {
297    type Output = Duration;
298
299    fn div(self, rhs: u32) -> Duration {
300        Duration::from_micros(self.micros / rhs as u64)
301    }
302}
303
304impl ops::DivAssign<u32> for Duration {
305    fn div_assign(&mut self, rhs: u32) {
306        self.micros /= rhs as u64;
307    }
308}
309
310impl ops::Shl<u32> for Duration {
311    type Output = Duration;
312
313    fn shl(self, rhs: u32) -> Duration {
314        Duration::from_micros(self.micros << rhs)
315    }
316}
317
318impl ops::ShlAssign<u32> for Duration {
319    fn shl_assign(&mut self, rhs: u32) {
320        self.micros <<= rhs;
321    }
322}
323
324impl ops::Shr<u32> for Duration {
325    type Output = Duration;
326
327    fn shr(self, rhs: u32) -> Duration {
328        Duration::from_micros(self.micros >> rhs)
329    }
330}
331
332impl ops::ShrAssign<u32> for Duration {
333    fn shr_assign(&mut self, rhs: u32) {
334        self.micros >>= rhs;
335    }
336}
337
338impl From<::core::time::Duration> for Duration {
339    fn from(other: ::core::time::Duration) -> Duration {
340        Duration::from_micros(other.as_secs() * 1000000 + other.subsec_micros() as u64)
341    }
342}
343
344impl From<Duration> for ::core::time::Duration {
345    fn from(val: Duration) -> Self {
346        ::core::time::Duration::from_micros(val.total_micros())
347    }
348}
349
350#[cfg(test)]
351mod test {
352    use super::*;
353
354    #[test]
355    fn test_instant_ops() {
356        // std::ops::Add
357        assert_eq!(
358            Instant::from_millis(4) + Duration::from_millis(6),
359            Instant::from_millis(10)
360        );
361        // std::ops::Sub
362        assert_eq!(
363            Instant::from_millis(7) - Duration::from_millis(5),
364            Instant::from_millis(2)
365        );
366    }
367
368    #[test]
369    fn test_instant_getters() {
370        let instant = Instant::from_millis(5674);
371        assert_eq!(instant.secs(), 5);
372        assert_eq!(instant.millis(), 674);
373        assert_eq!(instant.total_millis(), 5674);
374    }
375
376    #[test]
377    fn test_instant_display() {
378        assert_eq!(format!("{}", Instant::from_millis(74)), "0.074s");
379        assert_eq!(format!("{}", Instant::from_millis(5674)), "5.674s");
380        assert_eq!(format!("{}", Instant::from_millis(5000)), "5.000s");
381    }
382
383    #[test]
384    #[cfg(feature = "std")]
385    fn test_instant_conversions() {
386        let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into();
387        assert_eq!(
388            Instant::from(::std::time::UNIX_EPOCH),
389            Instant::from_millis(0)
390        );
391        assert_eq!(epoc, ::std::time::UNIX_EPOCH);
392        epoc = Instant::from_millis(2085955200i64 * 1000).into();
393        assert_eq!(
394            epoc,
395            ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)
396        );
397    }
398
399    #[test]
400    fn test_duration_ops() {
401        // std::ops::Add
402        assert_eq!(
403            Duration::from_millis(40) + Duration::from_millis(2),
404            Duration::from_millis(42)
405        );
406        // std::ops::Sub
407        assert_eq!(
408            Duration::from_millis(555) - Duration::from_millis(42),
409            Duration::from_millis(513)
410        );
411        // std::ops::Mul
412        assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286));
413        // std::ops::Div
414        assert_eq!(Duration::from_millis(53) / 4, Duration::from_micros(13250));
415    }
416
417    #[test]
418    fn test_duration_assign_ops() {
419        let mut duration = Duration::from_millis(4735);
420        duration += Duration::from_millis(1733);
421        assert_eq!(duration, Duration::from_millis(6468));
422        duration -= Duration::from_millis(1234);
423        assert_eq!(duration, Duration::from_millis(5234));
424        duration *= 4;
425        assert_eq!(duration, Duration::from_millis(20936));
426        duration /= 5;
427        assert_eq!(duration, Duration::from_micros(4187200));
428    }
429
430    #[test]
431    #[should_panic(expected = "overflow when subtracting durations")]
432    fn test_sub_from_zero_overflow() {
433        let _ = Duration::from_millis(0) - Duration::from_millis(1);
434    }
435
436    #[test]
437    #[should_panic(expected = "attempt to divide by zero")]
438    fn test_div_by_zero() {
439        let _ = Duration::from_millis(4) / 0;
440    }
441
442    #[test]
443    fn test_duration_getters() {
444        let instant = Duration::from_millis(4934);
445        assert_eq!(instant.secs(), 4);
446        assert_eq!(instant.millis(), 934);
447        assert_eq!(instant.total_millis(), 4934);
448    }
449
450    #[test]
451    fn test_duration_conversions() {
452        let mut std_duration = ::core::time::Duration::from_millis(4934);
453        let duration: Duration = std_duration.into();
454        assert_eq!(duration, Duration::from_millis(4934));
455        assert_eq!(Duration::from(std_duration), Duration::from_millis(4934));
456
457        std_duration = duration.into();
458        assert_eq!(std_duration, ::core::time::Duration::from_millis(4934));
459    }
460}