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