diff --git a/cap-primitives/src/rustix/fs/metadata_ext.rs b/cap-primitives/src/rustix/fs/metadata_ext.rs index 29813c43..7091017e 100644 --- a/cap-primitives/src/rustix/fs/metadata_ext.rs +++ b/cap-primitives/src/rustix/fs/metadata_ext.rs @@ -285,7 +285,14 @@ impl MetadataExt { #[allow(clippy::similar_names)] fn system_time_from_rustix(sec: i64, nsec: u64) -> Option { - SystemClock::UNIX_EPOCH.checked_add(Duration::new(u64::try_from(sec).unwrap(), nsec as _)) + if sec >= 0 { + SystemClock::UNIX_EPOCH.checked_add(Duration::new(u64::try_from(sec).unwrap(), nsec as _)) + } else { + SystemClock::UNIX_EPOCH + .checked_sub(Duration::new(u64::try_from(-sec).unwrap(), 0)) + .map(|t| t.checked_add(Duration::new(0, nsec as u32))) + .flatten() + } } impl rustix::fs::MetadataExt for MetadataExt { @@ -396,3 +403,15 @@ impl rustix::fs::MetadataExt for MetadataExt { self.ctim } } + +/// It should be possible to represent times before the Epoch. +/// https://github.com/bytecodealliance/cap-std/issues/328 +#[test] +fn negative_time() { + let system_time = system_time_from_rustix(-1, 1).unwrap(); + let d = SystemClock::UNIX_EPOCH.duration_since(system_time).unwrap(); + assert_eq!(d.as_secs(), 0); + if !cfg!(emulate_second_only_system) { + assert_eq!(d.subsec_nanos(), 999999999); + } +} diff --git a/cap-primitives/src/time/system_time.rs b/cap-primitives/src/time/system_time.rs index 2fe5d1ce..5444791d 100644 --- a/cap-primitives/src/time/system_time.rs +++ b/cap-primitives/src/time/system_time.rs @@ -37,10 +37,20 @@ impl SystemTime { #[inline] pub fn from_std(std: time::SystemTime) -> Self { if cfg!(emulate_second_only_system) { - let duration = std.duration_since(time::SystemTime::UNIX_EPOCH).unwrap(); - let secs = time::Duration::from_secs(duration.as_secs()); - Self { - std: time::SystemTime::UNIX_EPOCH.checked_add(secs).unwrap(), + match std.duration_since(time::SystemTime::UNIX_EPOCH) { + Ok(duration) => { + let secs = time::Duration::from_secs(duration.as_secs()); + Self { + std: time::SystemTime::UNIX_EPOCH.checked_add(secs).unwrap(), + } + } + Err(_) => { + let duration = time::SystemTime::UNIX_EPOCH.duration_since(std).unwrap(); + let secs = time::Duration::from_secs(duration.as_secs()); + Self { + std: time::SystemTime::UNIX_EPOCH.checked_sub(secs).unwrap(), + } + } } } else { Self { std }