]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
1 | //! Timestamps for files in Rust |
2 | //! | |
3 | //! This library provides platform-agnostic inspection of the various timestamps | |
4 | //! present in the standard `fs::Metadata` structure. | |
5 | //! | |
6 | //! # Installation | |
7 | //! | |
ea8adc8c | 8 | //! Add this to your `Cargo.toml`: |
476ff2be SL |
9 | //! |
10 | //! ```toml | |
11 | //! [dependencies] | |
12 | //! filetime = "0.1" | |
13 | //! ``` | |
14 | //! | |
15 | //! # Usage | |
16 | //! | |
17 | //! ```no_run | |
18 | //! use std::fs; | |
19 | //! use filetime::FileTime; | |
20 | //! | |
21 | //! let metadata = fs::metadata("foo.txt").unwrap(); | |
22 | //! | |
23 | //! let mtime = FileTime::from_last_modification_time(&metadata); | |
24 | //! println!("{}", mtime); | |
25 | //! | |
26 | //! let atime = FileTime::from_last_access_time(&metadata); | |
27 | //! assert!(mtime < atime); | |
28 | //! | |
29 | //! // Inspect values that can be interpreted across platforms | |
30 | //! println!("{}", mtime.seconds_relative_to_1970()); | |
31 | //! println!("{}", mtime.nanoseconds()); | |
32 | //! | |
33 | //! // Print the platform-specific value of seconds | |
34 | //! println!("{}", mtime.seconds()); | |
35 | //! ``` | |
36 | ||
abe05a73 XL |
37 | #[macro_use] |
38 | extern crate cfg_if; | |
39 | ||
476ff2be SL |
40 | use std::fmt; |
41 | use std::fs; | |
42 | use std::io; | |
43 | use std::path::Path; | |
44 | ||
abe05a73 XL |
45 | cfg_if! { |
46 | if #[cfg(target_os = "redox")] { | |
47 | #[path = "redox.rs"] | |
48 | mod imp; | |
49 | } else if #[cfg(windows)] { | |
50 | #[path = "windows.rs"] | |
51 | mod imp; | |
52 | } else { | |
53 | #[path = "unix/mod.rs"] | |
54 | mod imp; | |
55 | } | |
56 | } | |
ea8adc8c | 57 | |
476ff2be SL |
58 | /// A helper structure to represent a timestamp for a file. |
59 | /// | |
60 | /// The actual value contined within is platform-specific and does not have the | |
61 | /// same meaning across platforms, but comparisons and stringification can be | |
62 | /// significant among the same platform. | |
63 | #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)] | |
64 | pub struct FileTime { | |
65 | seconds: u64, | |
66 | nanos: u32, | |
67 | } | |
68 | ||
69 | impl FileTime { | |
70 | /// Creates a new timestamp representing a 0 time. | |
71 | /// | |
72 | /// Useful for creating the base of a cmp::max chain of times. | |
73 | pub fn zero() -> FileTime { | |
74 | FileTime { seconds: 0, nanos: 0 } | |
75 | } | |
76 | ||
77 | /// Creates a new instance of `FileTime` with a number of seconds and | |
78 | /// nanoseconds relative to January 1, 1970. | |
79 | /// | |
80 | /// Note that this is typically the relative point that Unix time stamps are | |
81 | /// from, but on Windows the native time stamp is relative to January 1, | |
82 | /// 1601 so the return value of `seconds` from the returned `FileTime` | |
83 | /// instance may not be the same as that passed in. | |
84 | pub fn from_seconds_since_1970(seconds: u64, nanos: u32) -> FileTime { | |
85 | FileTime { | |
86 | seconds: seconds + if cfg!(windows) {11644473600} else {0}, | |
87 | nanos: nanos, | |
88 | } | |
89 | } | |
90 | ||
91 | /// Creates a new timestamp from the last modification time listed in the | |
92 | /// specified metadata. | |
93 | /// | |
94 | /// The returned value corresponds to the `mtime` field of `stat` on Unix | |
95 | /// platforms and the `ftLastWriteTime` field on Windows platforms. | |
96 | pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime { | |
ea8adc8c | 97 | imp::from_last_modification_time(meta) |
476ff2be SL |
98 | } |
99 | ||
100 | /// Creates a new timestamp from the last access time listed in the | |
101 | /// specified metadata. | |
102 | /// | |
103 | /// The returned value corresponds to the `atime` field of `stat` on Unix | |
104 | /// platforms and the `ftLastAccessTime` field on Windows platforms. | |
105 | pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime { | |
ea8adc8c | 106 | imp::from_last_access_time(meta) |
476ff2be SL |
107 | } |
108 | ||
109 | /// Creates a new timestamp from the creation time listed in the specified | |
110 | /// metadata. | |
111 | /// | |
112 | /// The returned value corresponds to the `birthtime` field of `stat` on | |
113 | /// Unix platforms and the `ftCreationTime` field on Windows platforms. Note | |
114 | /// that not all Unix platforms have this field available and may return | |
115 | /// `None` in some circumstances. | |
116 | pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> { | |
ea8adc8c | 117 | imp::from_creation_time(meta) |
476ff2be SL |
118 | } |
119 | ||
120 | /// Returns the whole number of seconds represented by this timestamp. | |
121 | /// | |
122 | /// Note that this value's meaning is **platform specific**. On Unix | |
123 | /// platform time stamps are typically relative to January 1, 1970, but on | |
124 | /// Windows platforms time stamps are relative to January 1, 1601. | |
125 | pub fn seconds(&self) -> u64 { self.seconds } | |
126 | ||
127 | /// Returns the whole number of seconds represented by this timestamp, | |
128 | /// relative to the Unix epoch start of January 1, 1970. | |
129 | /// | |
130 | /// Note that this does not return the same value as `seconds` for Windows | |
131 | /// platforms as seconds are relative to a different date there. | |
132 | pub fn seconds_relative_to_1970(&self) -> u64 { | |
133 | self.seconds - if cfg!(windows) {11644473600} else {0} | |
134 | } | |
135 | ||
136 | /// Returns the nanosecond precision of this timestamp. | |
137 | /// | |
138 | /// The returned value is always less than one billion and represents a | |
139 | /// portion of a second forward from the seconds returned by the `seconds` | |
140 | /// method. | |
141 | pub fn nanoseconds(&self) -> u32 { self.nanos } | |
142 | } | |
143 | ||
144 | impl fmt::Display for FileTime { | |
145 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
146 | write!(f, "{}.{:09}s", self.seconds, self.nanos) | |
147 | } | |
148 | } | |
149 | ||
150 | /// Set the last access and modification times for a file on the filesystem. | |
151 | /// | |
152 | /// This function will set the `atime` and `mtime` metadata fields for a file | |
153 | /// on the local filesystem, returning any error encountered. | |
154 | pub fn set_file_times<P>(p: P, atime: FileTime, mtime: FileTime) | |
ea8adc8c XL |
155 | -> io::Result<()> |
156 | where P: AsRef<Path> | |
157 | { | |
158 | imp::set_file_times(p.as_ref(), atime, mtime) | |
476ff2be SL |
159 | } |
160 | ||
ea8adc8c XL |
161 | /// Set the last access and modification times for a file on the filesystem. |
162 | /// This function does not follow symlink. | |
163 | /// | |
164 | /// This function will set the `atime` and `mtime` metadata fields for a file | |
165 | /// on the local filesystem, returning any error encountered. | |
166 | pub fn set_symlink_file_times<P>(p: P, atime: FileTime, mtime: FileTime) | |
167 | -> io::Result<()> | |
168 | where P: AsRef<Path> | |
169 | { | |
170 | imp::set_symlink_file_times(p.as_ref(), atime, mtime) | |
476ff2be SL |
171 | } |
172 | ||
173 | #[cfg(test)] | |
174 | mod tests { | |
175 | extern crate tempdir; | |
176 | ||
ea8adc8c XL |
177 | use std::io; |
178 | use std::path::Path; | |
476ff2be SL |
179 | use std::fs::{self, File}; |
180 | use self::tempdir::TempDir; | |
ea8adc8c XL |
181 | use super::{FileTime, set_file_times, set_symlink_file_times}; |
182 | ||
183 | #[cfg(unix)] | |
184 | fn make_symlink<P,Q>(src: P, dst: Q) -> io::Result<()> | |
185 | where P: AsRef<Path>, | |
186 | Q: AsRef<Path>, | |
187 | { | |
188 | use std::os::unix::fs::symlink; | |
189 | symlink(src, dst) | |
190 | } | |
191 | ||
192 | #[cfg(windows)] | |
193 | fn make_symlink<P,Q>(src: P, dst: Q) -> io::Result<()> | |
194 | where P: AsRef<Path>, | |
195 | Q: AsRef<Path>, | |
196 | { | |
197 | use std::os::windows::fs::symlink_file; | |
198 | symlink_file(src, dst) | |
199 | } | |
476ff2be SL |
200 | |
201 | #[test] | |
202 | fn set_file_times_test() { | |
203 | let td = TempDir::new("filetime").unwrap(); | |
204 | let path = td.path().join("foo.txt"); | |
205 | File::create(&path).unwrap(); | |
206 | ||
207 | let metadata = fs::metadata(&path).unwrap(); | |
208 | let mtime = FileTime::from_last_modification_time(&metadata); | |
209 | let atime = FileTime::from_last_access_time(&metadata); | |
210 | set_file_times(&path, atime, mtime).unwrap(); | |
211 | ||
212 | let new_mtime = FileTime::from_seconds_since_1970(10_000, 0); | |
213 | set_file_times(&path, atime, new_mtime).unwrap(); | |
214 | ||
215 | let metadata = fs::metadata(&path).unwrap(); | |
216 | let mtime = FileTime::from_last_modification_time(&metadata); | |
217 | assert_eq!(mtime, new_mtime); | |
ea8adc8c XL |
218 | |
219 | let spath = td.path().join("bar.txt"); | |
220 | make_symlink(&path, &spath).unwrap(); | |
221 | let metadata = fs::symlink_metadata(&spath).unwrap(); | |
222 | let smtime = FileTime::from_last_modification_time(&metadata); | |
223 | ||
224 | set_file_times(&spath, atime, mtime).unwrap(); | |
225 | ||
226 | let metadata = fs::metadata(&path).unwrap(); | |
227 | let cur_mtime = FileTime::from_last_modification_time(&metadata); | |
228 | assert_eq!(mtime, cur_mtime); | |
229 | ||
230 | let metadata = fs::symlink_metadata(&spath).unwrap(); | |
231 | let cur_mtime = FileTime::from_last_modification_time(&metadata); | |
232 | assert_eq!(smtime, cur_mtime); | |
233 | ||
234 | set_file_times(&spath, atime, new_mtime).unwrap(); | |
235 | ||
236 | let metadata = fs::metadata(&path).unwrap(); | |
237 | let mtime = FileTime::from_last_modification_time(&metadata); | |
238 | assert_eq!(mtime, new_mtime); | |
239 | ||
240 | let metadata = fs::symlink_metadata(&spath).unwrap(); | |
241 | let mtime = FileTime::from_last_modification_time(&metadata); | |
242 | assert_eq!(mtime, smtime); | |
243 | } | |
244 | ||
245 | #[test] | |
246 | fn set_symlink_file_times_test() { | |
247 | let td = TempDir::new("filetime").unwrap(); | |
248 | let path = td.path().join("foo.txt"); | |
249 | File::create(&path).unwrap(); | |
250 | ||
251 | let metadata = fs::metadata(&path).unwrap(); | |
252 | let mtime = FileTime::from_last_modification_time(&metadata); | |
253 | let atime = FileTime::from_last_access_time(&metadata); | |
254 | set_symlink_file_times(&path, atime, mtime).unwrap(); | |
255 | ||
256 | let new_mtime = FileTime::from_seconds_since_1970(10_000, 0); | |
257 | set_symlink_file_times(&path, atime, new_mtime).unwrap(); | |
258 | ||
259 | let metadata = fs::metadata(&path).unwrap(); | |
260 | let mtime = FileTime::from_last_modification_time(&metadata); | |
261 | assert_eq!(mtime, new_mtime); | |
262 | ||
263 | let spath = td.path().join("bar.txt"); | |
264 | make_symlink(&path, &spath).unwrap(); | |
265 | ||
266 | let metadata = fs::symlink_metadata(&spath).unwrap(); | |
267 | let smtime = FileTime::from_last_modification_time(&metadata); | |
268 | let satime = FileTime::from_last_access_time(&metadata); | |
269 | set_symlink_file_times(&spath, smtime, satime).unwrap(); | |
270 | ||
271 | let metadata = fs::metadata(&path).unwrap(); | |
272 | let mtime = FileTime::from_last_modification_time(&metadata); | |
273 | assert_eq!(mtime, new_mtime); | |
274 | ||
275 | let new_smtime = FileTime::from_seconds_since_1970(20_000, 0); | |
276 | set_symlink_file_times(&spath, atime, new_smtime).unwrap(); | |
277 | ||
278 | let metadata = fs::metadata(&spath).unwrap(); | |
279 | let mtime = FileTime::from_last_modification_time(&metadata); | |
280 | assert_eq!(mtime, new_mtime); | |
281 | ||
282 | let metadata = fs::symlink_metadata(&spath).unwrap(); | |
283 | let mtime = FileTime::from_last_modification_time(&metadata); | |
284 | assert_eq!(mtime, new_smtime); | |
476ff2be SL |
285 | } |
286 | } |