time: Add saturating arithmetic for SystemTime

This commit implements the following methods:
* `SystemTime::saturating_add`
* `SystemTime::saturating_sub`
* `SystemTime::saturating_duration_since`

The implementation of these methods is rather trivial, as the main logic
lies behind the constants `SystemTime::MIN` and `SystemTime::MAX`.
This commit is contained in:
Clara Engler 2026-01-16 10:39:35 +01:00
parent bcf787a780
commit 567b569e2b
No known key found for this signature in database
GPG key ID: 4CBE96203A1A78A7
2 changed files with 89 additions and 0 deletions

View file

@ -682,6 +682,56 @@ impl SystemTime {
pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_sub_duration(&duration).map(SystemTime)
}
/// Saturating [`SystemTime`] addition, computing `self + duration`,
/// returning [`SystemTime::MAX`] if overflow occurred.
///
/// In the case that the `duration` is smaller than the time precision of
/// the operating system, `self` will be returned.
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
pub fn saturating_add(&self, duration: Duration) -> SystemTime {
self.checked_add(duration).unwrap_or(SystemTime::MAX)
}
/// Saturating [`SystemTime`] subtraction, computing `self - duration`,
/// returning [`SystemTime::MIN`] if overflow occurred.
///
/// In the case that the `duration` is smaller than the time precision of
/// the operating system, `self` will be returned.
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
pub fn saturating_sub(&self, duration: Duration) -> SystemTime {
self.checked_sub(duration).unwrap_or(SystemTime::MIN)
}
/// Saturating computation of time elapsed from an earlier point in time,
/// returning [`Duration::ZERO`] in the case that `earlier` is later or
/// equal to `self`.
///
/// # Examples
///
/// ```no_run
/// #![feature(time_saturating_systemtime)]
/// use std::time::{Duration, SystemTime};
///
/// let now = SystemTime::now();
/// let prev = now.saturating_sub(Duration::new(1, 0));
///
/// // now - prev should return non-zero.
/// assert_eq!(now.saturating_duration_since(prev), Duration::new(1, 0));
/// assert!(now.duration_since(prev).is_ok());
///
/// // prev - now should return zero (and fail with the non-saturating).
/// assert_eq!(prev.saturating_duration_since(now), Duration::ZERO);
/// assert!(prev.duration_since(now).is_err());
///
/// // now - now should return zero (and work with the non-saturating).
/// assert_eq!(now.saturating_duration_since(now), Duration::ZERO);
/// assert!(now.duration_since(now).is_ok());
/// ```
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
pub fn saturating_duration_since(&self, earlier: SystemTime) -> Duration {
self.duration_since(earlier).unwrap_or(Duration::ZERO)
}
}
#[stable(feature = "time2", since = "1.8.0")]

View file

@ -1,5 +1,6 @@
#![feature(duration_constants)]
#![feature(time_systemtime_limits)]
#![feature(time_saturating_systemtime)]
use std::fmt::Debug;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@ -269,3 +270,41 @@ fn system_time_max_min() {
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
);
}