diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index bd6fd5a3de428..aaea951e337c4 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -15,6 +15,12 @@ struct Timespec { } impl Timespec { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1); + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + const MIN: Timespec = Self::new(i64::MIN, 0); + const fn zero() -> Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } @@ -209,6 +215,12 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime { t: Timespec::MAX }; + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime { t: Timespec::MIN }; + pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { SystemTime(Timespec::new(tv_sec, tv_nsec)) } diff --git a/library/std/src/sys/pal/sgx/time.rs b/library/std/src/sys/pal/sgx/time.rs index db4cf2804bf13..0c20ec6852585 100644 --- a/library/std/src/sys/pal/sgx/time.rs +++ b/library/std/src/sys/pal/sgx/time.rs @@ -28,6 +28,12 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime(Duration::MAX); + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime(Duration::ZERO); + pub fn now() -> SystemTime { SystemTime(usercalls::insecure_time()) } diff --git a/library/std/src/sys/pal/solid/time.rs b/library/std/src/sys/pal/solid/time.rs index c39d715c6a6f6..b691603759a2e 100644 --- a/library/std/src/sys/pal/solid/time.rs +++ b/library/std/src/sys/pal/solid/time.rs @@ -10,6 +10,12 @@ pub struct SystemTime(abi::time_t); pub const UNIX_EPOCH: SystemTime = SystemTime(0); impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime(abi::time_t::MAX); + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime(abi::time_t::MIN); + pub fn now() -> SystemTime { let rtc = unsafe { let mut out = MaybeUninit::zeroed(); diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index 861b98da18daf..36cf9c11569f0 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -66,6 +66,24 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = MAX_UEFI_TIME; + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { + year: 1900, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + nanosecond: 0, + timezone: -1440, + daylight: 0, + pad1: 0, + pad2: 0, + }); + pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self { Self(system_time_internal::from_uefi(&t)) } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index c207f41cad4b5..12449c856b3e9 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -29,6 +29,12 @@ pub(crate) struct Timespec { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime { t: Timespec::MAX }; + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime { t: Timespec::MIN }; + #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] pub fn new(tv_sec: i64, tv_nsec: i64) -> Result { Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? }) @@ -61,6 +67,15 @@ impl fmt::Debug for SystemTime { } impl Timespec { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) }; + + // As described below, on AppleOS, dates before epoch are represented differently. + // This is not an issue here however, because we are using tv_sec = i64::MIN, + // which will cause the compatibility wrapper to not be executed at all. + #[unstable(feature = "time_systemtime_limits", issue = "none")] + const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) }; + const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } } diff --git a/library/std/src/sys/pal/unsupported/time.rs b/library/std/src/sys/pal/unsupported/time.rs index 6d67b538a96bf..de5a359fa1c19 100644 --- a/library/std/src/sys/pal/unsupported/time.rs +++ b/library/std/src/sys/pal/unsupported/time.rs @@ -27,6 +27,12 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = Duration::ZERO; + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = Duration::MAX; + pub fn now() -> SystemTime { panic!("time not implemented on this platform") } diff --git a/library/std/src/sys/pal/wasip1/time.rs b/library/std/src/sys/pal/wasip1/time.rs index 0d8d0b59ac14a..057819eb5f49a 100644 --- a/library/std/src/sys/pal/wasip1/time.rs +++ b/library/std/src/sys/pal/wasip1/time.rs @@ -39,6 +39,12 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime(Duration::MAX); + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime(Duration::ZERO); + pub fn now() -> SystemTime { SystemTime(current_time(wasi::CLOCKID_REALTIME)) } diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs index 434891839944d..118033cbad750 100644 --- a/library/std/src/sys/pal/wasip2/time.rs +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -31,6 +31,12 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime(Duration::MAX); + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime(Duration::ZERO); + pub fn now() -> SystemTime { let now = wasip2::clocks::wall_clock::now(); SystemTime(Duration::new(now.seconds, now.nanoseconds)) diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index 0d31b80e56afc..e9e8b58c0ec98 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -64,6 +64,22 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime { + t: c::FILETIME { + dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32, + dwHighDateTime: (i64::MAX >> 32) as u32, + }, + }; + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime { + t: c::FILETIME { + dwLowDateTime: (i64::MIN & 0xFFFFFFFF) as u32, + dwHighDateTime: (i64::MIN >> 32) as u32, + }, + }; + pub fn now() -> SystemTime { unsafe { let mut t: SystemTime = mem::zeroed(); diff --git a/library/std/src/sys/pal/xous/time.rs b/library/std/src/sys/pal/xous/time.rs index ae8be81c0b7c5..5cb59a3783c93 100644 --- a/library/std/src/sys/pal/xous/time.rs +++ b/library/std/src/sys/pal/xous/time.rs @@ -35,6 +35,12 @@ impl Instant { } impl SystemTime { + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime(Duration::MAX); + + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime(Duration::ZERO); + pub fn now() -> SystemTime { let result = blocking_scalar(systime_server(), GetUtcTimeMs.into()) .expect("failed to request utc time in ms"); diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 87aaf9091f1bc..d8637207822e1 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -511,6 +511,46 @@ impl SystemTime { #[stable(feature = "assoc_unix_epoch", since = "1.28.0")] pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH; + /// Represents the maximum value representable by [`SystemTime`] on this platform. + /// + /// This value differs a lot between platforms, but it is always the case + /// that any positive addition to [`SystemTime::MAX`] will fail. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(time_systemtime_limits)] + /// use std::time::{Duration, SystemTime}; + /// + /// // Adding zero will change nothing. + /// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX)); + /// + /// // But adding just 1ns will already fail. + /// assert_eq!(SystemTime::MAX.checked_add(Duration::new(0, 1)), None); + /// ``` + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX); + + /// Represents the minimum value representable by [`SystemTime`] on this platform. + /// + /// This value differs a lot between platforms, but it is always the case + /// that any positive subtraction from [`SystemTime::MIN`] will fail. + /// + /// # Examples + /// + /// ``` + /// #![feature(time_systemtime_limits)] + /// use std::time::{Duration, SystemTime}; + /// + /// // Subtracting zero will change nothing. + /// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN)); + /// + /// // But subtracting just 1ns will already fail. + /// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(0, 1)), None); + /// ``` + #[unstable(feature = "time_systemtime_limits", issue = "none")] + pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN); + /// Returns the system time corresponding to "now". /// /// # Examples diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs index be1948af91564..31cc7171fe52e 100644 --- a/library/std/tests/time.rs +++ b/library/std/tests/time.rs @@ -1,4 +1,5 @@ #![feature(duration_constants)] +#![feature(time_systemtime_limits)] use std::fmt::Debug; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; @@ -237,9 +238,27 @@ fn system_time_duration_since_max_range_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() { + // 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 ± a single nanosecond. + assert!(SystemTime::MAX.checked_add(Duration::new(0, 1)).is_none()); + assert!(SystemTime::MAX.checked_sub(Duration::new(0, 1)).is_some()); + assert!(SystemTime::MIN.checked_add(Duration::new(0, 1)).is_some()); + assert!(SystemTime::MIN.checked_sub(Duration::new(0, 1)).is_none()); +}