Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std: Expose SystemTime accessors on fs::Metadata #30865

Merged
merged 1 commit into from
Feb 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/libstd/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use sys::fs as fs_imp;
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.
///
Expand Down Expand Up @@ -660,6 +661,52 @@ impl Metadata {
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<SystemTime> {
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may also be good to point out that even though all platforms do provide this value, not all platforms will actually keep it updated. I know at least on Windows there is an option to disable updating of the last access time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux too, with noatime.

///
/// 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<SystemTime> {
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<SystemTime> {
self.0.created().map(FromInner::from_inner)
}
}

impl AsInner<fs_imp::FileAttr> for Metadata {
Expand Down Expand Up @@ -2468,4 +2515,24 @@ mod tests {
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());
}
}
}
62 changes: 62 additions & 0 deletions src/libstd/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use ptr;
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};

Expand Down Expand Up @@ -86,6 +87,67 @@ impl FileAttr {
}
}

#[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<SystemTime> {
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<SystemTime> {
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<SystemTime> {
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<SystemTime> {
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<SystemTime> {
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<SystemTime> {
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<SystemTime> {
Err(io::Error::new(io::ErrorKind::Other,
"creation time is not available on this platform \
currently"))
}
}

impl AsInner<raw::stat> for FileAttr {
fn as_inner(&self) -> &raw::stat { &self.stat }
}
Expand Down
12 changes: 12 additions & 0 deletions src/libstd/sys/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ mod inner {
}
}

impl From<libc::timeval> 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
Expand Down Expand Up @@ -282,6 +288,12 @@ mod inner {
}
}

impl From<libc::timespec> 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")
Expand Down
6 changes: 3 additions & 3 deletions src/libstd/sys/windows/ext/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
}

Expand Down
31 changes: 26 additions & 5 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use ptr;
use slice;
use sync::Arc;
use sys::handle::Handle;
use sys::time::SystemTime;
use sys::{c, cvt};
use sys_common::FromInner;

Expand Down Expand Up @@ -421,19 +422,39 @@ impl FileAttr {
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<SystemTime> {
Ok(SystemTime::from(self.data.ftLastWriteTime))
}

pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(self.data.ftLastAccessTime))
}

fn to_u64(&self, ft: &c::FILETIME) -> u64 {
(ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
pub fn created(&self) -> io::Result<SystemTime> {
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 {
self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
}
}

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
Expand Down
6 changes: 6 additions & 0 deletions src/libstd/sys/windows/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ impl fmt::Debug for SystemTime {
}
}

impl From<c::FILETIME> 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)
Expand Down
7 changes: 7 additions & 0 deletions src/libstd/time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use error::Error;
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;
Expand Down Expand Up @@ -227,6 +228,12 @@ impl fmt::Display for SystemTimeError {
}
}

impl FromInner<time::SystemTime> for SystemTime {
fn from_inner(time: time::SystemTime) -> SystemTime {
SystemTime(time)
}
}

#[cfg(test)]
mod tests {
use super::{Instant, SystemTime, Duration, UNIX_EPOCH};
Expand Down