Doug
Doug

Reputation: 35216

How can you get the modification time of a file in a cross platform manner?

The stable MetadataExt trait offers some basic functionality for this purpose, but it doesn't work on Windows.

I also see this RFC on the topic of I/O reform but now I'm totally confused. Wasn't the reason we took so long to get a 1.0 stable because of the absolutely epic feedback on the I/O story for Rust?

Why are the experimental std::os::unix::fs::MetadataExt traits in stable, when there is no cross platform story for the same functionality?

Is there some standard crate that I haven't found for this purpose? This question refers to Rust 1.3.0.

Upvotes: 6

Views: 6747

Answers (3)

Gubatron
Gubatron

Reputation: 6489

Here's what I have in my util's module. Pass the path and get the modified unix timestamp in seconds.

use std::fs;
use std::time::UNIX_EPOCH;

pub fn file_modified_time_in_seconds(path: &str) -> u64 {
    fs::metadata(path)
    .unwrap()
    .modified()
    .unwrap()
    .duration_since(UNIX_EPOCH)
    .unwrap()
    .as_secs()
}

Upvotes: 4

antoyo
antoyo

Reputation: 11933

Starting with Rust 1.10, there is a Metadata::modified method to get the modification time.

Upvotes: 4

DK.
DK.

Reputation: 59155

First of all, there is a crate for this: filetime (pointed out by Vladimir Matveev). Just be aware that the FileTime::seconds method in that crate is platform specific.

Secondly, 1.0 was about building the foundation for the language, not the complete standard library. It shipped with a lot of things that you might normally expect missing. Quite a few are still missing (as of 1.3). Part of this is is that the core team are very conservative about what they'll promise to support, given how seriously they're taking backward compatibility.

Third, MetadataExt isn't experimental. It's stable. It just happens to be platform-specific.

Fourth, MetadataExt is actually a bit of a red herring. The actual problem here is that rustdoc is built as a kind of post-process of the compiler, inserted after type checking is done. This puts it after macro expansion. This is relevant because it's during macro expansion that platform-specific #[cfg(...)] attributes are resolved. This is important because this is how the language does platform-specific code.

As a result, docs are only built for the platform you're specifically targeting. The docs on the official website all target Linux. As a result, nothing Windows-specific shows up. Now, if you download the Windows compiler, it comes with a set of docs, and those docs target Windows. If you open those up and search, you'll find std::os::windows::fs::MetadataExt, which looks like this:

pub trait MetadataExt {
    fn file_attributes(&self) -> u32;
    fn creation_time(&self) -> u64;
    fn last_access_time(&self) -> u64;
    fn last_write_time(&self) -> u64;
    fn file_size(&self) -> u64;
}

(I can't just link to this for hopefully obvious reasons.)

As an aside, what you linked to isn't an RFC; it's an issue on the RFC repository. It's for tracking things that RFCs should be written about.

So, to wrap this all up: you can use the aforementioned crate or you can write platform-specific code, which will likely boil down to one path for Windows, and one path for everything else, using the two different MetadataExt traits. One thing to be careful of is that the two systems use different epochs, meaning the times aren't directly comparable. UNIX uses an epoch of Jan 1 1970, Windows uses Jan 1 1601.

The simplest thing is probably to just use the crate mentioned above.

Upvotes: 12

Related Questions