Skip to main content

omaha_client/time/
complex.rs

1// Copyright 2020 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9// Trait Implementations for `ComplexTime`
10pub mod complex_time_impls {
11    use super::super::{ComplexTime, ReadableSystemTime};
12    use std::fmt::Display;
13    use std::ops::{Add, AddAssign, Sub, SubAssign};
14    use std::time::Duration;
15
16    /// `ComplexTime` implements `Display` to provide a human-readable, detailed, format for its
17    /// values. It uses the `ReadableSystemTime` struct for its `SystemTime` component, and the
18    /// `Debug` trait implementation of `Instant`, as that type's internals are not accessible, and
19    /// it only implements `Debug`.
20    ///
21    /// # Example
22    /// ```no_run
23    /// use omaha_client::time::ComplexTime;
24    /// use std::time::{Duration, Instant, SystemTime};
25    /// assert_eq!(
26    ///     format!("{}", ComplexTime{
27    ///                       wall: SystemTime::UNIX_EPOCH + Duration::from_nanos(994610096026420000),
28    ///                       mono: Instant::now(),
29    ///                   }),
30    ///     "2001-07-08 16:34:56.026 UTC (994610096.026420000) at Instant{ tv_sec: SEC, tv_nsec: NSEC }"
31    /// );
32    ///```
33    impl Display for ComplexTime {
34        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35            write!(f, "{} at {:?}", ReadableSystemTime(self.wall), self.mono)
36        }
37    }
38
39    impl Add<Duration> for ComplexTime {
40        type Output = Self;
41
42        fn add(self, dur: Duration) -> Self {
43            Self { wall: self.wall + dur, mono: self.mono + dur }
44        }
45    }
46
47    /// AddAssign implementation that relies on the above Add implementation.
48    impl AddAssign<Duration> for ComplexTime {
49        fn add_assign(&mut self, other: Duration) {
50            *self = *self + other;
51        }
52    }
53
54    /// A `Sub` implementation for ComplexTime that subtracts the duration from both times that
55    /// the ComplexTime holds.
56    impl Sub<Duration> for ComplexTime {
57        type Output = Self;
58
59        fn sub(self, dur: Duration) -> Self {
60            Self {
61                wall: self.wall.checked_sub(dur).unwrap(),
62                mono: self.mono.checked_sub(dur).unwrap(),
63            }
64        }
65    }
66
67    /// AddAssign implementation that relies on the above Add implementation.
68    impl SubAssign<Duration> for ComplexTime {
69        fn sub_assign(&mut self, other: Duration) {
70            *self = *self - other;
71        }
72    }
73
74    #[cfg(test)]
75    mod tests {
76        use super::super::super::PartialComplexTime;
77        use super::super::system_time_conversion;
78        use super::*;
79        use std::time::{Duration, Instant, SystemTime};
80
81        #[test]
82        fn test_truncate_submicrosecond_walltime() {
83            let time = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
84            assert_eq!(
85                time.truncate_submicrosecond_walltime().wall,
86                system_time_conversion::micros_from_epoch_to_system_time(
87                    system_time_conversion::checked_system_time_to_micros_from_epoch(time.wall)
88                        .unwrap()
89                )
90            );
91        }
92
93        #[test]
94        fn test_wall_duration_since() {
95            let early = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
96            let later = ComplexTime { wall: early.wall + Duration::from_secs(200), ..early };
97            assert_eq!(later.wall_duration_since(early).unwrap(), Duration::from_secs(200))
98        }
99
100        #[test]
101        fn test_is_after_or_eq_any() {
102            let wall = SystemTime::now();
103            let mono = Instant::now();
104            let comp = ComplexTime { wall, mono };
105
106            let dur = Duration::from_secs(60);
107            let wall_after = wall + dur;
108            let mono_after = mono + dur;
109            let comp_after = comp + dur;
110
111            let comp_wall_after_mono_not = ComplexTime::from((wall_after, mono));
112            let comp_mono_after_wall_not = ComplexTime::from((wall, mono_after));
113
114            // strictly after cases
115            assert!(comp_after.is_after_or_eq_any(comp));
116            assert!(comp_after.is_after_or_eq_any(PartialComplexTime::Wall(wall)));
117            assert!(comp_after.is_after_or_eq_any(PartialComplexTime::Monotonic(mono)));
118            assert!(comp_after.is_after_or_eq_any(PartialComplexTime::Complex(comp)));
119
120            // reversed (note these are all negated)
121            assert!(!comp.is_after_or_eq_any(comp_after));
122            assert!(!comp.is_after_or_eq_any(PartialComplexTime::Wall(wall_after)));
123            assert!(!comp.is_after_or_eq_any(PartialComplexTime::Monotonic(mono_after)));
124            assert!(!comp.is_after_or_eq_any(PartialComplexTime::Complex(comp_after)));
125
126            // strictly equal cases
127            assert!(comp_after.is_after_or_eq_any(comp_after));
128            assert!(comp_after.is_after_or_eq_any(PartialComplexTime::Wall(wall_after)));
129            assert!(comp_after.is_after_or_eq_any(PartialComplexTime::Monotonic(mono_after)));
130            assert!(comp_after.is_after_or_eq_any(PartialComplexTime::Complex(comp_after)));
131
132            // wall is after, mono is not
133            assert!(comp_wall_after_mono_not.is_after_or_eq_any(comp));
134
135            // mono is after, wall is not
136            assert!(comp_mono_after_wall_not.is_after_or_eq_any(comp));
137        }
138
139        #[test]
140        fn test_complex_time_impl_add() {
141            let earlier = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
142            let dur = Duration::from_secs(60 * 60);
143
144            let later = earlier + dur;
145
146            let wall_duration_added = later.wall.duration_since(earlier.wall).unwrap();
147            let mono_duration_added = later.mono.duration_since(earlier.mono);
148
149            assert_eq!(wall_duration_added, dur);
150            assert_eq!(mono_duration_added, dur);
151        }
152
153        #[test]
154        fn test_complex_time_impl_add_assign() {
155            let mut time = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
156            let earlier = time;
157            let dur = Duration::from_secs(60 * 60);
158
159            time += dur;
160
161            let wall_duration_added = time.wall.duration_since(earlier.wall).unwrap();
162            let mono_duration_added = time.mono.duration_since(earlier.mono);
163
164            assert_eq!(wall_duration_added, dur);
165            assert_eq!(mono_duration_added, dur);
166        }
167
168        #[test]
169        fn test_complex_time_impl_sub() {
170            // If this test was executed early after boot, it's possible `Instant::now()` could be
171            // less than 60*60 seconds. To make the tests more deterministic, we'll create a
172            // synthetic now we'll use in tests that's at least 24 hours from the real `now()`
173            // value.
174            let mono = Instant::now() + Duration::from_secs(24 * 60 * 60);
175            let time = ComplexTime { wall: SystemTime::now(), mono };
176            let dur = Duration::from_secs(60 * 60);
177            let earlier = time - dur;
178
179            let wall_duration_subtracted = time.wall.duration_since(earlier.wall).unwrap();
180            let mono_duration_subtracted = time.mono.duration_since(earlier.mono);
181
182            assert_eq!(wall_duration_subtracted, dur);
183            assert_eq!(mono_duration_subtracted, dur);
184        }
185
186        #[test]
187        fn test_complex_time_impl_sub_assign() {
188            // If this test was executed early after boot, it's possible `Instant::now()` could be
189            // less than 60*60 seconds. To make the tests more deterministic, we'll create a
190            // synthetic now we'll use in tests that's at least 24 hours from the real `now()`
191            // value.
192            let mono = Instant::now() + Duration::from_secs(24 * 60 * 60);
193            let mut time = ComplexTime { wall: SystemTime::now(), mono };
194            let before_sub = time;
195            let dur = Duration::from_secs(60 * 60);
196
197            time -= dur;
198
199            let wall_duration_subtracted = before_sub.wall.duration_since(time.wall).unwrap();
200            let mono_duration_subtracted = before_sub.mono.duration_since(time.mono);
201
202            assert_eq!(wall_duration_subtracted, dur);
203            assert_eq!(mono_duration_subtracted, dur);
204        }
205    }
206}
207
208/// Conversions for `ComplexTime`.
209///
210/// This implements `From<T> for ComplexTime` for many T.
211/// This implements `From<ComplexTime> for U` for many U (which are outside this module)
212pub mod complex_time_type_conversions {
213    use super::super::ComplexTime;
214    use std::time::{Instant, SystemTime};
215
216    // `From<T> for ComplexTime`
217
218    impl From<(SystemTime, Instant)> for ComplexTime {
219        fn from(t: (SystemTime, Instant)) -> ComplexTime {
220            ComplexTime { wall: t.0, mono: t.1 }
221        }
222    }
223
224    // `From<ComplexTime> for ...`
225
226    impl From<ComplexTime> for SystemTime {
227        fn from(complex: ComplexTime) -> SystemTime {
228            complex.wall
229        }
230    }
231    impl From<ComplexTime> for Instant {
232        fn from(complex: ComplexTime) -> Instant {
233            complex.mono
234        }
235    }
236
237    #[cfg(test)]
238    mod tests {
239        use super::*;
240
241        /// Test that the `ComplexTime` `From` implementations work correctly.
242        #[test]
243        fn test_from_std_time_tuple_for_complex_time() {
244            let system_time = SystemTime::now();
245            let instant = Instant::now();
246            assert_eq!(
247                ComplexTime::from((system_time, instant)),
248                ComplexTime { wall: system_time, mono: instant }
249            );
250        }
251
252        #[test]
253        fn test_from_complex_time_for_instant() {
254            let time = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
255            let instant_from_time: Instant = Instant::from(time);
256            let time_into_instant: Instant = time.into();
257
258            assert_eq!(instant_from_time, time_into_instant);
259            assert_eq!(instant_from_time, time.mono);
260            assert_eq!(time_into_instant, time.mono);
261        }
262
263        #[test]
264        fn test_from_complex_time_for_system_time() {
265            let time = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
266            let system_from_time: SystemTime = SystemTime::from(time);
267            let time_into_system: SystemTime = time.into();
268
269            assert_eq!(system_from_time, time_into_system);
270            assert_eq!(system_from_time, time.wall);
271            assert_eq!(time_into_system, time.wall);
272        }
273    }
274}
275
276/// Trait Implementations for `PartialComplexTime`
277pub mod partial_complex_time_impls {
278    use super::super::{PartialComplexTime, ReadableSystemTime};
279    use std::fmt::Display;
280    use std::ops::{Add, AddAssign, Sub, SubAssign};
281    use std::time::Duration;
282
283    /// `PartialComplexTime` implements `Display` to provide a human-readable, detailed, format for
284    /// its values. It uses the `ReadableSystemTime` struct for its `SystemTime` component, and the
285    /// `Debug` trait implementation of `Instant`, as that type's internals are not accessible, and
286    /// it only implements `Debug`.
287    ///
288    /// # Example
289    /// ```no_run
290    /// use std::time::{Duration, Instant, SystemTime};
291    /// use omaha_client::time::{ComplexTime, PartialComplexTime};
292    ///
293    /// assert_eq!(
294    ///     format!("{}", PartialComplexTime::Complex(ComplexTime{
295    ///                       wall: SystemTime::UNIX_EPOCH + Duration::from_nanos(994610096026420000),
296    ///                       mono: Instant::now()
297    ///                   })),
298    ///     "2001-07-08 16:34:56.026 UTC (994610096.026420000) and Instant{ tv_sec: SEC, tv_nsec: NSEC }"
299    /// );
300    ///
301    /// assert_eq!(
302    ///     format!("{}", PartialComplexTime::Wall(
303    ///                       SystemTime::UNIX_EPOCH + Duration::from_nanos(994610096026420000),
304    ///                   )),
305    ///     "2001-07-08 16:34:56.026 UTC (994610096.026420000) and Instant{ tv_sec: SEC, tv_nsec: NSEC }"
306    /// );
307    ///
308    /// assert_eq!(
309    ///     format!("{}", PartialComplexTime::Monotonic(Instant::now())),
310    ///     "2001-07-08 16:34:56.026 UTC (994610096.026420000) and Instant{ tv_sec: SEC, tv_nsec: NSEC }"
311    /// );
312    ///```
313    impl Display for PartialComplexTime {
314        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315            match self {
316                Self::Wall(w) => write!(f, "{} and No Monotonic", ReadableSystemTime(*w)),
317                Self::Monotonic(m) => write!(f, "No Wall and {m:?}"),
318                Self::Complex(t) => Display::fmt(t, f),
319            }
320        }
321    }
322
323    /// An `Add` implementation for PartialComplexTime that adds the duration to each of the time
324    /// values it holds.
325    ///
326    /// # Panics
327    ///
328    /// The Add<Duration> implementations for both SystemTime and Instant, which this uses, will
329    /// panic on overflow.
330    impl Add<Duration> for PartialComplexTime {
331        type Output = Self;
332
333        fn add(self, dur: Duration) -> Self {
334            match self {
335                Self::Wall(w) => Self::Wall(w + dur),
336                Self::Monotonic(m) => Self::Monotonic(m + dur),
337                Self::Complex(c) => Self::Complex(c + dur),
338            }
339        }
340    }
341
342    /// AddAssign implementation that relies on the above Add implementation.
343    impl AddAssign<Duration> for PartialComplexTime {
344        fn add_assign(&mut self, other: Duration) {
345            *self = *self + other;
346        }
347    }
348
349    /// A `Sub` implementation for PartialComplexTime that subtracts the duration to each of the time
350    /// values it holds.
351    ///
352    /// # Panics
353    ///
354    /// Panics when the result cannot be expressed in the underlying representation.
355    /// Specifically, SystemTime, Instant, and ComplexTime may not be able to represent the
356    /// resulting time.
357    impl Sub<Duration> for PartialComplexTime {
358        type Output = Self;
359        fn sub(self, dur: Duration) -> Self {
360            match self {
361                Self::Wall(w) => Self::Wall(w.checked_sub(dur).unwrap()),
362                Self::Monotonic(m) => Self::Monotonic(m.checked_sub(dur).unwrap()),
363                Self::Complex(c) => Self::Complex(c - dur),
364            }
365        }
366    }
367
368    /// SubAssign implementation that relies on the above Add implementation.
369    impl SubAssign<Duration> for PartialComplexTime {
370        fn sub_assign(&mut self, other: Duration) {
371            *self = *self - other;
372        }
373    }
374    #[cfg(test)]
375    mod tests {
376        use super::super::super::ComplexTime;
377        use super::*;
378        use std::time::{Instant, SystemTime};
379
380        #[test]
381        fn test_partial_complex_time_impl_add() {
382            let wall = SystemTime::now();
383            let mono = Instant::now();
384            let comp = ComplexTime { wall, mono };
385
386            let partial_wall = PartialComplexTime::Wall(wall);
387            let partial_mono = PartialComplexTime::Monotonic(mono);
388            let partial_comp = PartialComplexTime::Complex(comp);
389
390            let dur = Duration::from_secs(60 * 60);
391
392            let later_partial_wall = partial_wall + dur;
393            let later_partial_mono = partial_mono + dur;
394            let later_partial_comp = partial_comp + dur;
395
396            match later_partial_wall {
397                PartialComplexTime::Wall(w) => assert_eq!(w.duration_since(wall).unwrap(), dur),
398                x => panic!("{x:?} is not a PartialComplexTime::Wall"),
399            };
400            match later_partial_mono {
401                PartialComplexTime::Monotonic(m) => assert_eq!(m.duration_since(mono), dur),
402                x => panic!("{x:?} is not a PartialComplexTime::Monotonic"),
403            };
404            match later_partial_comp {
405                PartialComplexTime::Complex(c) => {
406                    assert_eq!(c.wall.duration_since(wall).unwrap(), dur);
407                    assert_eq!(c.mono.duration_since(mono), dur);
408                }
409                x => panic!("{x:?} is not a PartialComplexTime::Complex"),
410            };
411        }
412
413        #[test]
414        fn test_partial_complex_time_impl_add_assign() {
415            let wall = SystemTime::now();
416            let mono = Instant::now();
417            let comp = ComplexTime { wall, mono };
418
419            let mut partial_wall = PartialComplexTime::Wall(wall);
420            let mut partial_mono = PartialComplexTime::Monotonic(mono);
421            let mut partial_comp = PartialComplexTime::Complex(comp);
422
423            let dur = Duration::from_secs(60 * 60);
424
425            // perform the add-assign
426            partial_wall += dur;
427            partial_mono += dur;
428            partial_comp += dur;
429
430            match partial_wall {
431                PartialComplexTime::Wall(w) => assert_eq!(w.duration_since(wall).unwrap(), dur),
432                x => panic!("{x:?} is not a PartialComplexTime::Wall"),
433            };
434            match partial_mono {
435                PartialComplexTime::Monotonic(m) => assert_eq!(m.duration_since(mono), dur),
436                x => panic!("{x:?} is not a PartialComplexTime::Monotonic"),
437            };
438            match partial_comp {
439                PartialComplexTime::Complex(c) => {
440                    assert_eq!(c.wall.duration_since(comp.wall).unwrap(), dur);
441                    assert_eq!(c.mono.duration_since(comp.mono), dur);
442                }
443                x => panic!("{x:?} is not a PartialComplexTime::Complex"),
444            };
445        }
446
447        #[test]
448        fn test_partial_complex_time_impl_sub() {
449            let wall = SystemTime::now();
450            // If this test was executed early after boot, it's possible `Instant::now()` could be
451            // less than 60*60 seconds. To make the tests more deterministic, we'll create a
452            // synthetic now we'll use in tests that's at least 24 hours from the real `now()`
453            // value.
454            let mono = Instant::now() + Duration::from_secs(24 * 60 * 60);
455            let comp = ComplexTime { wall, mono };
456
457            let partial_wall = PartialComplexTime::Wall(wall);
458            let partial_mono = PartialComplexTime::Monotonic(mono);
459            let partial_comp = PartialComplexTime::Complex(comp);
460
461            let dur = Duration::from_secs(60 * 60);
462
463            let earlier_partial_wall = partial_wall - dur;
464            let earlier_partial_mono = partial_mono - dur;
465            let earlier_partial_comp = partial_comp - dur;
466
467            match earlier_partial_wall {
468                PartialComplexTime::Wall(w) => assert_eq!(wall.duration_since(w).unwrap(), dur),
469                x => panic!("{x:?} is not a PartialComplexTime::Wall"),
470            };
471            match earlier_partial_mono {
472                PartialComplexTime::Monotonic(m) => assert_eq!(mono.duration_since(m), dur),
473                x => panic!("{x:?} is not a PartialComplexTime::Monotonic"),
474            };
475            match earlier_partial_comp {
476                PartialComplexTime::Complex(c) => {
477                    assert_eq!(wall.duration_since(c.wall).unwrap(), dur);
478                    assert_eq!(mono.duration_since(c.mono), dur);
479                }
480                x => panic!("{x:?} is not a PartialComplexTime::Complex"),
481            };
482        }
483
484        #[test]
485        fn test_partial_complex_time_impl_sub_assign() {
486            let wall = SystemTime::now();
487            // If this test was executed early after boot, it's possible `Instant::now()` could be
488            // less than 60*60 seconds. To make the tests more deterministic, we'll create a
489            // synthetic now we'll use in tests that's at least 24 hours from the real `now()`
490            // value.
491            let mono = Instant::now() + Duration::from_secs(24 * 60 * 60);
492            let comp = ComplexTime { wall, mono };
493
494            let mut partial_wall = PartialComplexTime::Wall(wall);
495            let mut partial_mono = PartialComplexTime::Monotonic(mono);
496            let mut partial_comp = PartialComplexTime::Complex(comp);
497
498            let dur = Duration::from_secs(60 * 60);
499
500            // perform the add-assign
501            partial_wall -= dur;
502            partial_mono -= dur;
503            partial_comp -= dur;
504
505            match partial_wall {
506                PartialComplexTime::Wall(w) => assert_eq!(wall.duration_since(w).unwrap(), dur),
507                x => panic!("{x:?} is not a PartialComplexTime::Wall"),
508            };
509            match partial_mono {
510                PartialComplexTime::Monotonic(m) => assert_eq!(mono.duration_since(m), dur),
511                x => panic!("{x:?} is not a PartialComplexTime::Monotonic"),
512            };
513            match partial_comp {
514                PartialComplexTime::Complex(c) => {
515                    assert_eq!(wall.duration_since(c.wall).unwrap(), dur);
516                    assert_eq!(mono.duration_since(c.mono), dur);
517                }
518                x => panic!("{x:?} is not a PartialComplexTime::Complex"),
519            };
520        }
521    }
522}
523
524/// Conversions for `PartialComplexTime`.
525///
526/// This implements `From<T> for PartialComplexTime` for many T.
527/// This implements `From<PartialComplexTime> for U` for many U (which are outside this module)
528pub mod partial_complex_time_type_conversions {
529    use super::super::{ComplexTime, PartialComplexTime};
530    use std::time::{Instant, SystemTime};
531
532    // `From<T> for PartialComplexTime`
533
534    impl From<ComplexTime> for PartialComplexTime {
535        fn from(t: ComplexTime) -> Self {
536            PartialComplexTime::Complex(t)
537        }
538    }
539
540    // Provided so that fn's that take `impl Into<Option<PartialComplexTime>>` can easily take a
541    // ComplexTime without spelling out the whole conversion (mostly applies to builders)
542    impl From<ComplexTime> for Option<PartialComplexTime> {
543        fn from(t: ComplexTime) -> Self {
544            Some(PartialComplexTime::from(t))
545        }
546    }
547
548    impl From<SystemTime> for PartialComplexTime {
549        fn from(w: SystemTime) -> PartialComplexTime {
550            PartialComplexTime::Wall(w)
551        }
552    }
553
554    impl From<Instant> for PartialComplexTime {
555        fn from(m: Instant) -> PartialComplexTime {
556            PartialComplexTime::Monotonic(m)
557        }
558    }
559
560    impl From<(SystemTime, Instant)> for PartialComplexTime {
561        fn from(t: (SystemTime, Instant)) -> PartialComplexTime {
562            PartialComplexTime::Complex(ComplexTime::from(t))
563        }
564    }
565
566    #[cfg(test)]
567    mod tests {
568        use super::*;
569        use std::time::{Duration, Instant, SystemTime};
570
571        #[test]
572        fn test_from_complex_time_for_partial_complex_time() {
573            let complex = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
574            assert_eq!(PartialComplexTime::from(complex), PartialComplexTime::Complex(complex));
575        }
576
577        #[test]
578        fn test_from_complex_time_for_option_partial_complex_time() {
579            let complex = ComplexTime { wall: SystemTime::now(), mono: Instant::now() };
580            assert_eq!(
581                Option::<PartialComplexTime>::from(complex),
582                Some(PartialComplexTime::Complex(complex))
583            );
584        }
585
586        #[test]
587        fn test_from_system_time_for_partial_complex_time() {
588            let system_time = SystemTime::now();
589            assert_eq!(
590                PartialComplexTime::from(system_time),
591                PartialComplexTime::Wall(system_time)
592            );
593        }
594
595        #[test]
596        fn test_from_instant_for_partial_complex_time() {
597            let instant = Instant::now();
598            assert_eq!(PartialComplexTime::from(instant), PartialComplexTime::Monotonic(instant));
599        }
600
601        #[test]
602        fn test_from_std_time_tuple_for_partial_complex_time() {
603            let system_time = SystemTime::now();
604            let instant = Instant::now();
605
606            assert_eq!(
607                PartialComplexTime::from(system_time),
608                PartialComplexTime::Wall(system_time)
609            );
610            assert_eq!(PartialComplexTime::from(instant), PartialComplexTime::Monotonic(instant));
611            assert_eq!(
612                PartialComplexTime::from((system_time, instant)),
613                PartialComplexTime::Complex(ComplexTime { wall: system_time, mono: instant })
614            );
615        }
616
617        // `From<PartialComplexTime> for ...`
618
619        #[test]
620        fn test_checked_to_system_time() {
621            let system_time = SystemTime::now();
622            let instant = Instant::now();
623
624            assert_eq!(
625                PartialComplexTime::Wall(system_time).checked_to_system_time(),
626                Some(system_time)
627            );
628            assert_eq!(PartialComplexTime::Monotonic(instant).checked_to_system_time(), None);
629            assert_eq!(
630                PartialComplexTime::Complex((system_time, instant).into()).checked_to_system_time(),
631                Some(system_time)
632            );
633        }
634
635        #[test]
636        fn test_checked_to_micros_from_partial_complex_time() {
637            let system_time = SystemTime::UNIX_EPOCH + Duration::from_micros(123456789);
638            let instant = Instant::now();
639
640            assert_eq!(
641                123456789,
642                PartialComplexTime::Wall(system_time).checked_to_micros_since_epoch().unwrap()
643            );
644            assert_eq!(
645                123456789,
646                PartialComplexTime::Complex(ComplexTime::from((system_time, instant)))
647                    .checked_to_micros_since_epoch()
648                    .unwrap()
649            );
650            assert_eq!(
651                None,
652                PartialComplexTime::Monotonic(instant).checked_to_micros_since_epoch()
653            );
654        }
655
656        #[test]
657        fn test_checked_to_micros_from_partial_complex_time_before_epoch() {
658            let system_time = SystemTime::UNIX_EPOCH - Duration::from_micros(123456789);
659            let instant = Instant::now();
660
661            assert_eq!(
662                -123456789,
663                PartialComplexTime::Wall(system_time).checked_to_micros_since_epoch().unwrap()
664            );
665            assert_eq!(
666                -123456789,
667                PartialComplexTime::Complex(ComplexTime::from((system_time, instant)))
668                    .checked_to_micros_since_epoch()
669                    .unwrap()
670            );
671            assert_eq!(
672                None,
673                PartialComplexTime::Monotonic(instant).checked_to_micros_since_epoch()
674            );
675        }
676
677        #[test]
678        fn test_checked_to_micros_from_partial_complex_time_overflow_is_none() {
679            let system_time = SystemTime::UNIX_EPOCH + 2 * Duration::from_micros(u64::MAX);
680            assert_eq!(None, PartialComplexTime::Wall(system_time).checked_to_micros_since_epoch());
681        }
682
683        #[test]
684        fn test_checked_to_micros_from_partial_complex_time_negative_overflow_is_none() {
685            let system_time = SystemTime::UNIX_EPOCH - 2 * Duration::from_micros(u64::MAX);
686            assert_eq!(None, PartialComplexTime::Wall(system_time).checked_to_micros_since_epoch());
687        }
688
689        #[test]
690        fn test_complete_with() {
691            let system_time = SystemTime::UNIX_EPOCH - Duration::from_micros(100);
692            let instant = Instant::now();
693            let complex = ComplexTime::from((system_time, instant));
694
695            let wall = PartialComplexTime::Wall(system_time);
696            let mono = PartialComplexTime::Monotonic(instant);
697            let comp = PartialComplexTime::Complex(complex);
698
699            let other = complex + Duration::from_micros(500);
700
701            assert_eq!(wall.complete_with(other), ComplexTime::from((system_time, other.mono)));
702            assert_eq!(mono.complete_with(other), ComplexTime::from((other.wall, instant)));
703            assert_eq!(comp.complete_with(other), complex);
704        }
705
706        #[test]
707        fn test_destructure() {
708            let system_time = SystemTime::now();
709            let instant = Instant::now();
710            let complex = ComplexTime::from((system_time, instant));
711
712            let wall = PartialComplexTime::Wall(system_time);
713            let mono = PartialComplexTime::Monotonic(instant);
714            let comp = PartialComplexTime::Complex(complex);
715
716            assert_eq!(wall.destructure(), (Some(system_time), None));
717            assert_eq!(mono.destructure(), (None, Some(instant)));
718            assert_eq!(comp.destructure(), (Some(system_time), Some(instant)));
719        }
720    }
721}
722
723/// Module to ease the conversion betwee SystemTime and i64 microseconds from the from UNIX Epoch.
724pub mod system_time_conversion {
725    use std::convert::TryFrom;
726    use std::time::{Duration, SystemTime};
727
728    /// Convert a SystemTime into microseconds from the unix epoch, returning None on overflow.
729    /// Valid over roughly +/- 30,000 years from 1970-01-01 UTC.
730    pub fn checked_system_time_to_micros_from_epoch(time: SystemTime) -> Option<i64> {
731        match time.duration_since(SystemTime::UNIX_EPOCH) {
732            Ok(duration_since_epoch) => {
733                // Safely convert to i64 microseconds or return None.
734                let micros: u128 = duration_since_epoch.as_micros();
735                i64::try_from(micros).ok()
736            }
737            Err(e) => {
738                // Safely convert to i64 microseconds (negative), or return None.
739                let micros: u128 = e.duration().as_micros();
740                i64::try_from(micros).ok().and_then(i64::checked_neg)
741            }
742        }
743    }
744
745    /// Convert micro seconds from the unix epoch to SystemTime.
746    pub fn micros_from_epoch_to_system_time(micros: i64) -> SystemTime {
747        // Duration is always unsigned, so negative values need to be handled separately from
748        // positive values
749        if micros > 0 {
750            let duration = Duration::from_micros(micros as u64);
751            SystemTime::UNIX_EPOCH + duration
752        } else {
753            let duration = Duration::from_micros((micros as u64).wrapping_neg());
754            SystemTime::UNIX_EPOCH - duration
755        }
756    }
757
758    #[cfg(test)]
759    mod tests {
760        use super::*;
761
762        #[test]
763        fn test_system_time_to_micros() {
764            let system_time = SystemTime::UNIX_EPOCH + Duration::from_micros(123456789);
765            assert_eq!(checked_system_time_to_micros_from_epoch(system_time).unwrap(), 123456789)
766        }
767
768        #[test]
769        fn test_system_time_to_micros_negative() {
770            let system_time = SystemTime::UNIX_EPOCH - Duration::from_micros(123456789);
771            assert_eq!(checked_system_time_to_micros_from_epoch(system_time).unwrap(), -123456789)
772        }
773
774        #[test]
775        fn test_system_time_to_micros_overflow_is_none() {
776            let system_time = SystemTime::UNIX_EPOCH + 2 * Duration::from_micros(u64::MAX);
777            assert_eq!(checked_system_time_to_micros_from_epoch(system_time), None);
778        }
779
780        #[test]
781        fn test_system_time_to_micros_negative_overflow_is_none() {
782            let system_time = SystemTime::UNIX_EPOCH - 2 * Duration::from_micros(u64::MAX);
783            assert_eq!(checked_system_time_to_micros_from_epoch(system_time), None);
784        }
785
786        #[test]
787        fn test_system_time_from_micros() {
788            let system_time = SystemTime::UNIX_EPOCH + Duration::from_micros(123456789);
789            assert_eq!(micros_from_epoch_to_system_time(123456789), system_time);
790        }
791
792        #[test]
793        fn test_system_time_from_micros_negative() {
794            let system_time = SystemTime::UNIX_EPOCH - Duration::from_micros(123456789);
795            assert_eq!(micros_from_epoch_to_system_time(-123456789), system_time);
796        }
797
798        #[test]
799        fn test_system_time_from_micros_positive_max() {
800            let system_time = SystemTime::UNIX_EPOCH + Duration::from_micros(i64::MAX as u64);
801            assert_eq!(micros_from_epoch_to_system_time(i64::MAX), system_time);
802        }
803
804        #[test]
805        fn test_system_time_from_micros_negative_min() {
806            let system_time = SystemTime::UNIX_EPOCH - Duration::from_micros((i64::MAX as u64) + 1);
807            assert_eq!(micros_from_epoch_to_system_time(i64::MIN), system_time);
808        }
809    }
810}