Skip to content

Commit 71c14ef

Browse files
committed
time: Implement SystemTime::{MIN, MAX}
This commit introduces two new constants to SystemTime: `MIN` and `MAX`, whose value represent the maximum values for the respective data type, depending upon the platform. Technically, this value is already obtainable during runtime with the following algorithm: Use `SystemTime::UNIX_EPOCH` and call `checked_add` (or `checked_sub`) repeatedly with `Duration::new(0, 1)` on it, until it returns None. Mathematically speaking, this algorithm will terminate after a finite amount of steps, yet it is impractical to run it, as it takes practically forever. Besides, this commit also adds a unit test. Concrete implementation depending upon the platform is done in later commits. In the future, the hope of the authors lies within the creation of a `SystemTime::saturating_add` and `SystemTime::saturating_sub`, similar to the functions already present in `std::time::Duration`. However, for those, these constants are crucially required, thereby this should be seen as the initial step towards this direction. Below are platform specifc notes: # Hermit The HermitOS implementation is more or less identitcal to the Unix one. # sgx The implementation uses a `Duration` to store the Unix time, thereby implying `Duration::ZERO` and `Duration::MAX` as the limits. # solid The implementation uses a `time_t` to store the system time within a single value (i.e. no dual secs/nanosecs handling), thereby implying its `::MIN` and `::MAX` values as the respective boundaries. # UEFI UEFI has a weird way to store times, i.e. a very complicated struct. The standard proclaims "1900-01-01T00:00:00+0000" to be the lowest possible value and `MAX_UEFI_TIME` is already present for the upper limit. # Windows Windows is weird. The Win32 documentation makes no statement on a maximum value here. Next to this, there are two conflicting types: `SYSTEMTIME` and `FILETIME`. Rust's Standard Library uses `FILETIME`, whose limit will (probably) be `i64::MAX` packed into two integers. However, `SYSTEMTIME` has a lower-limit. # xous It is similar to sgx in the sense of using a `Duration`. # unsupported Unsupported platforms store a `SystemTime` in a `Duration`, just like sgx, thereby implying `Duration::ZERO` and `Duration::MAX` as the respective limits.
1 parent e2893f7 commit 71c14ef

File tree

10 files changed

+143
-0
lines changed

10 files changed

+143
-0
lines changed

library/std/src/sys/pal/hermit/time.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ struct Timespec {
1515
}
1616

1717
impl Timespec {
18+
const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);
19+
20+
const MIN: Timespec = Self::new(i64::MIN, 0);
21+
1822
const fn zero() -> Timespec {
1923
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
2024
}
@@ -209,6 +213,10 @@ pub struct SystemTime(Timespec);
209213
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
210214

211215
impl SystemTime {
216+
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
217+
218+
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
219+
212220
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
213221
SystemTime(Timespec::new(tv_sec, tv_nsec))
214222
}

library/std/src/sys/pal/sgx/time.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ impl Instant {
2828
}
2929

3030
impl SystemTime {
31+
pub const MAX: SystemTime = SystemTime(Duration::MAX);
32+
33+
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
34+
3135
pub fn now() -> SystemTime {
3236
SystemTime(usercalls::insecure_time())
3337
}

library/std/src/sys/pal/solid/time.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ pub struct SystemTime(abi::time_t);
1010
pub const UNIX_EPOCH: SystemTime = SystemTime(0);
1111

1212
impl SystemTime {
13+
pub const MAX: SystemTime = SystemTime(abi::time_t::MAX);
14+
15+
pub const MIN: SystemTime = SystemTime(abi::time_t::MIN);
16+
1317
pub fn now() -> SystemTime {
1418
let rtc = unsafe {
1519
let mut out = MaybeUninit::zeroed();

library/std/src/sys/pal/uefi/time.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ impl Instant {
7070
}
7171

7272
impl SystemTime {
73+
pub const MAX: SystemTime = MAX_UEFI_TIME;
74+
75+
pub const MIN: SystemTime = SystemTime::from_uefi(r_efi::efi::Time {
76+
year: 1900,
77+
month: 1,
78+
day: 1,
79+
hour: 0,
80+
minute: 0,
81+
second: 0,
82+
nanosecond: 0,
83+
timezone: -1440,
84+
daylight: 0,
85+
pad1: 0,
86+
pad2: 0,
87+
});
88+
7389
pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option<Self> {
7490
match system_time_internal::from_uefi(&t) {
7591
Some(x) => Some(Self(x)),

library/std/src/sys/pal/unix/time.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ pub(crate) struct Timespec {
3030
}
3131

3232
impl SystemTime {
33+
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
34+
35+
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
36+
3337
#[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
3438
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
3539
Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
@@ -62,6 +66,13 @@ impl fmt::Debug for SystemTime {
6266
}
6367

6468
impl Timespec {
69+
const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
70+
71+
// As described below, on Apple OS, dates before epoch are represented differently.
72+
// This is not an issue here however, because we are using tv_sec = i64::MIN,
73+
// which will cause the compatibility wrapper to not be executed at all.
74+
const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
75+
6576
const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
6677
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
6778
}

library/std/src/sys/pal/unsupported/time.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ impl Instant {
2727
}
2828

2929
impl SystemTime {
30+
pub const MAX: SystemTime = SystemTime(Duration::MAX);
31+
32+
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
33+
3034
pub fn now() -> SystemTime {
3135
panic!("time not implemented on this platform")
3236
}

library/std/src/sys/pal/windows/time.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ impl Instant {
6464
}
6565

6666
impl SystemTime {
67+
pub const MAX: SystemTime = SystemTime {
68+
t: c::FILETIME {
69+
dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32,
70+
dwHighDateTime: (i64::MAX >> 32) as u32,
71+
},
72+
};
73+
74+
pub const MIN: SystemTime =
75+
SystemTime { t: c::FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 } };
76+
6777
pub fn now() -> SystemTime {
6878
unsafe {
6979
let mut t: SystemTime = mem::zeroed();

library/std/src/sys/pal/xous/time.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ impl Instant {
3535
}
3636

3737
impl SystemTime {
38+
pub const MAX: SystemTime = SystemTime(Duration::MAX);
39+
40+
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
41+
3842
pub fn now() -> SystemTime {
3943
let result = blocking_scalar(systime_server(), GetUtcTimeMs.into())
4044
.expect("failed to request utc time in ms");

library/std/src/time.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,69 @@ impl SystemTime {
511511
#[stable(feature = "assoc_unix_epoch", since = "1.28.0")]
512512
pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH;
513513

514+
/// Represents the maximum value representable by [`SystemTime`] on this platform.
515+
///
516+
/// This value differs a lot between platforms, but it is always the case
517+
/// that any positive addition to [`SystemTime::MAX`] will fail.
518+
///
519+
/// # Examples
520+
///
521+
/// ```no_run
522+
/// #![feature(time_systemtime_limits)]
523+
/// use std::time::{Duration, SystemTime};
524+
///
525+
/// // Adding zero will change nothing.
526+
/// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
527+
///
528+
/// // But adding just 1ns will already fail.
529+
/// assert_eq!(SystemTime::MAX.checked_add(Duration::new(0, 1)), None);
530+
///
531+
/// // Utilize this for saturating arithmetic to improve error handling.
532+
/// // In this case, we will use a certificate with a timestamp in the
533+
/// // future as a practical example.
534+
/// let configured_offset = Duration::from_secs(60 * 60 * 24);
535+
/// let valid_after =
536+
/// SystemTime::now()
537+
/// .checked_add(configured_offset)
538+
/// .unwrap_or(SystemTime::MAX);
539+
/// ```
540+
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
541+
pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX);
542+
543+
/// Represents the minimum value representable by [`SystemTime`] on this platform.
544+
///
545+
/// This value differs a lot between platforms, but it is always the case
546+
/// that any positive subtraction from [`SystemTime::MIN`] will fail.
547+
///
548+
/// Depending on the platform, this may be either less than or equal to
549+
/// [`SystemTime::UNIX_EPOCH`], depending on whether the operating system
550+
/// supports the representation of timestamps before the Unix epoch or not.
551+
/// However, it is always guaranteed that a [`SystemTime::UNIX_EPOCH`] fits
552+
/// between a [`SystemTime::MIN`] and [`SystemTime::MAX`].
553+
///
554+
/// # Examples
555+
///
556+
/// ```
557+
/// #![feature(time_systemtime_limits)]
558+
/// use std::time::{Duration, SystemTime};
559+
///
560+
/// // Subtracting zero will change nothing.
561+
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
562+
///
563+
/// // But subtracting just 1ns will already fail.
564+
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(0, 1)), None);
565+
///
566+
/// // Utilize this for saturating arithmetic to improve error handling.
567+
/// // In this case, we will use a cache expiry as a practical example.
568+
/// let configured_expiry = Duration::from_secs(60 * 3);
569+
/// let expiry_threshold =
570+
/// SystemTime::now()
571+
/// .checked_sub(configured_expiry)
572+
/// .unwrap_or(SystemTime::MIN);
573+
/// ```
574+
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
575+
pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN);
576+
514577
/// Returns the system time corresponding to "now".
515578
///
516579
/// # Examples

library/std/tests/time.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![feature(duration_constants)]
2+
#![feature(time_systemtime_limits)]
23

34
use std::fmt::Debug;
45
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@@ -237,9 +238,27 @@ fn system_time_duration_since_max_range_on_unix() {
237238
let min = SystemTime::UNIX_EPOCH - (Duration::new(i64::MAX as u64 + 1, 0));
238239
let max = SystemTime::UNIX_EPOCH + (Duration::new(i64::MAX as u64, 999_999_999));
239240

241+
assert_eq!(min, SystemTime::MIN);
242+
assert_eq!(max, SystemTime::MAX);
243+
240244
let delta_a = max.duration_since(min).expect("duration_since overflow");
241245
let delta_b = min.duration_since(max).expect_err("duration_since overflow").duration();
242246

243247
assert_eq!(Duration::MAX, delta_a);
244248
assert_eq!(Duration::MAX, delta_b);
245249
}
250+
251+
#[test]
252+
fn system_time_max_min() {
253+
// First, test everything with checked_* and Duration::ZERO.
254+
assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
255+
assert_eq!(SystemTime::MAX.checked_sub(Duration::ZERO), Some(SystemTime::MAX));
256+
assert_eq!(SystemTime::MIN.checked_add(Duration::ZERO), Some(SystemTime::MIN));
257+
assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
258+
259+
// Now do the same again with checked_* but try by ± a single nanosecond.
260+
assert!(SystemTime::MAX.checked_add(Duration::new(0, 1)).is_none());
261+
assert!(SystemTime::MAX.checked_sub(Duration::new(0, 1)).is_some());
262+
assert!(SystemTime::MIN.checked_add(Duration::new(0, 1)).is_some());
263+
assert!(SystemTime::MIN.checked_sub(Duration::new(0, 1)).is_none());
264+
}

0 commit comments

Comments
 (0)