use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WallTime {
since_epoch: Duration,
}
impl WallTime {
pub const UNIX_EPOCH: Self = Self {
since_epoch: Duration::ZERO,
};
pub fn now() -> Self {
SystemTime::now().into()
}
pub fn from_unix(secs: u64, nanos: u32) -> Self {
Self {
since_epoch: Duration::new(secs, nanos),
}
}
pub fn since_unix_epoch(self) -> Duration {
self.since_epoch
}
pub fn unix_secs(self) -> u64 {
self.since_epoch.as_secs()
}
pub fn subsec_nanos(self) -> u32 {
self.since_epoch.subsec_nanos()
}
pub fn saturating_sub(self, d: Duration) -> Self {
Self {
since_epoch: self.since_epoch.saturating_sub(d),
}
}
}
impl From<SystemTime> for WallTime {
fn from(t: SystemTime) -> Self {
Self {
since_epoch: t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default(),
}
}
}
impl From<WallTime> for SystemTime {
fn from(t: WallTime) -> Self {
SystemTime::UNIX_EPOCH + t.since_epoch
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_unix_preserves_full_nanoseconds() {
let t = WallTime::from_unix(42, 123_456_789);
assert_eq!(t.unix_secs(), 42);
assert_eq!(t.subsec_nanos(), 123_456_789);
assert_eq!(t.since_unix_epoch(), Duration::new(42, 123_456_789));
}
#[test]
fn systemtime_round_trip_through_unix_epoch() {
let st = SystemTime::UNIX_EPOCH + Duration::from_secs(1_700_000_000);
let wt: WallTime = st.into();
assert_eq!(wt.unix_secs(), 1_700_000_000);
let back: SystemTime = wt.into();
assert_eq!(back, st);
}
#[test]
fn pre_epoch_systemtime_clamps_to_epoch() {
let before = SystemTime::UNIX_EPOCH - Duration::from_secs(5);
let wt: WallTime = before.into();
assert_eq!(wt, WallTime::UNIX_EPOCH);
}
#[test]
fn saturating_sub_floors_at_epoch() {
let t = WallTime::from_unix(0, 500);
assert_eq!(
t.saturating_sub(Duration::from_secs(1)),
WallTime::UNIX_EPOCH
);
assert_eq!(
WallTime::from_unix(10, 0).saturating_sub(Duration::from_secs(3)),
WallTime::from_unix(7, 0)
);
}
#[test]
fn ordering_matches_chronology() {
assert!(WallTime::from_unix(1, 0) < WallTime::from_unix(1, 1));
assert!(WallTime::from_unix(2, 0) > WallTime::from_unix(1, 999_999_999));
assert_eq!(WallTime::UNIX_EPOCH, WallTime::from_unix(0, 0));
}
}