Skip to content

Commit d1681bb

Browse files
committed
std: Expose SystemTime accessors on fs::Metadata
These accessors are used to get at the last modification, last access, and creation time of the underlying file. Currently not all platforms provide the creation time, so that currently returns `Option`.
1 parent d0ef740 commit d1681bb

File tree

7 files changed

+183
-8
lines changed

7 files changed

+183
-8
lines changed

src/libstd/fs.rs

+67
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use sys::fs as fs_imp;
2525
use sys_common::io::read_to_end_uninitialized;
2626
use sys_common::{AsInnerMut, FromInner, AsInner, IntoInner};
2727
use vec::Vec;
28+
use time::SystemTime;
2829

2930
/// A reference to an open file on the filesystem.
3031
///
@@ -660,6 +661,52 @@ impl Metadata {
660661
pub fn permissions(&self) -> Permissions {
661662
Permissions(self.0.perm())
662663
}
664+
665+
/// Returns the last modification time listed in this metadata.
666+
///
667+
/// The returned value corresponds to the `mtime` field of `stat` on Unix
668+
/// platforms and the `ftLastWriteTime` field on Windows platforms.
669+
///
670+
/// # Errors
671+
///
672+
/// This field may not be available on all platforms, and will return an
673+
/// `Err` on platforms where it is not available.
674+
#[unstable(feature = "fs_time", issue = "31399")]
675+
pub fn modified(&self) -> io::Result<SystemTime> {
676+
self.0.modified().map(FromInner::from_inner)
677+
}
678+
679+
/// Returns the last access time of this metadata.
680+
///
681+
/// The returned value corresponds to the `atime` field of `stat` on Unix
682+
/// platforms and the `ftLastAccessTime` field on Windows platforms.
683+
///
684+
/// Note that not all platforms will keep this field update in a file's
685+
/// metadata, for example Windows has an option to disable updating this
686+
/// time when files are accessed and Linux similarly has `noatime`.
687+
///
688+
/// # Errors
689+
///
690+
/// This field may not be available on all platforms, and will return an
691+
/// `Err` on platforms where it is not available.
692+
#[unstable(feature = "fs_time", issue = "31399")]
693+
pub fn accessed(&self) -> io::Result<SystemTime> {
694+
self.0.accessed().map(FromInner::from_inner)
695+
}
696+
697+
/// Returns the creation time listed in the this metadata.
698+
///
699+
/// The returned value corresponds to the `birthtime` field of `stat` on
700+
/// Unix platforms and the `ftCreationTime` field on Windows platforms.
701+
///
702+
/// # Errors
703+
///
704+
/// This field may not be available on all platforms, and will return an
705+
/// `Err` on platforms where it is not available.
706+
#[unstable(feature = "fs_time", issue = "31399")]
707+
pub fn created(&self) -> io::Result<SystemTime> {
708+
self.0.created().map(FromInner::from_inner)
709+
}
663710
}
664711

665712
impl AsInner<fs_imp::FileAttr> for Metadata {
@@ -2468,4 +2515,24 @@ mod tests {
24682515
assert!(link.is_dir());
24692516
assert!(d.exists());
24702517
}
2518+
2519+
#[test]
2520+
fn metadata_access_times() {
2521+
let tmpdir = tmpdir();
2522+
2523+
let b = tmpdir.join("b");
2524+
File::create(&b).unwrap();
2525+
2526+
let a = check!(fs::metadata(&tmpdir.path()));
2527+
let b = check!(fs::metadata(&b));
2528+
2529+
assert_eq!(check!(a.accessed()), check!(a.accessed()));
2530+
assert_eq!(check!(a.modified()), check!(a.modified()));
2531+
assert_eq!(check!(b.accessed()), check!(b.modified()));
2532+
2533+
if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
2534+
check!(a.created());
2535+
check!(b.created());
2536+
}
2537+
}
24712538
}

src/libstd/sys/unix/fs.rs

+62
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use ptr;
2222
use sync::Arc;
2323
use sys::fd::FileDesc;
2424
use sys::platform::raw;
25+
use sys::time::SystemTime;
2526
use sys::{cvt, cvt_r};
2627
use sys_common::{AsInner, FromInner};
2728

@@ -86,6 +87,67 @@ impl FileAttr {
8687
}
8788
}
8889

90+
#[cfg(any(target_os = "ios", target_os = "macos"))]
91+
// FIXME: update SystemTime to store a timespec and don't lose precision
92+
impl FileAttr {
93+
pub fn modified(&self) -> io::Result<SystemTime> {
94+
Ok(SystemTime::from(libc::timeval {
95+
tv_sec: self.stat.st_mtime,
96+
tv_usec: (self.stat.st_mtime_nsec / 1000) as libc::suseconds_t,
97+
}))
98+
}
99+
100+
pub fn accessed(&self) -> io::Result<SystemTime> {
101+
Ok(SystemTime::from(libc::timeval {
102+
tv_sec: self.stat.st_atime,
103+
tv_usec: (self.stat.st_atime_nsec / 1000) as libc::suseconds_t,
104+
}))
105+
}
106+
107+
pub fn created(&self) -> io::Result<SystemTime> {
108+
Ok(SystemTime::from(libc::timeval {
109+
tv_sec: self.stat.st_birthtime,
110+
tv_usec: (self.stat.st_birthtime_nsec / 1000) as libc::suseconds_t,
111+
}))
112+
}
113+
}
114+
115+
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
116+
impl FileAttr {
117+
pub fn modified(&self) -> io::Result<SystemTime> {
118+
Ok(SystemTime::from(libc::timespec {
119+
tv_sec: self.stat.st_mtime,
120+
tv_nsec: self.stat.st_mtime_nsec as libc::c_long,
121+
}))
122+
}
123+
124+
pub fn accessed(&self) -> io::Result<SystemTime> {
125+
Ok(SystemTime::from(libc::timespec {
126+
tv_sec: self.stat.st_atime,
127+
tv_nsec: self.stat.st_atime_nsec as libc::c_long,
128+
}))
129+
}
130+
131+
#[cfg(any(target_os = "bitrig",
132+
target_os = "freebsd",
133+
target_os = "openbsd"))]
134+
pub fn created(&self) -> io::Result<SystemTime> {
135+
Ok(SystemTime::from(libc::timespec {
136+
tv_sec: self.stat.st_birthtime,
137+
tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
138+
}))
139+
}
140+
141+
#[cfg(not(any(target_os = "bitrig",
142+
target_os = "freebsd",
143+
target_os = "openbsd")))]
144+
pub fn created(&self) -> io::Result<SystemTime> {
145+
Err(io::Error::new(io::ErrorKind::Other,
146+
"creation time is not available on this platform \
147+
currently"))
148+
}
149+
}
150+
89151
impl AsInner<raw::stat> for FileAttr {
90152
fn as_inner(&self) -> &raw::stat { &self.stat }
91153
}

src/libstd/sys/unix/time.rs

+12
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ mod inner {
146146
}
147147
}
148148

149+
impl From<libc::timeval> for SystemTime {
150+
fn from(t: libc::timeval) -> SystemTime {
151+
SystemTime { t: t }
152+
}
153+
}
154+
149155
impl PartialEq for SystemTime {
150156
fn eq(&self, other: &SystemTime) -> bool {
151157
self.t.tv_sec == other.t.tv_sec && self.t.tv_usec == other.t.tv_usec
@@ -282,6 +288,12 @@ mod inner {
282288
}
283289
}
284290

291+
impl From<libc::timespec> for SystemTime {
292+
fn from(t: libc::timespec) -> SystemTime {
293+
SystemTime { t: Timespec { t: t } }
294+
}
295+
}
296+
285297
impl fmt::Debug for SystemTime {
286298
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287299
f.debug_struct("SystemTime")

src/libstd/sys/windows/ext/fs.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ pub trait MetadataExt {
196196
#[stable(feature = "metadata_ext", since = "1.1.0")]
197197
impl MetadataExt for Metadata {
198198
fn file_attributes(&self) -> u32 { self.as_inner().attrs() }
199-
fn creation_time(&self) -> u64 { self.as_inner().created() }
200-
fn last_access_time(&self) -> u64 { self.as_inner().accessed() }
201-
fn last_write_time(&self) -> u64 { self.as_inner().modified() }
199+
fn creation_time(&self) -> u64 { self.as_inner().created_u64() }
200+
fn last_access_time(&self) -> u64 { self.as_inner().accessed_u64() }
201+
fn last_write_time(&self) -> u64 { self.as_inner().modified_u64() }
202202
fn file_size(&self) -> u64 { self.as_inner().size() }
203203
}
204204

src/libstd/sys/windows/fs.rs

+26-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use ptr;
2020
use slice;
2121
use sync::Arc;
2222
use sys::handle::Handle;
23+
use sys::time::SystemTime;
2324
use sys::{c, cvt};
2425
use sys_common::FromInner;
2526

@@ -421,19 +422,39 @@ impl FileAttr {
421422
FileType::new(self.data.dwFileAttributes, self.reparse_tag)
422423
}
423424

424-
pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) }
425-
pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) }
426-
pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) }
425+
pub fn modified(&self) -> io::Result<SystemTime> {
426+
Ok(SystemTime::from(self.data.ftLastWriteTime))
427+
}
428+
429+
pub fn accessed(&self) -> io::Result<SystemTime> {
430+
Ok(SystemTime::from(self.data.ftLastAccessTime))
431+
}
427432

428-
fn to_u64(&self, ft: &c::FILETIME) -> u64 {
429-
(ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
433+
pub fn created(&self) -> io::Result<SystemTime> {
434+
Ok(SystemTime::from(self.data.ftCreationTime))
435+
}
436+
437+
pub fn modified_u64(&self) -> u64 {
438+
to_u64(&self.data.ftLastWriteTime)
439+
}
440+
441+
pub fn accessed_u64(&self) -> u64 {
442+
to_u64(&self.data.ftLastAccessTime)
443+
}
444+
445+
pub fn created_u64(&self) -> u64 {
446+
to_u64(&self.data.ftCreationTime)
430447
}
431448

432449
fn is_reparse_point(&self) -> bool {
433450
self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
434451
}
435452
}
436453

454+
fn to_u64(ft: &c::FILETIME) -> u64 {
455+
(ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
456+
}
457+
437458
impl FilePermissions {
438459
pub fn readonly(&self) -> bool {
439460
self.attrs & c::FILE_ATTRIBUTE_READONLY != 0

src/libstd/sys/windows/time.rs

+6
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ impl fmt::Debug for SystemTime {
166166
}
167167
}
168168

169+
impl From<c::FILETIME> for SystemTime {
170+
fn from(t: c::FILETIME) -> SystemTime {
171+
SystemTime { t: t }
172+
}
173+
}
174+
169175
fn dur2intervals(d: &Duration) -> i64 {
170176
d.as_secs().checked_mul(INTERVALS_PER_SEC).and_then(|i| {
171177
i.checked_add(d.subsec_nanos() as u64 / 100)

src/libstd/time/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use error::Error;
1616
use fmt;
1717
use ops::{Add, Sub};
1818
use sys::time;
19+
use sys_common::FromInner;
1920

2021
#[stable(feature = "time", since = "1.3.0")]
2122
pub use self::duration::Duration;
@@ -227,6 +228,12 @@ impl fmt::Display for SystemTimeError {
227228
}
228229
}
229230

231+
impl FromInner<time::SystemTime> for SystemTime {
232+
fn from_inner(time: time::SystemTime) -> SystemTime {
233+
SystemTime(time)
234+
}
235+
}
236+
230237
#[cfg(test)]
231238
mod tests {
232239
use super::{Instant, SystemTime, Duration, UNIX_EPOCH};

0 commit comments

Comments
 (0)