Skip to content

Commit bf49cd4

Browse files
committed
assure negative nanoseconds on MacOS are handled
Copied from https://github.com/rust-lang/rust at a8ece1190bf6b340175bc5b688e52bd29924f483
1 parent f4a29dc commit bf49cd4

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

gix-index/src/fs.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ impl Metadata {
6666
#[cfg(target_os = "aix")]
6767
let nanoseconds = self.0.st_mtim.tv_nsec;
6868

69-
Some(system_time_from_secs_nanos(
70-
seconds.try_into().ok()?,
71-
nanoseconds.try_into().ok()?,
72-
))
69+
// All operating systems treat the seconds as offset from unix epoch, hence it must
70+
// be signed in order to deal with dates before epoch.
71+
// Rustix seems to think this value is u64, but we fix it here for now.
72+
let seconds = seconds as i64;
73+
system_time_from_secs_nanos(seconds, nanoseconds.try_into().ok()?)
7374
}
7475
#[cfg(windows)]
7576
self.0.modified().ok()
@@ -94,10 +95,11 @@ impl Metadata {
9495
#[cfg(target_os = "aix")]
9596
let nanoseconds = self.0.st_ctim.tv_nsec;
9697

97-
Some(system_time_from_secs_nanos(
98-
seconds.try_into().ok()?,
99-
nanoseconds.try_into().ok()?,
100-
))
98+
// All operating systems treat the seconds as offset from unix epoch, hence it must
99+
// be signed in order to deal with dates before epoch.
100+
// Rustix seems to think this value is u64, but we fix it here for now.
101+
let seconds = seconds as i64;
102+
system_time_from_secs_nanos(seconds, nanoseconds.try_into().ok()?)
101103
}
102104
#[cfg(windows)]
103105
self.0.created().ok()
@@ -186,11 +188,30 @@ impl Metadata {
186188
}
187189

188190
#[cfg(not(windows))]
189-
fn system_time_from_secs_nanos(secs: i64, nanos: u32) -> SystemTime {
190-
let d = std::time::Duration::new(secs.abs_diff(0), nanos);
191-
if secs < 0 {
191+
fn system_time_from_secs_nanos(secs: i64, nanos: i32) -> Option<SystemTime> {
192+
// Copied from https://github.com/rust-lang/rust at a8ece1190bf6b340175bc5b688e52bd29924f483, MIT licensed, and adapted.
193+
// On Apple OS, dates before epoch are represented differently than on other
194+
// Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1`
195+
// and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and
196+
// `nanoseconds=-900_000_000` on Apple OS.
197+
//
198+
// To compensate, we first detect this special case by checking if both
199+
// seconds and nanoseconds are in range, and then correct the value for seconds
200+
// and nanoseconds to match the common unix representation.
201+
//
202+
// Please note that Apple OS nonetheless accepts the standard unix format when
203+
// setting file times, which makes this compensation round-trippable and generally
204+
// transparent.
205+
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
206+
let (secs, nanos) = if (secs <= 0 && secs > i64::MIN) && (nanos < 0 && nanos > -1_000_000_000) {
207+
(secs - 1, nanos + 1_000_000_000)
208+
} else {
209+
(secs, nanos)
210+
};
211+
let d = std::time::Duration::new(secs.abs_diff(0), nanos.try_into().ok()?);
212+
Some(if secs < 0 {
192213
std::time::UNIX_EPOCH - d
193214
} else {
194215
std::time::UNIX_EPOCH + d
195-
}
216+
})
196217
}

0 commit comments

Comments
 (0)