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(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)]
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 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 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 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 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 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 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
231/// Set the last access and modification times for a file on the filesystem.
232///
233/// This function will set the `atime` and `mtime` metadata fields for a file
234/// on the local filesystem, returning any error encountered.
235pub fn set_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()>
236where
237    P: AsRef<Path>,
238{
239    imp::set_file_times(p.as_ref(), atime, mtime)
240}
241
242/// Set the last access and modification times for a file handle.
243///
244/// This function will either or both of  the `atime` and `mtime` metadata
245/// fields for a file handle , returning any error encountered. If `None` is
246/// specified then the time won't be updated. If `None` is specified for both
247/// options then no action is taken.
248pub fn set_file_handle_times(
249    f: &fs::File,
250    atime: Option<FileTime>,
251    mtime: Option<FileTime>,
252) -> io::Result<()> {
253    imp::set_file_handle_times(f, atime, mtime)
254}
255
256/// Set the last access and modification times for a file on the filesystem.
257/// This function does not follow symlink.
258///
259/// This function will set the `atime` and `mtime` metadata fields for a file
260/// on the local filesystem, returning any error encountered.
261pub fn set_symlink_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()>
262where
263    P: AsRef<Path>,
264{
265    imp::set_symlink_file_times(p.as_ref(), atime, mtime)
266}
267
268/// Set the last modification time for a file on the filesystem.
269///
270/// This function will set the `mtime` metadata field for a file on the local
271/// filesystem, returning any error encountered.
272///
273/// # Platform support
274///
275/// Where supported this will attempt to issue just one syscall to update only
276/// the `mtime`, but where not supported this may issue one syscall to learn the
277/// existing `atime` so only the `mtime` can be configured.
278pub fn set_file_mtime<P>(p: P, mtime: FileTime) -> io::Result<()>
279where
280    P: AsRef<Path>,
281{
282    imp::set_file_mtime(p.as_ref(), mtime)
283}
284
285/// Set the last access time for a file on the filesystem.
286///
287/// This function will set the `atime` metadata field for a file on the local
288/// filesystem, returning any error encountered.
289///
290/// # Platform support
291///
292/// Where supported this will attempt to issue just one syscall to update only
293/// the `atime`, but where not supported this may issue one syscall to learn the
294/// existing `mtime` so only the `atime` can be configured.
295pub fn set_file_atime<P>(p: P, atime: FileTime) -> io::Result<()>
296where
297    P: AsRef<Path>,
298{
299    imp::set_file_atime(p.as_ref(), atime)
300}
301
302#[cfg(test)]
303mod tests {
304    use super::{
305        set_file_atime, set_file_handle_times, set_file_mtime, set_file_times,
306        set_symlink_file_times, FileTime,
307    };
308    use std::fs::{self, File};
309    use std::io;
310    use std::path::Path;
311    use std::time::{Duration, UNIX_EPOCH};
312    use tempfile::Builder;
313
314    #[cfg(unix)]
315    fn make_symlink_file<P, Q>(src: P, dst: Q) -> io::Result<()>
316    where
317        P: AsRef<Path>,
318        Q: AsRef<Path>,
319    {
320        use std::os::unix::fs::symlink;
321        symlink(src, dst)
322    }
323
324    #[cfg(windows)]
325    fn make_symlink_file<P, Q>(src: P, dst: Q) -> io::Result<()>
326    where
327        P: AsRef<Path>,
328        Q: AsRef<Path>,
329    {
330        use std::os::windows::fs::symlink_file;
331        symlink_file(src, dst)
332    }
333
334    #[cfg(unix)]
335    fn make_symlink_dir<P, Q>(src: P, dst: Q) -> io::Result<()>
336    where
337        P: AsRef<Path>,
338        Q: AsRef<Path>,
339    {
340        use std::os::unix::fs::symlink;
341        symlink(src, dst)
342    }
343
344    #[cfg(windows)]
345    fn make_symlink_dir<P, Q>(src: P, dst: Q) -> io::Result<()>
346    where
347        P: AsRef<Path>,
348        Q: AsRef<Path>,
349    {
350        use std::os::windows::fs::symlink_dir;
351        symlink_dir(src, dst)
352    }
353
354    #[test]
355    #[cfg(windows)]
356    fn from_unix_time_test() {
357        let time = FileTime::from_unix_time(10, 100_000_000);
358        assert_eq!(11644473610, time.seconds);
359        assert_eq!(100_000_000, time.nanos);
360
361        let time = FileTime::from_unix_time(-10, 100_000_000);
362        assert_eq!(11644473590, time.seconds);
363        assert_eq!(100_000_000, time.nanos);
364
365        let time = FileTime::from_unix_time(-12_000_000_000, 0);
366        assert_eq!(-355526400, time.seconds);
367        assert_eq!(0, time.nanos);
368    }
369
370    #[test]
371    #[cfg(not(windows))]
372    fn from_unix_time_test() {
373        let time = FileTime::from_unix_time(10, 100_000_000);
374        assert_eq!(10, time.seconds);
375        assert_eq!(100_000_000, time.nanos);
376
377        let time = FileTime::from_unix_time(-10, 100_000_000);
378        assert_eq!(-10, time.seconds);
379        assert_eq!(100_000_000, time.nanos);
380
381        let time = FileTime::from_unix_time(-12_000_000_000, 0);
382        assert_eq!(-12_000_000_000, time.seconds);
383        assert_eq!(0, time.nanos);
384    }
385
386    #[test]
387    #[cfg(windows)]
388    fn from_system_time_test() {
389        let time = FileTime::from_system_time(UNIX_EPOCH + Duration::from_secs(10));
390        assert_eq!(11644473610, time.seconds);
391        assert_eq!(0, time.nanos);
392
393        let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(10));
394        assert_eq!(11644473590, time.seconds);
395        assert_eq!(0, time.nanos);
396
397        let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_millis(1100));
398        assert_eq!(11644473598, time.seconds);
399        assert_eq!(900_000_000, time.nanos);
400
401        let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(12_000_000_000));
402        assert_eq!(-355526400, time.seconds);
403        assert_eq!(0, time.nanos);
404    }
405
406    #[test]
407    #[cfg(not(windows))]
408    fn from_system_time_test() {
409        let time = FileTime::from_system_time(UNIX_EPOCH + Duration::from_secs(10));
410        assert_eq!(10, time.seconds);
411        assert_eq!(0, time.nanos);
412
413        let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(10));
414        assert_eq!(-10, time.seconds);
415        assert_eq!(0, time.nanos);
416
417        let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_millis(1100));
418        assert_eq!(-2, time.seconds);
419        assert_eq!(900_000_000, time.nanos);
420
421        let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(12_000_000));
422        assert_eq!(-12_000_000, time.seconds);
423        assert_eq!(0, time.nanos);
424    }
425
426    #[test]
427    fn set_file_times_test() -> io::Result<()> {
428        let td = Builder::new().prefix("filetime").tempdir()?;
429        let path = td.path().join("foo.txt");
430        let mut f = File::create(&path)?;
431
432        let metadata = fs::metadata(&path)?;
433        let mtime = FileTime::from_last_modification_time(&metadata);
434        let atime = FileTime::from_last_access_time(&metadata);
435        set_file_times(&path, atime, mtime)?;
436
437        let new_mtime = FileTime::from_unix_time(10_000, 0);
438        set_file_times(&path, atime, new_mtime)?;
439
440        let metadata = fs::metadata(&path)?;
441        let mtime = FileTime::from_last_modification_time(&metadata);
442        assert_eq!(mtime, new_mtime, "modification should be updated");
443
444        // Update just mtime
445        let new_mtime = FileTime::from_unix_time(20_000, 0);
446        set_file_handle_times(&mut f, None, Some(new_mtime))?;
447        let metadata = f.metadata()?;
448        let mtime = FileTime::from_last_modification_time(&metadata);
449        assert_eq!(mtime, new_mtime, "modification time should be updated");
450        let new_atime = FileTime::from_last_access_time(&metadata);
451        assert_eq!(atime, new_atime, "accessed time should not be updated");
452
453        // Update just atime
454        let new_atime = FileTime::from_unix_time(30_000, 0);
455        set_file_handle_times(&mut f, Some(new_atime), None)?;
456        let metadata = f.metadata()?;
457        let mtime = FileTime::from_last_modification_time(&metadata);
458        assert_eq!(mtime, new_mtime, "modification time should not be updated");
459        let atime = FileTime::from_last_access_time(&metadata);
460        assert_eq!(atime, new_atime, "accessed time should be updated");
461
462        let spath = td.path().join("bar.txt");
463        make_symlink_file(&path, &spath)?;
464        let metadata = fs::symlink_metadata(&spath)?;
465        let smtime = FileTime::from_last_modification_time(&metadata);
466
467        set_file_times(&spath, atime, mtime)?;
468
469        let metadata = fs::metadata(&path)?;
470        let cur_mtime = FileTime::from_last_modification_time(&metadata);
471        assert_eq!(mtime, cur_mtime);
472
473        let metadata = fs::symlink_metadata(&spath)?;
474        let cur_mtime = FileTime::from_last_modification_time(&metadata);
475        assert_eq!(smtime, cur_mtime);
476
477        set_file_times(&spath, atime, new_mtime)?;
478
479        let metadata = fs::metadata(&path)?;
480        let mtime = FileTime::from_last_modification_time(&metadata);
481        assert_eq!(mtime, new_mtime);
482
483        let metadata = fs::symlink_metadata(&spath)?;
484        let mtime = FileTime::from_last_modification_time(&metadata);
485        assert_eq!(mtime, smtime);
486        Ok(())
487    }
488
489    #[test]
490    fn set_dir_times_test() -> io::Result<()> {
491        let td = Builder::new().prefix("filetime").tempdir()?;
492        let path = td.path().join("foo");
493        fs::create_dir(&path)?;
494
495        let metadata = fs::metadata(&path)?;
496        let mtime = FileTime::from_last_modification_time(&metadata);
497        let atime = FileTime::from_last_access_time(&metadata);
498        set_file_times(&path, atime, mtime)?;
499
500        let new_mtime = FileTime::from_unix_time(10_000, 0);
501        set_file_times(&path, atime, new_mtime)?;
502
503        let metadata = fs::metadata(&path)?;
504        let mtime = FileTime::from_last_modification_time(&metadata);
505        assert_eq!(mtime, new_mtime, "modification should be updated");
506
507        // Update just mtime
508        let new_mtime = FileTime::from_unix_time(20_000, 0);
509        set_file_mtime(&path, new_mtime)?;
510        let metadata = fs::metadata(&path)?;
511        let mtime = FileTime::from_last_modification_time(&metadata);
512        assert_eq!(mtime, new_mtime, "modification time should be updated");
513        let new_atime = FileTime::from_last_access_time(&metadata);
514        assert_eq!(atime, new_atime, "accessed time should not be updated");
515
516        // Update just atime
517        let new_atime = FileTime::from_unix_time(30_000, 0);
518        set_file_atime(&path, new_atime)?;
519        let metadata = fs::metadata(&path)?;
520        let mtime = FileTime::from_last_modification_time(&metadata);
521        assert_eq!(mtime, new_mtime, "modification time should not be updated");
522        let atime = FileTime::from_last_access_time(&metadata);
523        assert_eq!(atime, new_atime, "accessed time should be updated");
524
525        let spath = td.path().join("bar");
526        make_symlink_dir(&path, &spath)?;
527        let metadata = fs::symlink_metadata(&spath)?;
528        let smtime = FileTime::from_last_modification_time(&metadata);
529
530        set_file_times(&spath, atime, mtime)?;
531
532        let metadata = fs::metadata(&path)?;
533        let cur_mtime = FileTime::from_last_modification_time(&metadata);
534        assert_eq!(mtime, cur_mtime);
535
536        let metadata = fs::symlink_metadata(&spath)?;
537        let cur_mtime = FileTime::from_last_modification_time(&metadata);
538        assert_eq!(smtime, cur_mtime);
539
540        set_file_times(&spath, atime, new_mtime)?;
541
542        let metadata = fs::metadata(&path)?;
543        let mtime = FileTime::from_last_modification_time(&metadata);
544        assert_eq!(mtime, new_mtime);
545
546        let metadata = fs::symlink_metadata(&spath)?;
547        let mtime = FileTime::from_last_modification_time(&metadata);
548        assert_eq!(mtime, smtime);
549        Ok(())
550    }
551
552    #[test]
553    fn set_file_times_pre_unix_epoch_test() {
554        let td = Builder::new().prefix("filetime").tempdir().unwrap();
555        let path = td.path().join("foo.txt");
556        File::create(&path).unwrap();
557
558        let metadata = fs::metadata(&path).unwrap();
559        let mtime = FileTime::from_last_modification_time(&metadata);
560        let atime = FileTime::from_last_access_time(&metadata);
561        set_file_times(&path, atime, mtime).unwrap();
562
563        let new_mtime = FileTime::from_unix_time(-10_000, 0);
564        set_file_times(&path, atime, new_mtime).unwrap();
565
566        let metadata = fs::metadata(&path).unwrap();
567        let mtime = FileTime::from_last_modification_time(&metadata);
568        assert_eq!(mtime, new_mtime);
569    }
570
571    #[test]
572    #[cfg(windows)]
573    fn set_file_times_pre_windows_epoch_test() {
574        let td = Builder::new().prefix("filetime").tempdir().unwrap();
575        let path = td.path().join("foo.txt");
576        File::create(&path).unwrap();
577
578        let metadata = fs::metadata(&path).unwrap();
579        let mtime = FileTime::from_last_modification_time(&metadata);
580        let atime = FileTime::from_last_access_time(&metadata);
581        set_file_times(&path, atime, mtime).unwrap();
582
583        let new_mtime = FileTime::from_unix_time(-12_000_000_000, 0);
584        assert!(set_file_times(&path, atime, new_mtime).is_err());
585    }
586
587    #[test]
588    fn set_symlink_file_times_test() {
589        let td = Builder::new().prefix("filetime").tempdir().unwrap();
590        let path = td.path().join("foo.txt");
591        File::create(&path).unwrap();
592
593        let metadata = fs::metadata(&path).unwrap();
594        let mtime = FileTime::from_last_modification_time(&metadata);
595        let atime = FileTime::from_last_access_time(&metadata);
596        set_symlink_file_times(&path, atime, mtime).unwrap();
597
598        let new_mtime = FileTime::from_unix_time(10_000, 0);
599        set_symlink_file_times(&path, atime, new_mtime).unwrap();
600
601        let metadata = fs::metadata(&path).unwrap();
602        let mtime = FileTime::from_last_modification_time(&metadata);
603        assert_eq!(mtime, new_mtime);
604
605        let spath = td.path().join("bar.txt");
606        make_symlink_file(&path, &spath).unwrap();
607
608        let metadata = fs::symlink_metadata(&spath).unwrap();
609        let smtime = FileTime::from_last_modification_time(&metadata);
610        let satime = FileTime::from_last_access_time(&metadata);
611        set_symlink_file_times(&spath, smtime, satime).unwrap();
612
613        let metadata = fs::metadata(&path).unwrap();
614        let mtime = FileTime::from_last_modification_time(&metadata);
615        assert_eq!(mtime, new_mtime);
616
617        let new_smtime = FileTime::from_unix_time(20_000, 0);
618        set_symlink_file_times(&spath, atime, new_smtime).unwrap();
619
620        let metadata = fs::metadata(&spath).unwrap();
621        let mtime = FileTime::from_last_modification_time(&metadata);
622        assert_eq!(mtime, new_mtime);
623
624        let metadata = fs::symlink_metadata(&spath).unwrap();
625        let mtime = FileTime::from_last_modification_time(&metadata);
626        assert_eq!(mtime, new_smtime);
627    }
628
629    #[test]
630    fn set_symlink_dir_times_test() {
631        let td = Builder::new().prefix("filetime").tempdir().unwrap();
632        let path = td.path().join("foo");
633        fs::create_dir(&path);
634
635        let metadata = fs::metadata(&path).unwrap();
636        let mtime = FileTime::from_last_modification_time(&metadata);
637        let atime = FileTime::from_last_access_time(&metadata);
638        set_symlink_file_times(&path, atime, mtime).unwrap();
639
640        let new_mtime = FileTime::from_unix_time(10_000, 0);
641        set_symlink_file_times(&path, atime, new_mtime).unwrap();
642
643        let metadata = fs::metadata(&path).unwrap();
644        let mtime = FileTime::from_last_modification_time(&metadata);
645        assert_eq!(mtime, new_mtime);
646
647        let spath = td.path().join("bar");
648        make_symlink_dir(&path, &spath).unwrap();
649
650        let metadata = fs::symlink_metadata(&spath).unwrap();
651        let smtime = FileTime::from_last_modification_time(&metadata);
652        let satime = FileTime::from_last_access_time(&metadata);
653        set_symlink_file_times(&spath, smtime, satime).unwrap();
654
655        let metadata = fs::metadata(&path).unwrap();
656        let mtime = FileTime::from_last_modification_time(&metadata);
657        assert_eq!(mtime, new_mtime);
658
659        let new_smtime = FileTime::from_unix_time(20_000, 0);
660        set_symlink_file_times(&spath, atime, new_smtime).unwrap();
661
662        let metadata = fs::metadata(&spath).unwrap();
663        let mtime = FileTime::from_last_modification_time(&metadata);
664        assert_eq!(mtime, new_mtime);
665
666        let metadata = fs::symlink_metadata(&spath).unwrap();
667        let mtime = FileTime::from_last_modification_time(&metadata);
668        assert_eq!(mtime, new_smtime);
669    }
670
671    #[test]
672    fn set_single_time_test() {
673        use super::{set_file_atime, set_file_mtime};
674
675        let td = Builder::new().prefix("filetime").tempdir().unwrap();
676        let path = td.path().join("foo.txt");
677        File::create(&path).unwrap();
678
679        let metadata = fs::metadata(&path).unwrap();
680        let mtime = FileTime::from_last_modification_time(&metadata);
681        let atime = FileTime::from_last_access_time(&metadata);
682        set_file_times(&path, atime, mtime).unwrap();
683
684        let new_mtime = FileTime::from_unix_time(10_000, 0);
685        set_file_mtime(&path, new_mtime).unwrap();
686
687        let metadata = fs::metadata(&path).unwrap();
688        let mtime = FileTime::from_last_modification_time(&metadata);
689        assert_eq!(mtime, new_mtime, "modification time should be updated");
690        assert_eq!(
691            atime,
692            FileTime::from_last_access_time(&metadata),
693            "access time should not be updated",
694        );
695
696        let new_atime = FileTime::from_unix_time(20_000, 0);
697        set_file_atime(&path, new_atime).unwrap();
698
699        let metadata = fs::metadata(&path).unwrap();
700        let atime = FileTime::from_last_access_time(&metadata);
701        assert_eq!(atime, new_atime, "access time should be updated");
702        assert_eq!(
703            mtime,
704            FileTime::from_last_modification_time(&metadata),
705            "modification time should not be updated"
706        );
707    }
708}