]> git.proxmox.com Git - cargo.git/blob - vendor/filetime/src/lib.rs
New upstream version 0.35.0
[cargo.git] / vendor / filetime / src / lib.rs
1 //! Timestamps for files in Rust
2 //!
3 //! This library provides platform-agnostic inspection of the various timestamps
4 //! present in the standard `fs::Metadata` structure.
5 //!
6 //! # Installation
7 //!
8 //! Add this to your `Cargo.toml`:
9 //!
10 //! ```toml
11 //! [dependencies]
12 //! filetime = "0.1"
13 //! ```
14 //!
15 //! # Usage
16 //!
17 //! ```no_run
18 //! use std::fs;
19 //! use filetime::FileTime;
20 //!
21 //! let metadata = fs::metadata("foo.txt").unwrap();
22 //!
23 //! let mtime = FileTime::from_last_modification_time(&metadata);
24 //! println!("{}", mtime);
25 //!
26 //! let atime = FileTime::from_last_access_time(&metadata);
27 //! assert!(mtime < atime);
28 //!
29 //! // Inspect values that can be interpreted across platforms
30 //! println!("{}", mtime.unix_seconds());
31 //! println!("{}", mtime.nanoseconds());
32 //!
33 //! // Print the platform-specific value of seconds
34 //! println!("{}", mtime.seconds());
35 //! ```
36
37 use std::fmt;
38 use std::fs;
39 use std::io;
40 use std::path::Path;
41 use std::time::{Duration, SystemTime, UNIX_EPOCH};
42
43 cfg_if::cfg_if! {
44 if #[cfg(target_os = "redox")] {
45 #[path = "redox.rs"]
46 mod imp;
47 } else if #[cfg(windows)] {
48 #[path = "windows.rs"]
49 mod imp;
50 } else if #[cfg(target_arch = "wasm32")] {
51 #[path = "wasm.rs"]
52 mod imp;
53 } else {
54 #[path = "unix/mod.rs"]
55 mod imp;
56 }
57 }
58
59 /// A helper structure to represent a timestamp for a file.
60 ///
61 /// The actual value contined within is platform-specific and does not have the
62 /// same meaning across platforms, but comparisons and stringification can be
63 /// significant among the same platform.
64 #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
65 pub struct FileTime {
66 seconds: i64,
67 nanos: u32,
68 }
69
70 impl FileTime {
71 /// Creates a new timestamp representing a 0 time.
72 ///
73 /// Useful for creating the base of a cmp::max chain of times.
74 pub fn zero() -> FileTime {
75 FileTime {
76 seconds: 0,
77 nanos: 0,
78 }
79 }
80
81 fn emulate_second_only_system(self) -> FileTime {
82 if cfg!(emulate_second_only_system) {
83 FileTime {
84 seconds: self.seconds,
85 nanos: 0,
86 }
87 } else {
88 self
89 }
90 }
91
92 /// Creates a new instance of `FileTime` with a number of seconds and
93 /// nanoseconds relative to the Unix epoch, 1970-01-01T00:00:00Z.
94 ///
95 /// Negative seconds represent times before the Unix epoch, and positive
96 /// values represent times after it. Nanos always count forwards in time.
97 ///
98 /// Note that this is typically the relative point that Unix time stamps are
99 /// from, but on Windows the native time stamp is relative to January 1,
100 /// 1601 so the return value of `seconds` from the returned `FileTime`
101 /// instance may not be the same as that passed in.
102 pub fn from_unix_time(seconds: i64, nanos: u32) -> FileTime {
103 FileTime {
104 seconds: seconds + if cfg!(windows) { 11644473600 } else { 0 },
105 nanos,
106 }
107 .emulate_second_only_system()
108 }
109
110 /// Creates a new timestamp from the last modification time listed in the
111 /// specified metadata.
112 ///
113 /// The returned value corresponds to the `mtime` field of `stat` on Unix
114 /// platforms and the `ftLastWriteTime` field on Windows platforms.
115 pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
116 imp::from_last_modification_time(meta).emulate_second_only_system()
117 }
118
119 /// Creates a new timestamp from the last access time listed in the
120 /// specified metadata.
121 ///
122 /// The returned value corresponds to the `atime` field of `stat` on Unix
123 /// platforms and the `ftLastAccessTime` field on Windows platforms.
124 pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
125 imp::from_last_access_time(meta).emulate_second_only_system()
126 }
127
128 /// Creates a new timestamp from the creation time listed in the specified
129 /// metadata.
130 ///
131 /// The returned value corresponds to the `birthtime` field of `stat` on
132 /// Unix platforms and the `ftCreationTime` field on Windows platforms. Note
133 /// that not all Unix platforms have this field available and may return
134 /// `None` in some circumstances.
135 pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> {
136 imp::from_creation_time(meta).map(|x| x.emulate_second_only_system())
137 }
138
139 /// Creates a new timestamp from the given SystemTime.
140 ///
141 /// Windows counts file times since 1601-01-01T00:00:00Z, and cannot
142 /// represent times before this, but it's possible to create a SystemTime
143 /// that does. This function will error if passed such a SystemTime.
144 pub fn from_system_time(time: SystemTime) -> FileTime {
145 let epoch = if cfg!(windows) {
146 UNIX_EPOCH - Duration::from_secs(11644473600)
147 } else {
148 UNIX_EPOCH
149 };
150
151 time.duration_since(epoch)
152 .map(|d| FileTime {
153 seconds: d.as_secs() as i64,
154 nanos: d.subsec_nanos(),
155 })
156 .unwrap_or_else(|e| {
157 let until_epoch = e.duration();
158 let (sec_offset, nanos) = if until_epoch.subsec_nanos() == 0 {
159 (0, 0)
160 } else {
161 (-1, 1_000_000_000 - until_epoch.subsec_nanos())
162 };
163
164 FileTime {
165 seconds: -1 * until_epoch.as_secs() as i64 + sec_offset,
166 nanos,
167 }
168 })
169 .emulate_second_only_system()
170 }
171
172 /// Returns the whole number of seconds represented by this timestamp.
173 ///
174 /// Note that this value's meaning is **platform specific**. On Unix
175 /// platform time stamps are typically relative to January 1, 1970, but on
176 /// Windows platforms time stamps are relative to January 1, 1601.
177 pub fn seconds(&self) -> i64 {
178 self.seconds
179 }
180
181 /// Returns the whole number of seconds represented by this timestamp,
182 /// relative to the Unix epoch start of January 1, 1970.
183 ///
184 /// Note that this does not return the same value as `seconds` for Windows
185 /// platforms as seconds are relative to a different date there.
186 pub fn unix_seconds(&self) -> i64 {
187 self.seconds - if cfg!(windows) { 11644473600 } else { 0 }
188 }
189
190 /// Returns the nanosecond precision of this timestamp.
191 ///
192 /// The returned value is always less than one billion and represents a
193 /// portion of a second forward from the seconds returned by the `seconds`
194 /// method.
195 pub fn nanoseconds(&self) -> u32 {
196 self.nanos
197 }
198 }
199
200 impl fmt::Display for FileTime {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 write!(f, "{}.{:09}s", self.seconds, self.nanos)
203 }
204 }
205
206 impl From<SystemTime> for FileTime {
207 fn from(time: SystemTime) -> FileTime {
208 FileTime::from_system_time(time)
209 }
210 }
211
212 /// Set the last access and modification times for a file on the filesystem.
213 ///
214 /// This function will set the `atime` and `mtime` metadata fields for a file
215 /// on the local filesystem, returning any error encountered.
216 pub fn set_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()>
217 where
218 P: AsRef<Path>,
219 {
220 imp::set_file_times(p.as_ref(), atime, mtime)
221 }
222
223 /// Set the last access and modification times for a file handle.
224 ///
225 /// This function will either or both of the `atime` and `mtime` metadata
226 /// fields for a file handle , returning any error encountered. If `None` is
227 /// specified then the time won't be updated. If `None` is specified for both
228 /// options then no action is taken.
229 pub fn set_file_handle_times(
230 f: &fs::File,
231 atime: Option<FileTime>,
232 mtime: Option<FileTime>,
233 ) -> io::Result<()> {
234 imp::set_file_handle_times(f, atime, mtime)
235 }
236
237 /// Set the last access and modification times for a file on the filesystem.
238 /// This function does not follow symlink.
239 ///
240 /// This function will set the `atime` and `mtime` metadata fields for a file
241 /// on the local filesystem, returning any error encountered.
242 pub fn set_symlink_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()>
243 where
244 P: AsRef<Path>,
245 {
246 imp::set_symlink_file_times(p.as_ref(), atime, mtime)
247 }
248
249 /// Set the last modification time for a file on the filesystem.
250 ///
251 /// This function will set the `mtime` metadata field for a file on the local
252 /// filesystem, returning any error encountered.
253 ///
254 /// # Platform support
255 ///
256 /// Where supported this will attempt to issue just one syscall to update only
257 /// the `mtime`, but where not supported this may issue one syscall to learn the
258 /// existing `atime` so only the `mtime` can be configured.
259 pub fn set_file_mtime<P>(p: P, mtime: FileTime) -> io::Result<()>
260 where
261 P: AsRef<Path>,
262 {
263 imp::set_file_mtime(p.as_ref(), mtime)
264 }
265
266 /// Set the last access time for a file on the filesystem.
267 ///
268 /// This function will set the `atime` metadata field for a file on the local
269 /// filesystem, returning any error encountered.
270 ///
271 /// # Platform support
272 ///
273 /// Where supported this will attempt to issue just one syscall to update only
274 /// the `atime`, but where not supported this may issue one syscall to learn the
275 /// existing `mtime` so only the `atime` can be configured.
276 pub fn set_file_atime<P>(p: P, atime: FileTime) -> io::Result<()>
277 where
278 P: AsRef<Path>,
279 {
280 imp::set_file_atime(p.as_ref(), atime)
281 }
282
283 #[cfg(test)]
284 mod tests {
285 use tempdir::TempDir;
286 use super::{set_file_handle_times, set_file_times, set_symlink_file_times, FileTime};
287 use std::fs::{self, File};
288 use std::io;
289 use std::path::Path;
290 use std::time::{Duration, UNIX_EPOCH};
291
292 #[cfg(unix)]
293 fn make_symlink<P, Q>(src: P, dst: Q) -> io::Result<()>
294 where
295 P: AsRef<Path>,
296 Q: AsRef<Path>,
297 {
298 use std::os::unix::fs::symlink;
299 symlink(src, dst)
300 }
301
302 #[cfg(windows)]
303 fn make_symlink<P, Q>(src: P, dst: Q) -> io::Result<()>
304 where
305 P: AsRef<Path>,
306 Q: AsRef<Path>,
307 {
308 use std::os::windows::fs::symlink_file;
309 symlink_file(src, dst)
310 }
311
312 #[test]
313 #[cfg(windows)]
314 fn from_unix_time_test() {
315 let time = FileTime::from_unix_time(10, 100_000_000);
316 assert_eq!(11644473610, time.seconds);
317 assert_eq!(100_000_000, time.nanos);
318
319 let time = FileTime::from_unix_time(-10, 100_000_000);
320 assert_eq!(11644473590, time.seconds);
321 assert_eq!(100_000_000, time.nanos);
322
323 let time = FileTime::from_unix_time(-12_000_000_000, 0);
324 assert_eq!(-355526400, time.seconds);
325 assert_eq!(0, time.nanos);
326 }
327
328 #[test]
329 #[cfg(not(windows))]
330 fn from_unix_time_test() {
331 let time = FileTime::from_unix_time(10, 100_000_000);
332 assert_eq!(10, time.seconds);
333 assert_eq!(100_000_000, time.nanos);
334
335 let time = FileTime::from_unix_time(-10, 100_000_000);
336 assert_eq!(-10, time.seconds);
337 assert_eq!(100_000_000, time.nanos);
338
339 let time = FileTime::from_unix_time(-12_000_000_000, 0);
340 assert_eq!(-12_000_000_000, time.seconds);
341 assert_eq!(0, time.nanos);
342 }
343
344 #[test]
345 #[cfg(windows)]
346 fn from_system_time_test() {
347 let time = FileTime::from_system_time(UNIX_EPOCH + Duration::from_secs(10));
348 assert_eq!(11644473610, time.seconds);
349 assert_eq!(0, time.nanos);
350
351 let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(10));
352 assert_eq!(11644473590, time.seconds);
353 assert_eq!(0, time.nanos);
354
355 let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_millis(1100));
356 assert_eq!(11644473598, time.seconds);
357 assert_eq!(900_000_000, time.nanos);
358
359 let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(12_000_000_000));
360 assert_eq!(-355526400, time.seconds);
361 assert_eq!(0, time.nanos);
362 }
363
364 #[test]
365 #[cfg(not(windows))]
366 fn from_system_time_test() {
367 let time = FileTime::from_system_time(UNIX_EPOCH + Duration::from_secs(10));
368 assert_eq!(10, time.seconds);
369 assert_eq!(0, time.nanos);
370
371 let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(10));
372 assert_eq!(-10, time.seconds);
373 assert_eq!(0, time.nanos);
374
375 let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_millis(1100));
376 assert_eq!(-2, time.seconds);
377 assert_eq!(900_000_000, time.nanos);
378
379 let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(12_000_000));
380 assert_eq!(-12_000_000, time.seconds);
381 assert_eq!(0, time.nanos);
382 }
383
384 #[test]
385 fn set_file_times_test() -> io::Result<()> {
386 let td = TempDir::new("filetime")?;
387 let path = td.path().join("foo.txt");
388 let mut f = File::create(&path)?;
389
390 let metadata = fs::metadata(&path)?;
391 let mtime = FileTime::from_last_modification_time(&metadata);
392 let atime = FileTime::from_last_access_time(&metadata);
393 set_file_times(&path, atime, mtime)?;
394
395 let new_mtime = FileTime::from_unix_time(10_000, 0);
396 set_file_times(&path, atime, new_mtime)?;
397
398 let metadata = fs::metadata(&path)?;
399 let mtime = FileTime::from_last_modification_time(&metadata);
400 assert_eq!(mtime, new_mtime);
401
402 // Update just mtime
403 let new_mtime = FileTime::from_unix_time(20_000, 0);
404 set_file_handle_times(&mut f, None, Some(new_mtime))?;
405 let metadata = f.metadata()?;
406 let mtime = FileTime::from_last_modification_time(&metadata);
407 assert_eq!(mtime, new_mtime);
408 let new_atime = FileTime::from_last_access_time(&metadata);
409 assert_eq!(atime, new_atime);
410
411 // Update just atime
412 let new_atime = FileTime::from_unix_time(30_000, 0);
413 set_file_handle_times(&mut f, Some(new_atime), None)?;
414 let metadata = f.metadata()?;
415 let mtime = FileTime::from_last_modification_time(&metadata);
416 assert_eq!(mtime, new_mtime);
417 let atime = FileTime::from_last_access_time(&metadata);
418 assert_eq!(atime, new_atime);
419
420 let spath = td.path().join("bar.txt");
421 make_symlink(&path, &spath)?;
422 let metadata = fs::symlink_metadata(&spath)?;
423 let smtime = FileTime::from_last_modification_time(&metadata);
424
425 set_file_times(&spath, atime, mtime)?;
426
427 let metadata = fs::metadata(&path)?;
428 let cur_mtime = FileTime::from_last_modification_time(&metadata);
429 assert_eq!(mtime, cur_mtime);
430
431 let metadata = fs::symlink_metadata(&spath)?;
432 let cur_mtime = FileTime::from_last_modification_time(&metadata);
433 assert_eq!(smtime, cur_mtime);
434
435 set_file_times(&spath, atime, new_mtime)?;
436
437 let metadata = fs::metadata(&path)?;
438 let mtime = FileTime::from_last_modification_time(&metadata);
439 assert_eq!(mtime, new_mtime);
440
441 let metadata = fs::symlink_metadata(&spath)?;
442 let mtime = FileTime::from_last_modification_time(&metadata);
443 assert_eq!(mtime, smtime);
444 Ok(())
445 }
446
447 #[test]
448 fn set_file_times_pre_unix_epoch_test() {
449 let td = TempDir::new("filetime").unwrap();
450 let path = td.path().join("foo.txt");
451 File::create(&path).unwrap();
452
453 let metadata = fs::metadata(&path).unwrap();
454 let mtime = FileTime::from_last_modification_time(&metadata);
455 let atime = FileTime::from_last_access_time(&metadata);
456 set_file_times(&path, atime, mtime).unwrap();
457
458 let new_mtime = FileTime::from_unix_time(-10_000, 0);
459 set_file_times(&path, atime, new_mtime).unwrap();
460
461 let metadata = fs::metadata(&path).unwrap();
462 let mtime = FileTime::from_last_modification_time(&metadata);
463 assert_eq!(mtime, new_mtime);
464 }
465
466 #[test]
467 #[cfg(windows)]
468 fn set_file_times_pre_windows_epoch_test() {
469 let td = TempDir::new("filetime").unwrap();
470 let path = td.path().join("foo.txt");
471 File::create(&path).unwrap();
472
473 let metadata = fs::metadata(&path).unwrap();
474 let mtime = FileTime::from_last_modification_time(&metadata);
475 let atime = FileTime::from_last_access_time(&metadata);
476 set_file_times(&path, atime, mtime).unwrap();
477
478 let new_mtime = FileTime::from_unix_time(-12_000_000_000, 0);
479 assert!(set_file_times(&path, atime, new_mtime).is_err());
480 }
481
482 #[test]
483 fn set_symlink_file_times_test() {
484 let td = TempDir::new("filetime").unwrap();
485 let path = td.path().join("foo.txt");
486 File::create(&path).unwrap();
487
488 let metadata = fs::metadata(&path).unwrap();
489 let mtime = FileTime::from_last_modification_time(&metadata);
490 let atime = FileTime::from_last_access_time(&metadata);
491 set_symlink_file_times(&path, atime, mtime).unwrap();
492
493 let new_mtime = FileTime::from_unix_time(10_000, 0);
494 set_symlink_file_times(&path, atime, new_mtime).unwrap();
495
496 let metadata = fs::metadata(&path).unwrap();
497 let mtime = FileTime::from_last_modification_time(&metadata);
498 assert_eq!(mtime, new_mtime);
499
500 let spath = td.path().join("bar.txt");
501 make_symlink(&path, &spath).unwrap();
502
503 let metadata = fs::symlink_metadata(&spath).unwrap();
504 let smtime = FileTime::from_last_modification_time(&metadata);
505 let satime = FileTime::from_last_access_time(&metadata);
506 set_symlink_file_times(&spath, smtime, satime).unwrap();
507
508 let metadata = fs::metadata(&path).unwrap();
509 let mtime = FileTime::from_last_modification_time(&metadata);
510 assert_eq!(mtime, new_mtime);
511
512 let new_smtime = FileTime::from_unix_time(20_000, 0);
513 set_symlink_file_times(&spath, atime, new_smtime).unwrap();
514
515 let metadata = fs::metadata(&spath).unwrap();
516 let mtime = FileTime::from_last_modification_time(&metadata);
517 assert_eq!(mtime, new_mtime);
518
519 let metadata = fs::symlink_metadata(&spath).unwrap();
520 let mtime = FileTime::from_last_modification_time(&metadata);
521 assert_eq!(mtime, new_smtime);
522 }
523
524 #[test]
525 fn set_single_time_test() {
526 use super::{set_file_atime, set_file_mtime};
527
528 let td = TempDir::new("filetime").unwrap();
529 let path = td.path().join("foo.txt");
530 File::create(&path).unwrap();
531
532 let metadata = fs::metadata(&path).unwrap();
533 let mtime = FileTime::from_last_modification_time(&metadata);
534 let atime = FileTime::from_last_access_time(&metadata);
535 set_file_times(&path, atime, mtime).unwrap();
536
537 let new_mtime = FileTime::from_unix_time(10_000, 0);
538 set_file_mtime(&path, new_mtime).unwrap();
539
540 let metadata = fs::metadata(&path).unwrap();
541 let mtime = FileTime::from_last_modification_time(&metadata);
542 assert_eq!(mtime, new_mtime);
543 assert_eq!(atime, FileTime::from_last_access_time(&metadata));
544
545 let new_atime = FileTime::from_unix_time(20_000, 0);
546 set_file_atime(&path, new_atime).unwrap();
547
548 let metadata = fs::metadata(&path).unwrap();
549 let atime = FileTime::from_last_access_time(&metadata);
550 assert_eq!(atime, new_atime);
551 assert_eq!(mtime, FileTime::from_last_modification_time(&metadata));
552 }
553 }