1use rand::Rng;
4use std::time::{Duration, SystemTime};
5
6pub trait SystemTimeExt {
8 fn epoch(&self) -> Duration;
12
13 fn epoch_millis(&self) -> u64;
18
19 fn add_jittered(&self, rng: &mut impl Rng, jitter: Duration) -> SystemTime;
22}
23
24impl SystemTimeExt for SystemTime {
25 fn epoch(&self) -> Duration {
26 self.duration_since(std::time::UNIX_EPOCH)
27 .expect("failed to get epoch time")
28 }
29
30 fn epoch_millis(&self) -> u64 {
31 self.epoch().as_millis().min(u64::MAX as u128) as u64
32 }
33
34 fn add_jittered(&self, rng: &mut impl Rng, jitter: Duration) -> SystemTime {
35 *self + rng.gen_range(Duration::default()..=jitter * 2)
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn test_epoch() {
45 let time = SystemTime::UNIX_EPOCH;
46 assert_eq!(time.epoch(), Duration::from_secs(0));
47
48 let time = SystemTime::UNIX_EPOCH + Duration::from_secs(1) + Duration::from_millis(1);
49 assert_eq!(time.epoch(), Duration::from_millis(1_001));
50 }
51
52 #[test]
53 #[should_panic(expected = "failed to get epoch time")]
54 fn test_epoch_panics() {
55 let time = SystemTime::UNIX_EPOCH - Duration::from_secs(1);
56 time.epoch();
57 }
58
59 #[test]
60 fn test_epoch_millis() {
61 let time = SystemTime::UNIX_EPOCH;
62 assert_eq!(time.epoch_millis(), 0);
63
64 let time = SystemTime::UNIX_EPOCH + Duration::from_secs(1) + Duration::from_millis(1);
65 assert_eq!(time.epoch_millis(), 1_001);
66
67 let time = SystemTime::UNIX_EPOCH + Duration::from_secs(1) + Duration::from_nanos(999_999);
69 assert_eq!(time.epoch_millis(), 1_000);
70
71 let time = SystemTime::UNIX_EPOCH + Duration::from_millis(u64::MAX);
73 assert_eq!(time.epoch_millis(), u64::MAX);
74 let time = time + Duration::from_millis(1);
75 assert_eq!(time.epoch_millis(), u64::MAX);
76 }
77
78 #[test]
79 #[should_panic(expected = "failed to get epoch time")]
80 fn test_epoch_millis_panics() {
81 let time = SystemTime::UNIX_EPOCH - Duration::from_secs(1);
82 time.epoch_millis();
83 }
84
85 #[test]
86 fn test_add_jittered() {
87 let mut rng = rand::thread_rng();
88 let time = SystemTime::UNIX_EPOCH + Duration::from_secs(1);
89 let jitter = Duration::from_secs(2);
90
91 let (mut below, mut above) = (false, false);
93 for _ in 0..100 {
94 let new_time = time.add_jittered(&mut rng, jitter);
95
96 let avg = time + jitter;
98 below |= new_time < avg;
99 above |= new_time > avg;
100
101 assert!(new_time >= time);
103 assert!(new_time <= time + (jitter * 2));
104 }
105 assert!(below && above);
106 }
107}