| #![feature(duration_constants)] | |
| #![feature(duration_constructors)] | |
| #![feature(time_systemtime_limits)] | |
| #![feature(time_saturating_systemtime)] | |
| use std::fmt::Debug; | |
| use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; | |
| macro_rules! assert_almost_eq { | |
| ($a:expr, $b:expr) => {{ | |
| let (a, b) = ($a, $b); | |
| if a != b { | |
| let (a, b) = if a > b { (a, b) } else { (b, a) }; | |
| assert!(a - Duration::from_micros(1) <= b, "{:?} is not almost equal to {:?}", a, b); | |
| } | |
| }}; | |
| } | |
| #[test] | |
| fn instant_monotonic() { | |
| let a = Instant::now(); | |
| loop { | |
| let b = Instant::now(); | |
| assert!(b >= a); | |
| if b > a { | |
| break; | |
| } | |
| } | |
| } | |
| #[test] | |
| #[cfg(not(target_arch = "wasm32"))] | |
| fn instant_monotonic_concurrent() -> std::thread::Result<()> { | |
| let threads: Vec<_> = (0..8) | |
| .map(|_| { | |
| std::thread::spawn(|| { | |
| let mut old = Instant::now(); | |
| let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; | |
| for _ in 0..count { | |
| let new = Instant::now(); | |
| assert!(new >= old); | |
| old = new; | |
| } | |
| }) | |
| }) | |
| .collect(); | |
| for t in threads { | |
| t.join()?; | |
| } | |
| Ok(()) | |
| } | |
| #[test] | |
| fn instant_elapsed() { | |
| let a = Instant::now(); | |
| let _ = a.elapsed(); | |
| } | |
| #[test] | |
| fn instant_math() { | |
| let a = Instant::now(); | |
| let b = Instant::now(); | |
| println!("a: {a:?}"); | |
| println!("b: {b:?}"); | |
| let dur = b.duration_since(a); | |
| println!("dur: {dur:?}"); | |
| assert_almost_eq!(b - dur, a); | |
| assert_almost_eq!(a + dur, b); | |
| let second = Duration::SECOND; | |
| assert_almost_eq!(a - second + second, a); | |
| assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); | |
| // checked_add_duration will not panic on overflow | |
| let mut maybe_t = Some(Instant::now()); | |
| let max_duration = Duration::from_secs(u64::MAX); | |
| // in case `Instant` can store `>= now + max_duration`. | |
| for _ in 0..2 { | |
| maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); | |
| } | |
| assert_eq!(maybe_t, None); | |
| // checked_add_duration calculates the right time and will work for another year | |
| let year = Duration::from_secs(60 * 60 * 24 * 365); | |
| assert_eq!(a + year, a.checked_add(year).unwrap()); | |
| } | |
| #[test] | |
| fn instant_math_is_associative() { | |
| let now = Instant::now(); | |
| let offset = Duration::from_millis(5); | |
| // Changing the order of instant math shouldn't change the results, | |
| // especially when the expression reduces to X + identity. | |
| assert_eq!((now + offset) - now, (now - now) + offset); | |
| // On any platform, `Instant` should have the same resolution as `Duration` (e.g. 1 nanosecond) | |
| // or better. Otherwise, math will be non-associative (see #91417). | |
| let now = Instant::now(); | |
| let provided_offset = Duration::from_nanos(1); | |
| let later = now + provided_offset; | |
| let measured_offset = later - now; | |
| assert_eq!(measured_offset, provided_offset); | |
| } | |
| #[test] | |
| fn instant_duration_since_saturates() { | |
| let a = Instant::now(); | |
| // This also checks that computing an instant before program startup works. | |
| assert_eq!((a - Duration::from_days(1)).duration_since(a), Duration::ZERO); | |
| } | |
| #[test] | |
| fn instant_checked_duration_since_nopanic() { | |
| let now = Instant::now(); | |
| let earlier = now - Duration::SECOND; | |
| let later = now + Duration::SECOND; | |
| assert_eq!(earlier.checked_duration_since(now), None); | |
| assert_eq!(later.checked_duration_since(now), Some(Duration::SECOND)); | |
| assert_eq!(now.checked_duration_since(now), Some(Duration::ZERO)); | |
| } | |
| #[test] | |
| fn instant_saturating_duration_since_nopanic() { | |
| let a = Instant::now(); | |
| #[allow(deprecated, deprecated_in_future)] | |
| let ret = (a - Duration::SECOND).saturating_duration_since(a); | |
| assert_eq!(ret, Duration::ZERO); | |
| } | |
| #[test] | |
| fn system_time_math() { | |
| let a = SystemTime::now(); | |
| let b = SystemTime::now(); | |
| match b.duration_since(a) { | |
| Ok(Duration::ZERO) => { | |
| assert_almost_eq!(a, b); | |
| } | |
| Ok(dur) => { | |
| assert!(b > a); | |
| assert_almost_eq!(b - dur, a); | |
| assert_almost_eq!(a + dur, b); | |
| } | |
| Err(dur) => { | |
| let dur = dur.duration(); | |
| assert!(a > b); | |
| assert_almost_eq!(b + dur, a); | |
| assert_almost_eq!(a - dur, b); | |
| } | |
| } | |
| let second = Duration::SECOND; | |
| assert_almost_eq!(a.duration_since(a - second).unwrap(), second); | |
| assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); | |
| assert_almost_eq!(a - second + second, a); | |
| assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); | |
| let one_second_from_epoch = UNIX_EPOCH + Duration::SECOND; | |
| let one_second_from_epoch2 = | |
| UNIX_EPOCH + Duration::from_millis(500) + Duration::from_millis(500); | |
| assert_eq!(one_second_from_epoch, one_second_from_epoch2); | |
| // checked_add_duration will not panic on overflow | |
| let mut maybe_t = Some(SystemTime::UNIX_EPOCH); | |
| let max_duration = Duration::from_secs(u64::MAX); | |
| // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. | |
| for _ in 0..2 { | |
| maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); | |
| } | |
| assert_eq!(maybe_t, None); | |
| // checked_add_duration calculates the right time and will work for another year | |
| let year = Duration::from_secs(60 * 60 * 24 * 365); | |
| assert_eq!(a + year, a.checked_add(year).unwrap()); | |
| } | |
| #[test] | |
| fn system_time_elapsed() { | |
| let a = SystemTime::now(); | |
| drop(a.elapsed()); | |
| } | |
| #[test] | |
| fn since_epoch() { | |
| let ts = SystemTime::now(); | |
| let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap(); | |
| let b = ts.duration_since(UNIX_EPOCH).unwrap(); | |
| assert!(b > a); | |
| assert_eq!(b - a, Duration::SECOND); | |
| let thirty_years = Duration::SECOND * 60 * 60 * 24 * 365 * 30; | |
| // Right now for CI this test is run in an emulator, and apparently the | |
| // aarch64 emulator's sense of time is that we're still living in the | |
| // 70s. This is also true for riscv (also qemu) | |
| // | |
| // Otherwise let's assume that we're all running computers later than | |
| // 2000. | |
| if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { | |
| assert!(a > thirty_years); | |
| } | |
| // let's assume that we're all running computers earlier than 2090. | |
| // Should give us ~70 years to fix this! | |
| let hundred_twenty_years = thirty_years * 4; | |
| assert!(a < hundred_twenty_years); | |
| } | |
| #[test] | |
| fn big_math() { | |
| // Check that the same result occurs when adding/subtracting each duration one at a time as when | |
| // adding/subtracting them all at once. | |
| #[track_caller] | |
| fn check<T: Eq + Copy + Debug>(start: Option<T>, op: impl Fn(&T, Duration) -> Option<T>) { | |
| const DURATIONS: [Duration; 2] = | |
| [Duration::from_secs(i64::MAX as _), Duration::from_secs(50)]; | |
| if let Some(start) = start { | |
| assert_eq!( | |
| op(&start, DURATIONS.into_iter().sum()), | |
| DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d)) | |
| ) | |
| } | |
| } | |
| check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add); | |
| check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub); | |
| let instant = Instant::now(); | |
| check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add); | |
| check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add); | |
| check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); | |
| check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn system_time_duration_since_max_range_on_unix() { | |
| // Repro regression https://github.com/rust-lang/rust/issues/146228 | |
| // Min and max values of `SystemTime` on Unix. | |
| let min = SystemTime::UNIX_EPOCH - (Duration::new(i64::MAX as u64 + 1, 0)); | |
| let max = SystemTime::UNIX_EPOCH + (Duration::new(i64::MAX as u64, 999_999_999)); | |
| assert_eq!(min, SystemTime::MIN); | |
| assert_eq!(max, SystemTime::MAX); | |
| let delta_a = max.duration_since(min).expect("duration_since overflow"); | |
| let delta_b = min.duration_since(max).expect_err("duration_since overflow").duration(); | |
| assert_eq!(Duration::MAX, delta_a); | |
| assert_eq!(Duration::MAX, delta_b); | |
| } | |
| #[test] | |
| fn system_time_max_min() { | |
| #[cfg(not(target_os = "windows"))] | |
| /// Most (all?) non-Windows systems have nanosecond precision. | |
| const MIN_INTERVAL: Duration = Duration::new(0, 1); | |
| #[cfg(target_os = "windows")] | |
| /// Windows' time precision is at 100ns. | |
| const MIN_INTERVAL: Duration = Duration::new(0, 100); | |
| // First, test everything with checked_* and Duration::ZERO. | |
| assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX)); | |
| assert_eq!(SystemTime::MAX.checked_sub(Duration::ZERO), Some(SystemTime::MAX)); | |
| assert_eq!(SystemTime::MIN.checked_add(Duration::ZERO), Some(SystemTime::MIN)); | |
| assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN)); | |
| // Now do the same again with checked_* but try by ± the lowest time precision. | |
| assert!(SystemTime::MAX.checked_add(MIN_INTERVAL).is_none()); | |
| assert!(SystemTime::MAX.checked_sub(MIN_INTERVAL).is_some()); | |
| assert!(SystemTime::MIN.checked_add(MIN_INTERVAL).is_some()); | |
| assert!(SystemTime::MIN.checked_sub(MIN_INTERVAL).is_none()); | |
| } | |
| #[test] | |
| fn system_time_saturating() { | |
| // Perform saturating addition on SystemTime::MAX to see how it behaves. | |
| assert_eq!(SystemTime::MAX.saturating_add(Duration::ZERO), SystemTime::MAX); | |
| assert_eq!(SystemTime::MAX.saturating_add(Duration::new(1, 0)), SystemTime::MAX); | |
| assert!(SystemTime::MAX.checked_add(Duration::new(1, 0)).is_none()); | |
| assert_eq!( | |
| SystemTime::MAX.saturating_sub(Duration::new(1, 0)), | |
| SystemTime::MAX.checked_sub(Duration::new(1, 0)).unwrap() | |
| ); | |
| // Perform saturating subtraction on SystemTime::MIn to see how it behaves. | |
| assert_eq!(SystemTime::MIN.saturating_sub(Duration::ZERO), SystemTime::MIN); | |
| assert_eq!(SystemTime::MIN.saturating_sub(Duration::new(1, 0)), SystemTime::MIN); | |
| assert!(SystemTime::MIN.checked_sub(Duration::new(1, 0)).is_none()); | |
| assert_eq!( | |
| SystemTime::MIN.saturating_add(Duration::new(1, 0)), | |
| SystemTime::MIN.checked_add(Duration::new(1, 0)).unwrap() | |
| ); | |
| // Check saturating_duration_since with various constant values. | |
| assert!(SystemTime::MAX.saturating_duration_since(SystemTime::MIN) >= Duration::ZERO); | |
| assert_eq!(SystemTime::MAX.saturating_duration_since(SystemTime::MAX), Duration::ZERO); | |
| assert!(SystemTime::MAX.duration_since(SystemTime::MAX).is_ok()); | |
| assert_eq!(SystemTime::MIN.saturating_duration_since(SystemTime::MAX), Duration::ZERO); | |
| assert!(SystemTime::MIN.duration_since(SystemTime::MAX).is_err()); | |
| assert_eq!( | |
| (SystemTime::UNIX_EPOCH + Duration::new(1, 0)) | |
| .saturating_duration_since(SystemTime::UNIX_EPOCH), | |
| Duration::new(1, 0) | |
| ); | |
| assert_eq!( | |
| SystemTime::UNIX_EPOCH | |
| .saturating_duration_since(SystemTime::UNIX_EPOCH + Duration::new(1, 0)), | |
| Duration::ZERO | |
| ); | |
| } |