diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b5a682955c0..343d71abb64 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -25,6 +25,7 @@ use sys_common::io::read_to_end_uninitialized; use sys_common::{AsInnerMut, FromInner, AsInner, IntoInner}; use vec::Vec; +use time::SystemTime; /// A reference to an open file on the filesystem. /// @@ -660,6 +661,52 @@ pub fn len(&self) -> u64 { self.0.size() } pub fn permissions(&self) -> Permissions { Permissions(self.0.perm()) } + + /// Returns the last modification time listed in this metadata. + /// + /// The returned value corresponds to the `mtime` field of `stat` on Unix + /// platforms and the `ftLastWriteTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + #[unstable(feature = "fs_time", issue = "31399")] + pub fn modified(&self) -> io::Result { + self.0.modified().map(FromInner::from_inner) + } + + /// Returns the last access time of this metadata. + /// + /// The returned value corresponds to the `atime` field of `stat` on Unix + /// platforms and the `ftLastAccessTime` field on Windows platforms. + /// + /// Note that not all platforms will keep this field update in a file's + /// metadata, for example Windows has an option to disable updating this + /// time when files are accessed and Linux similarly has `noatime`. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + #[unstable(feature = "fs_time", issue = "31399")] + pub fn accessed(&self) -> io::Result { + self.0.accessed().map(FromInner::from_inner) + } + + /// Returns the creation time listed in the this metadata. + /// + /// The returned value corresponds to the `birthtime` field of `stat` on + /// Unix platforms and the `ftCreationTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + #[unstable(feature = "fs_time", issue = "31399")] + pub fn created(&self) -> io::Result { + self.0.created().map(FromInner::from_inner) + } } impl AsInner for Metadata { @@ -2468,4 +2515,24 @@ fn create_dir_all_with_junctions() { assert!(link.is_dir()); assert!(d.exists()); } + + #[test] + fn metadata_access_times() { + let tmpdir = tmpdir(); + + let b = tmpdir.join("b"); + File::create(&b).unwrap(); + + let a = check!(fs::metadata(&tmpdir.path())); + let b = check!(fs::metadata(&b)); + + assert_eq!(check!(a.accessed()), check!(a.accessed())); + assert_eq!(check!(a.modified()), check!(a.modified())); + assert_eq!(check!(b.accessed()), check!(b.modified())); + + if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + check!(a.created()); + check!(b.created()); + } + } } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 2527c6774ff..e672d9f1586 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -22,6 +22,7 @@ use sync::Arc; use sys::fd::FileDesc; use sys::platform::raw; +use sys::time::SystemTime; use sys::{cvt, cvt_r}; use sys_common::{AsInner, FromInner}; @@ -86,6 +87,67 @@ pub fn file_type(&self) -> FileType { } } +#[cfg(any(target_os = "ios", target_os = "macos"))] +// FIXME: update SystemTime to store a timespec and don't lose precision +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timeval { + tv_sec: self.stat.st_mtime, + tv_usec: (self.stat.st_mtime_nsec / 1000) as libc::suseconds_t, + })) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timeval { + tv_sec: self.stat.st_atime, + tv_usec: (self.stat.st_atime_nsec / 1000) as libc::suseconds_t, + })) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(libc::timeval { + tv_sec: self.stat.st_birthtime, + tv_usec: (self.stat.st_birthtime_nsec / 1000) as libc::suseconds_t, + })) + } +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime, + tv_nsec: self.stat.st_mtime_nsec as libc::c_long, + })) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime, + tv_nsec: self.stat.st_atime_nsec as libc::c_long, + })) + } + + #[cfg(any(target_os = "bitrig", + target_os = "freebsd", + target_os = "openbsd"))] + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_birthtime, + tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, + })) + } + + #[cfg(not(any(target_os = "bitrig", + target_os = "freebsd", + target_os = "openbsd")))] + pub fn created(&self) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, + "creation time is not available on this platform \ + currently")) + } +} + impl AsInner for FileAttr { fn as_inner(&self) -> &raw::stat { &self.stat } } diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index a07c30d9648..dd248416f84 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -146,6 +146,12 @@ pub fn sub_duration(&self, other: &Duration) -> SystemTime { } } + impl From for SystemTime { + fn from(t: libc::timeval) -> SystemTime { + SystemTime { t: t } + } + } + impl PartialEq for SystemTime { fn eq(&self, other: &SystemTime) -> bool { self.t.tv_sec == other.t.tv_sec && self.t.tv_usec == other.t.tv_usec @@ -282,6 +288,12 @@ pub fn sub_duration(&self, other: &Duration) -> SystemTime { } } + impl From for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } + } + impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("SystemTime") diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index d060c902fba..d378a6853f3 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -196,9 +196,9 @@ pub trait MetadataExt { #[stable(feature = "metadata_ext", since = "1.1.0")] impl MetadataExt for Metadata { fn file_attributes(&self) -> u32 { self.as_inner().attrs() } - fn creation_time(&self) -> u64 { self.as_inner().created() } - fn last_access_time(&self) -> u64 { self.as_inner().accessed() } - fn last_write_time(&self) -> u64 { self.as_inner().modified() } + fn creation_time(&self) -> u64 { self.as_inner().created_u64() } + fn last_access_time(&self) -> u64 { self.as_inner().accessed_u64() } + fn last_write_time(&self) -> u64 { self.as_inner().modified_u64() } fn file_size(&self) -> u64 { self.as_inner().size() } } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 8d921146653..16d337fcc70 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -20,6 +20,7 @@ use slice; use sync::Arc; use sys::handle::Handle; +use sys::time::SystemTime; use sys::{c, cvt}; use sys_common::FromInner; @@ -421,12 +422,28 @@ pub fn file_type(&self) -> FileType { FileType::new(self.data.dwFileAttributes, self.reparse_tag) } - pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) } - pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) } - pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) } + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(self.data.ftLastWriteTime)) + } - fn to_u64(&self, ft: &c::FILETIME) -> u64 { - (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(self.data.ftLastAccessTime)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(self.data.ftCreationTime)) + } + + pub fn modified_u64(&self) -> u64 { + to_u64(&self.data.ftLastWriteTime) + } + + pub fn accessed_u64(&self) -> u64 { + to_u64(&self.data.ftLastAccessTime) + } + + pub fn created_u64(&self) -> u64 { + to_u64(&self.data.ftCreationTime) } fn is_reparse_point(&self) -> bool { @@ -434,6 +451,10 @@ fn is_reparse_point(&self) -> bool { } } +fn to_u64(ft: &c::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) +} + impl FilePermissions { pub fn readonly(&self) -> bool { self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 058587b11dc..ef8ed606526 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -166,6 +166,12 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } +impl From for SystemTime { + fn from(t: c::FILETIME) -> SystemTime { + SystemTime { t: t } + } +} + fn dur2intervals(d: &Duration) -> i64 { d.as_secs().checked_mul(INTERVALS_PER_SEC).and_then(|i| { i.checked_add(d.subsec_nanos() as u64 / 100) diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index f885733c2d1..a0cf443c0c3 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -16,6 +16,7 @@ use fmt; use ops::{Add, Sub}; use sys::time; +use sys_common::FromInner; #[stable(feature = "time", since = "1.3.0")] pub use self::duration::Duration; @@ -227,6 +228,12 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } +impl FromInner for SystemTime { + fn from_inner(time: time::SystemTime) -> SystemTime { + SystemTime(time) + } +} + #[cfg(test)] mod tests { use super::{Instant, SystemTime, Duration, UNIX_EPOCH};