Skip to main content

filetime/
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.2"
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
37use std::fmt;
38use std::fs;
39use std::io;
40use std::path::Path;
41use std::time::{Duration, SystemTime, UNIX_EPOCH};
42
43cfg_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(all(target_family = "wasm", not(target_os = "emscripten")))] {
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)]
65pub struct FileTime {
66    seconds: i64,
67    nanos: u32,
68}
69
70impl 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 const fn zero() -> FileTime {
75        FileTime {
76            seconds: 0,
77            nanos: 0,
78        }
79    }
80
81    const 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 timestamp representing the current system time.
93    ///
94    /// ```
95    /// # use filetime::FileTime;
96    /// #
97    /// # fn example() -> std::io::Result<()> {
98    /// #     let path = "";
99    /// #
100    /// filetime::set_file_mtime(path, FileTime::now())?;
101    /// #
102    /// #     Ok(())
103    /// # }
104    /// ```
105    ///
106    /// Equivalent to `FileTime::from_system_time(SystemTime::now())`.
107    pub fn now() -> FileTime {
108        FileTime::from_system_time(SystemTime::now())
109    }
110
111    /// Creates a new instance of `FileTime` with a number of seconds and
112    /// nanoseconds relative to the Unix epoch, 1970-01-01T00:00:00Z.
113    ///
114    /// Negative seconds represent times before the Unix epoch, and positive
115    /// values represent times after it. Nanos always count forwards in time.
116    ///
117    /// Note that this is typically the relative point that Unix time stamps are
118    /// from, but on Windows the native time stamp is relative to January 1,
119    /// 1601 so the return value of `seconds` from the returned `FileTime`
120    /// instance may not be the same as that passed in.
121    pub const fn from_unix_time(seconds: i64, nanos: u32) -> FileTime {
122        FileTime {
123            seconds: seconds + if cfg!(windows) { 11644473600 } else { 0 },
124            nanos,
125        }
126        .emulate_second_only_system()
127    }
128
129    /// Creates a new timestamp from the last modification time listed in the
130    /// specified metadata.
131    ///
132    /// The returned value corresponds to the `mtime` field of `stat` on Unix
133    /// platforms and the `ftLastWriteTime` field on Windows platforms.
134    pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
135        imp::from_last_modification_time(meta).emulate_second_only_system()
136    }
137
138    /// Creates a new timestamp from the last access time listed in the
139    /// specified metadata.
140    ///
141    /// The returned value corresponds to the `atime` field of `stat` on Unix
142    /// platforms and the `ftLastAccessTime` field on Windows platforms.
143    pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
144        imp::from_last_access_time(meta).emulate_second_only_system()
145    }
146
147    /// Creates a new timestamp from the creation time listed in the specified
148    /// metadata.
149    ///
150    /// The returned value corresponds to the `birthtime` field of `stat` on
151    /// Unix platforms and the `ftCreationTime` field on Windows platforms. Note
152    /// that not all Unix platforms have this field available and may return
153    /// `None` in some circumstances.
154    pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> {
155        imp::from_creation_time(meta).map(|x| x.emulate_second_only_system())
156    }
157
158    /// Creates a new timestamp from the given SystemTime.
159    ///
160    /// Windows counts file times since 1601-01-01T00:00:00Z, and cannot
161    /// represent times before this, but it's possible to create a SystemTime
162    /// that does. This function will error if passed such a SystemTime.
163    pub fn from_system_time(time: SystemTime) -> FileTime {
164        let epoch = if cfg!(windows) {
165            UNIX_EPOCH - Duration::from_secs(11644473600)
166        } else {
167            UNIX_EPOCH
168        };
169
170        time.duration_since(epoch)
171            .map(|d| FileTime {
172                seconds: d.as_secs() as i64,
173                nanos: d.subsec_nanos(),
174            })
175            .unwrap_or_else(|e| {
176                let until_epoch = e.duration();
177                let (sec_offset, nanos) = if until_epoch.subsec_nanos() == 0 {
178                    (0, 0)
179                } else {
180                    (-1, 1_000_000_000 - until_epoch.subsec_nanos())
181                };
182
183                FileTime {
184                    seconds: -1 * until_epoch.as_secs() as i64 + sec_offset,
185                    nanos,
186                }
187            })
188            .emulate_second_only_system()
189    }
190
191    /// Returns the whole number of seconds represented by this timestamp.
192    ///
193    /// Note that this value's meaning is **platform specific**. On Unix
194    /// platform time stamps are typically relative to January 1, 1970, but on
195    /// Windows platforms time stamps are relative to January 1, 1601.
196    pub const fn seconds(&self) -> i64 {
197        self.seconds
198    }
199
200    /// Returns the whole number of seconds represented by this timestamp,
201    /// relative to the Unix epoch start of January 1, 1970.
202    ///
203    /// Note that this does not return the same value as `seconds` for Windows
204    /// platforms as seconds are relative to a different date there.
205    pub const fn unix_seconds(&self) -> i64 {
206        self.seconds - if cfg!(windows) { 11644473600 } else { 0 }
207    }
208
209    /// Returns the nanosecond precision of this timestamp.
210    ///
211    /// The returned value is always less than one billion and represents a
212    /// portion of a second forward from the seconds returned by the `seconds`
213    /// method.
214    pub const fn nanoseconds(&self) -> u32 {
215        self.nanos
216    }
217}
218
219impl fmt::Display for FileTime {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        write!(f, "{}.{:09}s", self.seconds, self.nanos)
222    }
223}
224
225impl From<SystemTime> for FileTime {
226    fn from(time: SystemTime) -> FileTime {
227        FileTime::from_system_time(time)
228    }
229}
230
231impl From<FileTime> for SystemTime {
232    fn from(time: FileTime) -> SystemTime {
233        let seconds = time.unix_seconds();
234        if seconds < 0 {
235            SystemTime::UNIX_EPOCH - Duration::from_secs(-seconds as u64)
236                + Duration::from_nanos(time.nanoseconds() as u64)
237        } else {
238            SystemTime::UNIX_EPOCH + Duration::new(seconds as u64, time.nanoseconds())
239        }
240    }
241}
242
243/// Set the last access and modification times for a file on the filesystem.
244///
245/// This function will set the `atime` and `mtime` metadata fields for a file
246/// on the local filesystem, returning any error encountered.
247pub fn set_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()>
248where
249    P: AsRef<Path>,
250{
251    imp::open(p.as_ref())?.set_times(
252        fs::FileTimes::new()
253            .set_accessed(atime.into())
254            .set_modified(mtime.into()),
255    )
256}
257
258/// Set the last access and modification times for a file handle.
259///
260/// This function will either or both of  the `atime` and `mtime` metadata
261/// fields for a file handle , returning any error encountered. If `None` is
262/// specified then the time won't be updated. If `None` is specified for both
263/// options then no action is taken.
264pub fn set_file_handle_times(
265    f: &fs::File,
266    atime: Option<FileTime>,
267    mtime: Option<FileTime>,
268) -> io::Result<()> {
269    let mut times = fs::FileTimes::new();
270    if let Some(t) = atime {
271        times = times.set_accessed(t.into());
272    }
273    if let Some(t) = mtime {
274        times = times.set_modified(t.into());
275    }
276    f.set_times(times)
277}
278
279/// Set the last access and modification times for a file on the filesystem.
280/// This function does not follow symlink.
281///
282/// This function will set the `atime` and `mtime` metadata fields for a file
283/// on the local filesystem, returning any error encountered.
284pub fn set_symlink_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()>
285where
286    P: AsRef<Path>,
287{
288    imp::set_symlink_file_times(p.as_ref(), atime, mtime)
289}
290
291/// Set the last modification time for a file on the filesystem.
292///
293/// This function will set the `mtime` metadata field for a file on the local
294/// filesystem, returning any error encountered.
295///
296/// # Platform support
297///
298/// Where supported this will attempt to issue just one syscall to update only
299/// the `mtime`, but where not supported this may issue one syscall to learn the
300/// existing `atime` so only the `mtime` can be configured.
301pub fn set_file_mtime<P>(p: P, mtime: FileTime) -> io::Result<()>
302where
303    P: AsRef<Path>,
304{
305    imp::open(p.as_ref())?.set_times(fs::FileTimes::new().set_modified(mtime.into()))
306}
307
308/// Set the last access time for a file on the filesystem.
309///
310/// This function will set the `atime` metadata field for a file on the local
311/// filesystem, returning any error encountered.
312///
313/// # Platform support
314///
315/// Where supported this will attempt to issue just one syscall to update only
316/// the `atime`, but where not supported this may issue one syscall to learn the
317/// existing `mtime` so only the `atime` can be configured.
318pub fn set_file_atime<P>(p: P, atime: FileTime) -> io::Result<()>
319where
320    P: AsRef<Path>,
321{
322    imp::open(p.as_ref())?.set_times(fs::FileTimes::new().set_accessed(atime.into()))
323}
324
325#[cfg(test)]
326mod tests {
327    use super::{
328        set_file_atime, set_file_handle_times, set_file_mtime, set_file_times,
329        set_symlink_file_times, FileTime,
330    };
331    use std::fs::{self, File};
332    use std::io;
333    use std::path::Path;
334    use std::time::{Duration, UNIX_EPOCH};
335    use tempfile::Builder;
336
337    #[cfg(unix)]
338    fn make_symlink_file<P, Q>(src: P, dst: Q) -> io::Result<()>
339    where
340        P: AsRef<Path>,
341        Q: AsRef<Path>,
342    {
343        use std::os::unix::fs::symlink;
344        symlink(src, dst)
345    }
346
347    #[cfg(windows)]
348    fn make_symlink_file<P, Q>(src: P, dst: Q) -> io::Result<()>
349    where
350        P: AsRef<Path>,
351        Q: AsRef<Path>,
352    {
353        use std::os::windows::fs::symlink_file;
354        symlink_file(src, dst)
355    }
356
357    #[cfg(unix)]
358    fn make_symlink_dir<P, Q>(src: P, dst: Q) -> io::Result<()>
359    where
360        P: AsRef<Path>,
361        Q: AsRef<Path>,
362    {
363        use std::os::unix::fs::symlink;
364        symlink(src, dst)
365    }
366
367    #[cfg(windows)]
368    fn make_symlink_dir<P, Q>(src: P, dst: Q) -> io::Result<()>
369    where
370        P: AsRef<Path>,
371        Q: AsRef<Path>,
372    {
373        use std::os::windows::fs::symlink_dir;
374        symlink_dir(src, dst)
375    }
376
377    #[test]
378    #[cfg(windows)]
379    fn from_unix_time_test() {
380        let time = FileTime::from_unix_time(10, 100_000_000);
381        assert_eq!(11644473610, time.seconds);
382        assert_eq!(100_000_000, time.nanos);
383
384        let time = FileTime::from_unix_time(-10, 100_000_000);
385        assert_eq!(11644473590, time.seconds);
386        assert_eq!(100_000_000, time.nanos);
387
388        let time = FileTime::from_unix_time(-12_000_000_000, 0);
389        assert_eq!(-355526400, time.seconds);
390        assert_eq!(0, time.nanos);
391    }
392
393    #[test]
394    #[cfg(not(windows))]
395    fn from_unix_time_test() {
396        let time = FileTime::from_unix_time(10, 100_000_000);
397        assert_eq!(10, time.seconds);
398        assert_eq!(100_000_000, time.nanos);
399
400        let time = FileTime::from_unix_time(-10, 100_000_000);
401        assert_eq!(-10, time.seconds);
402        assert_eq!(100_000_000, time.nanos);
403
404        let time = FileTime::from_unix_time(-12_000_000_000, 0);
405        assert_eq!(-12_000_000_000, time.seconds);
406        assert_eq!(0, time.nanos);
407    }
408
409    #[test]
410    #[cfg(windows)]
411    fn to_from_system_time_test() {
412        let systime = UNIX_EPOCH + Duration::from_secs(10);
413        let time = FileTime::from_system_time(systime);
414        assert_eq!(11644473610, time.seconds);
415        assert_eq!(0, time.nanos);
416        assert_eq!(systime, time.into());
417
418        let systime = UNIX_EPOCH - Duration::from_secs(10);
419        let time = FileTime::from_system_time(systime);
420        assert_eq!(11644473590, time.seconds);
421        assert_eq!(0, time.nanos);
422        assert_eq!(systime, time.into());
423
424        let systime = UNIX_EPOCH - Duration::from_millis(1100);
425        let time = FileTime::from_system_time(systime);
426        assert_eq!(11644473598, time.seconds);
427        assert_eq!(900_000_000, time.nanos);
428        assert_eq!(systime, time.into());
429    }
430
431    #[test]
432    #[cfg(not(windows))]
433    fn to_from_system_time_test() {
434        let systime = UNIX_EPOCH + Duration::from_secs(10);
435        let time = FileTime::from_system_time(systime);
436        assert_eq!(10, time.seconds);
437        assert_eq!(0, time.nanos);
438        assert_eq!(systime, time.into());
439
440        let systime = UNIX_EPOCH - Duration::from_secs(10);
441        let time = FileTime::from_system_time(systime);
442        assert_eq!(-10, time.seconds);
443        assert_eq!(0, time.nanos);
444        assert_eq!(systime, time.into());
445
446        let systime = UNIX_EPOCH - Duration::from_millis(1100);
447        let time = FileTime::from_system_time(systime);
448        assert_eq!(-2, time.seconds);
449        assert_eq!(900_000_000, time.nanos);
450        assert_eq!(systime, time.into());
451
452        let systime = UNIX_EPOCH - Duration::from_secs(12_000_000);
453        let time = FileTime::from_system_time(systime);
454        assert_eq!(-12_000_000, time.seconds);
455        assert_eq!(0, time.nanos);
456        assert_eq!(systime, time.into());
457    }
458
459    #[test]
460    fn set_file_times_test() -> io::Result<()> {
461        let td = Builder::new().prefix("filetime").tempdir()?;
462        let path = td.path().join("foo.txt");
463        let mut f = File::create(&path)?;
464
465        let metadata = fs::metadata(&path)?;
466        let mtime = FileTime::from_last_modification_time(&metadata);
467        let atime = FileTime::from_last_access_time(&metadata);
468        set_file_times(&path, atime, mtime)?;
469
470        let new_mtime = FileTime::from_unix_time(10_000, 0);
471        set_file_times(&path, atime, new_mtime)?;
472
473        let metadata = fs::metadata(&path)?;
474        let mtime = FileTime::from_last_modification_time(&metadata);
475        assert_eq!(mtime, new_mtime, "modification should be updated");
476
477        // Update just mtime
478        let new_mtime = FileTime::from_unix_time(20_000, 0);
479        set_file_handle_times(&mut f, None, Some(new_mtime))?;
480        let metadata = f.metadata()?;
481        let mtime = FileTime::from_last_modification_time(&metadata);
482        assert_eq!(mtime, new_mtime, "modification time should be updated");
483        let new_atime = FileTime::from_last_access_time(&metadata);
484        assert_eq!(atime, new_atime, "accessed time should not be updated");
485
486        // Update just atime
487        let new_atime = FileTime::from_unix_time(30_000, 0);
488        set_file_handle_times(&mut f, Some(new_atime), None)?;
489        let metadata = f.metadata()?;
490        let mtime = FileTime::from_last_modification_time(&metadata);
491        assert_eq!(mtime, new_mtime, "modification time should not be updated");
492        let atime = FileTime::from_last_access_time(&metadata);
493        assert_eq!(atime, new_atime, "accessed time should be updated");
494
495        let spath = td.path().join("bar.txt");
496        make_symlink_file(&path, &spath)?;
497        let metadata = fs::symlink_metadata(&spath)?;
498        let smtime = FileTime::from_last_modification_time(&metadata);
499
500        set_file_times(&spath, atime, mtime)?;
501
502        let metadata = fs::metadata(&path)?;
503        let cur_mtime = FileTime::from_last_modification_time(&metadata);
504        assert_eq!(mtime, cur_mtime);
505
506        let metadata = fs::symlink_metadata(&spath)?;
507        let cur_mtime = FileTime::from_last_modification_time(&metadata);
508        assert_eq!(smtime, cur_mtime);
509
510        set_file_times(&spath, atime, new_mtime)?;
511
512        let metadata = fs::metadata(&path)?;
513        let mtime = FileTime::from_last_modification_time(&metadata);
514        assert_eq!(mtime, new_mtime);
515
516        let metadata = fs::symlink_metadata(&spath)?;
517        let mtime = FileTime::from_last_modification_time(&metadata);
518        assert_eq!(mtime, smtime);
519        Ok(())
520    }
521
522    #[test]
523    fn set_dir_times_test() -> io::Result<()> {
524        let td = Builder::new().prefix("filetime").tempdir()?;
525        let path = td.path().join("foo");
526        fs::create_dir(&path)?;
527
528        let metadata = fs::metadata(&path)?;
529        let mtime = FileTime::from_last_modification_time(&metadata);
530        let atime = FileTime::from_last_access_time(&metadata);
531        set_file_times(&path, atime, mtime)?;
532
533        let new_mtime = FileTime::from_unix_time(10_000, 0);
534        set_file_times(&path, atime, new_mtime)?;
535
536        let metadata = fs::metadata(&path)?;
537        let mtime = FileTime::from_last_modification_time(&metadata);
538        assert_eq!(mtime, new_mtime, "modification should be updated");
539
540        // Update just mtime
541        let new_mtime = FileTime::from_unix_time(20_000, 0);
542        set_file_mtime(&path, new_mtime)?;
543        let metadata = fs::metadata(&path)?;
544        let mtime = FileTime::from_last_modification_time(&metadata);
545        assert_eq!(mtime, new_mtime, "modification time should be updated");
546        let new_atime = FileTime::from_last_access_time(&metadata);
547        assert_eq!(atime, new_atime, "accessed time should not be updated");
548
549        // Update just atime
550        let new_atime = FileTime::from_unix_time(30_000, 0);
551        set_file_atime(&path, new_atime)?;
552        let metadata = fs::metadata(&path)?;
553        let mtime = FileTime::from_last_modification_time(&metadata);
554        assert_eq!(mtime, new_mtime, "modification time should not be updated");
555        let atime = FileTime::from_last_access_time(&metadata);
556        assert_eq!(atime, new_atime, "accessed time should be updated");
557
558        let spath = td.path().join("bar");
559        make_symlink_dir(&path, &spath)?;
560        let metadata = fs::symlink_metadata(&spath)?;
561        let smtime = FileTime::from_last_modification_time(&metadata);
562
563        set_file_times(&spath, atime, mtime)?;
564
565        let metadata = fs::metadata(&path)?;
566        let cur_mtime = FileTime::from_last_modification_time(&metadata);
567        assert_eq!(mtime, cur_mtime);
568
569        let metadata = fs::symlink_metadata(&spath)?;
570        let cur_mtime = FileTime::from_last_modification_time(&metadata);
571        assert_eq!(smtime, cur_mtime);
572
573        set_file_times(&spath, atime, new_mtime)?;
574
575        let metadata = fs::metadata(&path)?;
576        let mtime = FileTime::from_last_modification_time(&metadata);
577        assert_eq!(mtime, new_mtime);
578
579        let metadata = fs::symlink_metadata(&spath)?;
580        let mtime = FileTime::from_last_modification_time(&metadata);
581        assert_eq!(mtime, smtime);
582        Ok(())
583    }
584
585    #[test]
586    fn set_file_times_pre_unix_epoch_test() {
587        let td = Builder::new().prefix("filetime").tempdir().unwrap();
588        let path = td.path().join("foo.txt");
589        File::create(&path).unwrap();
590
591        let metadata = fs::metadata(&path).unwrap();
592        let mtime = FileTime::from_last_modification_time(&metadata);
593        let atime = FileTime::from_last_access_time(&metadata);
594        set_file_times(&path, atime, mtime).unwrap();
595
596        let new_mtime = FileTime::from_unix_time(-10_000, 0);
597        if cfg!(target_os = "aix") {
598            // On AIX, os checks if the unix timestamp is valid.
599            let result = set_file_times(&path, atime, new_mtime);
600            assert!(result.is_err());
601            assert!(result.err().unwrap().kind() == std::io::ErrorKind::InvalidInput);
602        } else {
603            set_file_times(&path, atime, new_mtime).unwrap();
604
605            let metadata = fs::metadata(&path).unwrap();
606            let mtime = FileTime::from_last_modification_time(&metadata);
607            assert_eq!(mtime, new_mtime);
608        }
609    }
610
611    #[test]
612    #[cfg(windows)]
613    fn set_file_times_pre_windows_epoch_test() {
614        let td = Builder::new().prefix("filetime").tempdir().unwrap();
615        let path = td.path().join("foo.txt");
616        File::create(&path).unwrap();
617
618        let metadata = fs::metadata(&path).unwrap();
619        let mtime = FileTime::from_last_modification_time(&metadata);
620        let atime = FileTime::from_last_access_time(&metadata);
621        set_file_times(&path, atime, mtime).unwrap();
622    }
623
624    #[test]
625    fn set_symlink_file_times_test() {
626        let td = Builder::new().prefix("filetime").tempdir().unwrap();
627        let path = td.path().join("foo.txt");
628        File::create(&path).unwrap();
629
630        let metadata = fs::metadata(&path).unwrap();
631        let mtime = FileTime::from_last_modification_time(&metadata);
632        let atime = FileTime::from_last_access_time(&metadata);
633        set_symlink_file_times(&path, atime, mtime).unwrap();
634
635        let new_mtime = FileTime::from_unix_time(10_000, 0);
636        set_symlink_file_times(&path, atime, new_mtime).unwrap();
637
638        let metadata = fs::metadata(&path).unwrap();
639        let mtime = FileTime::from_last_modification_time(&metadata);
640        assert_eq!(mtime, new_mtime);
641
642        let spath = td.path().join("bar.txt");
643        make_symlink_file(&path, &spath).unwrap();
644
645        let metadata = fs::symlink_metadata(&spath).unwrap();
646        let smtime = FileTime::from_last_modification_time(&metadata);
647        let satime = FileTime::from_last_access_time(&metadata);
648        set_symlink_file_times(&spath, smtime, satime).unwrap();
649
650        let metadata = fs::metadata(&path).unwrap();
651        let mtime = FileTime::from_last_modification_time(&metadata);
652        assert_eq!(mtime, new_mtime);
653
654        let new_smtime = FileTime::from_unix_time(20_000, 0);
655        set_symlink_file_times(&spath, atime, new_smtime).unwrap();
656
657        let metadata = fs::metadata(&spath).unwrap();
658        let mtime = FileTime::from_last_modification_time(&metadata);
659        assert_eq!(mtime, new_mtime);
660
661        let metadata = fs::symlink_metadata(&spath).unwrap();
662        let mtime = FileTime::from_last_modification_time(&metadata);
663        assert_eq!(mtime, new_smtime);
664    }
665
666    #[test]
667    fn set_symlink_dir_times_test() {
668        let td = Builder::new().prefix("filetime").tempdir().unwrap();
669        let path = td.path().join("foo");
670        fs::create_dir(&path).unwrap();
671
672        let metadata = fs::metadata(&path).unwrap();
673        let mtime = FileTime::from_last_modification_time(&metadata);
674        let atime = FileTime::from_last_access_time(&metadata);
675        set_symlink_file_times(&path, atime, mtime).unwrap();
676
677        let new_mtime = FileTime::from_unix_time(10_000, 0);
678        set_symlink_file_times(&path, atime, new_mtime).unwrap();
679
680        let metadata = fs::metadata(&path).unwrap();
681        let mtime = FileTime::from_last_modification_time(&metadata);
682        assert_eq!(mtime, new_mtime);
683
684        let spath = td.path().join("bar");
685        make_symlink_dir(&path, &spath).unwrap();
686
687        let metadata = fs::symlink_metadata(&spath).unwrap();
688        let smtime = FileTime::from_last_modification_time(&metadata);
689        let satime = FileTime::from_last_access_time(&metadata);
690        set_symlink_file_times(&spath, smtime, satime).unwrap();
691
692        let metadata = fs::metadata(&path).unwrap();
693        let mtime = FileTime::from_last_modification_time(&metadata);
694        assert_eq!(mtime, new_mtime);
695
696        let new_smtime = FileTime::from_unix_time(20_000, 0);
697        set_symlink_file_times(&spath, atime, new_smtime).unwrap();
698
699        let metadata = fs::metadata(&spath).unwrap();
700        let mtime = FileTime::from_last_modification_time(&metadata);
701        assert_eq!(mtime, new_mtime);
702
703        let metadata = fs::symlink_metadata(&spath).unwrap();
704        let mtime = FileTime::from_last_modification_time(&metadata);
705        assert_eq!(mtime, new_smtime);
706    }
707
708    #[test]
709    fn set_single_time_test() {
710        use super::{set_file_atime, set_file_mtime};
711
712        let td = Builder::new().prefix("filetime").tempdir().unwrap();
713        let path = td.path().join("foo.txt");
714        File::create(&path).unwrap();
715
716        let metadata = fs::metadata(&path).unwrap();
717        let mtime = FileTime::from_last_modification_time(&metadata);
718        let atime = FileTime::from_last_access_time(&metadata);
719        set_file_times(&path, atime, mtime).unwrap();
720
721        let new_mtime = FileTime::from_unix_time(10_000, 0);
722        set_file_mtime(&path, new_mtime).unwrap();
723
724        let metadata = fs::metadata(&path).unwrap();
725        let mtime = FileTime::from_last_modification_time(&metadata);
726        assert_eq!(mtime, new_mtime, "modification time should be updated");
727        assert_eq!(
728            atime,
729            FileTime::from_last_access_time(&metadata),
730            "access time should not be updated",
731        );
732
733        let new_atime = FileTime::from_unix_time(20_000, 0);
734        set_file_atime(&path, new_atime).unwrap();
735
736        let metadata = fs::metadata(&path).unwrap();
737        let atime = FileTime::from_last_access_time(&metadata);
738        assert_eq!(atime, new_atime, "access time should be updated");
739        assert_eq!(
740            mtime,
741            FileTime::from_last_modification_time(&metadata),
742            "modification time should not be updated"
743        );
744    }
745}