1 //! Timestamps for files in Rust
3 //! This library provides platform-agnostic inspection of the various timestamps
4 //! present in the standard `fs::Metadata` structure.
8 //! Add this to your `Cargo.toml`:
19 //! use filetime::FileTime;
21 //! let metadata = fs::metadata("foo.txt").unwrap();
23 //! let mtime = FileTime::from_last_modification_time(&metadata);
24 //! println!("{}", mtime);
26 //! let atime = FileTime::from_last_access_time(&metadata);
27 //! assert!(mtime < atime);
29 //! // Inspect values that can be interpreted across platforms
30 //! println!("{}", mtime.unix_seconds());
31 //! println!("{}", mtime.nanoseconds());
33 //! // Print the platform-specific value of seconds
34 //! println!("{}", mtime.seconds());
41 use std
::time
::{Duration, SystemTime, UNIX_EPOCH}
;
44 if #[cfg(target_os = "redox")] {
47 } else if #[cfg(windows)] {
48 #[path = "windows.rs"]
50 } else if #[cfg(target_arch = "wasm32")] {
54 #[path = "unix/mod.rs"]
59 /// A helper structure to represent a timestamp for a file.
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)]
71 /// Creates a new timestamp representing a 0 time.
73 /// Useful for creating the base of a cmp::max chain of times.
74 pub fn zero() -> FileTime
{
81 fn emulate_second_only_system(self) -> FileTime
{
82 if cfg
!(emulate_second_only_system
) {
84 seconds
: self.seconds
,
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.
95 /// Negative seconds represent times before the Unix epoch, and positive
96 /// values represent times after it. Nanos always count forwards in time.
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
{
104 seconds
: seconds
+ if cfg
!(windows
) { 11644473600 }
else { 0 }
,
107 .emulate_second_only_system()
110 /// Creates a new timestamp from the last modification time listed in the
111 /// specified metadata.
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()
119 /// Creates a new timestamp from the last access time listed in the
120 /// specified metadata.
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()
128 /// Creates a new timestamp from the creation time listed in the specified
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())
139 /// Creates a new timestamp from the given SystemTime.
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)
151 time
.duration_since(epoch
)
153 seconds
: d
.as_secs() as i64,
154 nanos
: d
.subsec_nanos(),
156 .unwrap_or_else(|e
| {
157 let until_epoch
= e
.duration();
158 let (sec_offset
, nanos
) = if until_epoch
.subsec_nanos() == 0 {
161 (-1, 1_000_000_000 - until_epoch
.subsec_nanos())
165 seconds
: -1 * until_epoch
.as_secs() as i64 + sec_offset
,
169 .emulate_second_only_system()
172 /// Returns the whole number of seconds represented by this timestamp.
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 {
181 /// Returns the whole number of seconds represented by this timestamp,
182 /// relative to the Unix epoch start of January 1, 1970.
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 }
190 /// Returns the nanosecond precision of this timestamp.
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`
195 pub fn nanoseconds(&self) -> u32 {
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
)
206 impl From
<SystemTime
> for FileTime
{
207 fn from(time
: SystemTime
) -> FileTime
{
208 FileTime
::from_system_time(time
)
212 /// Set the last access and modification times for a file on the filesystem.
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
<()>
220 imp
::set_file_times(p
.as_ref(), atime
, mtime
)
223 /// Set the last access and modification times for a file handle.
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(
231 atime
: Option
<FileTime
>,
232 mtime
: Option
<FileTime
>,
233 ) -> io
::Result
<()> {
234 imp
::set_file_handle_times(f
, atime
, mtime
)
237 /// Set the last access and modification times for a file on the filesystem.
238 /// This function does not follow symlink.
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
<()>
246 imp
::set_symlink_file_times(p
.as_ref(), atime
, mtime
)
249 /// Set the last modification time for a file on the filesystem.
251 /// This function will set the `mtime` metadata field for a file on the local
252 /// filesystem, returning any error encountered.
254 /// # Platform support
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
<()>
263 imp
::set_file_mtime(p
.as_ref(), mtime
)
266 /// Set the last access time for a file on the filesystem.
268 /// This function will set the `atime` metadata field for a file on the local
269 /// filesystem, returning any error encountered.
271 /// # Platform support
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
<()>
280 imp
::set_file_atime(p
.as_ref(), atime
)
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}
;
290 use std
::time
::{Duration, UNIX_EPOCH}
;
293 fn make_symlink
<P
, Q
>(src
: P
, dst
: Q
) -> io
::Result
<()>
298 use std
::os
::unix
::fs
::symlink
;
303 fn make_symlink
<P
, Q
>(src
: P
, dst
: Q
) -> io
::Result
<()>
308 use std
::os
::windows
::fs
::symlink_file
;
309 symlink_file(src
, dst
)
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
);
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
);
323 let time
= FileTime
::from_unix_time(-12_000_000_000, 0);
324 assert_eq
!(-355526400, time
.seconds
);
325 assert_eq
!(0, time
.nanos
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
)?
;
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
)?
;
395 let new_mtime
= FileTime
::from_unix_time(10_000, 0);
396 set_file_times(&path
, atime
, new_mtime
)?
;
398 let metadata
= fs
::metadata(&path
)?
;
399 let mtime
= FileTime
::from_last_modification_time(&metadata
);
400 assert_eq
!(mtime
, new_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
);
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
);
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
);
425 set_file_times(&spath
, atime
, mtime
)?
;
427 let metadata
= fs
::metadata(&path
)?
;
428 let cur_mtime
= FileTime
::from_last_modification_time(&metadata
);
429 assert_eq
!(mtime
, cur_mtime
);
431 let metadata
= fs
::symlink_metadata(&spath
)?
;
432 let cur_mtime
= FileTime
::from_last_modification_time(&metadata
);
433 assert_eq
!(smtime
, cur_mtime
);
435 set_file_times(&spath
, atime
, new_mtime
)?
;
437 let metadata
= fs
::metadata(&path
)?
;
438 let mtime
= FileTime
::from_last_modification_time(&metadata
);
439 assert_eq
!(mtime
, new_mtime
);
441 let metadata
= fs
::symlink_metadata(&spath
)?
;
442 let mtime
= FileTime
::from_last_modification_time(&metadata
);
443 assert_eq
!(mtime
, smtime
);
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();
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();
458 let new_mtime
= FileTime
::from_unix_time(-10_000, 0);
459 set_file_times(&path
, atime
, new_mtime
).unwrap();
461 let metadata
= fs
::metadata(&path
).unwrap();
462 let mtime
= FileTime
::from_last_modification_time(&metadata
);
463 assert_eq
!(mtime
, new_mtime
);
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();
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();
478 let new_mtime
= FileTime
::from_unix_time(-12_000_000_000, 0);
479 assert
!(set_file_times(&path
, atime
, new_mtime
).is_err());
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();
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();
493 let new_mtime
= FileTime
::from_unix_time(10_000, 0);
494 set_symlink_file_times(&path
, atime
, new_mtime
).unwrap();
496 let metadata
= fs
::metadata(&path
).unwrap();
497 let mtime
= FileTime
::from_last_modification_time(&metadata
);
498 assert_eq
!(mtime
, new_mtime
);
500 let spath
= td
.path().join("bar.txt");
501 make_symlink(&path
, &spath
).unwrap();
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();
508 let metadata
= fs
::metadata(&path
).unwrap();
509 let mtime
= FileTime
::from_last_modification_time(&metadata
);
510 assert_eq
!(mtime
, new_mtime
);
512 let new_smtime
= FileTime
::from_unix_time(20_000, 0);
513 set_symlink_file_times(&spath
, atime
, new_smtime
).unwrap();
515 let metadata
= fs
::metadata(&spath
).unwrap();
516 let mtime
= FileTime
::from_last_modification_time(&metadata
);
517 assert_eq
!(mtime
, new_mtime
);
519 let metadata
= fs
::symlink_metadata(&spath
).unwrap();
520 let mtime
= FileTime
::from_last_modification_time(&metadata
);
521 assert_eq
!(mtime
, new_smtime
);
525 fn set_single_time_test() {
526 use super::{set_file_atime, set_file_mtime}
;
528 let td
= TempDir
::new("filetime").unwrap();
529 let path
= td
.path().join("foo.txt");
530 File
::create(&path
).unwrap();
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();
537 let new_mtime
= FileTime
::from_unix_time(10_000, 0);
538 set_file_mtime(&path
, new_mtime
).unwrap();
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
));
545 let new_atime
= FileTime
::from_unix_time(20_000, 0);
546 set_file_atime(&path
, new_atime
).unwrap();
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
));