@@ -66,10 +66,11 @@ impl Metadata {
66
66
#[ cfg( target_os = "aix" ) ]
67
67
let nanoseconds = self . 0 . st_mtim . tv_nsec ;
68
68
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 ( ) ?)
73
74
}
74
75
#[ cfg( windows) ]
75
76
self . 0 . modified ( ) . ok ( )
@@ -94,10 +95,11 @@ impl Metadata {
94
95
#[ cfg( target_os = "aix" ) ]
95
96
let nanoseconds = self . 0 . st_ctim . tv_nsec ;
96
97
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 ( ) ?)
101
103
}
102
104
#[ cfg( windows) ]
103
105
self . 0 . created ( ) . ok ( )
@@ -186,11 +188,30 @@ impl Metadata {
186
188
}
187
189
188
190
#[ 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 {
192
213
std:: time:: UNIX_EPOCH - d
193
214
} else {
194
215
std:: time:: UNIX_EPOCH + d
195
- }
216
+ } )
196
217
}
0 commit comments