system_extensions/metadata/
time.rs

1#[cfg(windows)]
2extern crate winapi;
3
4use std::ffi::OsStr;
5use std::iter::once;
6use std::path::Path;
7use std::process::Command;
8
9
10/**
11    Manages the imports for machines running on windows.
12*/
13#[cfg(windows)]
14macro_rules! windows_imports {
15    () => {
16        use winapi::um::winnt;
17        use self::winapi::um::handleapi::CloseHandle;
18        use winapi::um::sysinfoapi;
19        use self::winapi::um::fileapi::{OPEN_EXISTING};
20        use self::winapi::shared::minwindef::{FILETIME};
21        use self::winapi::_core::ptr::{null_mut};
22        use self::winapi::um::winnt::{FILE_WRITE_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_WRITE};
23        use self::winapi::um::minwinbase::SYSTEMTIME;
24        use self::winapi::um::timezoneapi::SystemTimeToFileTime;
25        use winapi::um::fileapi::SetFileTime;
26        use std::os::windows::ffi::OsStrExt;
27    };
28}
29
30/**
31    Represents the time for a file.
32*/
33#[derive(Debug)]
34pub struct FileTime {
35    pub day: i16,
36    pub month: i16,
37    pub year: i16,
38    pub hour: i16,
39    pub minute: i16,
40    pub second: i16,
41    pub milliseconds: i16,
42}
43
44impl FileTime {
45    /**
46        Create a new file time.
47    */
48    pub fn new(day: i16, month: i16, year: i16) -> FileTime {
49        FileTime {
50            day,
51            month,
52            year,
53            hour: -1,
54            minute: -1,
55            second: -1,
56            milliseconds: -1,
57        }
58    }
59}
60
61#[cfg(windows)]
62unsafe fn filetime_to_systime(system_time: *mut winapi::um::minwinbase::SYSTEMTIME, time: &FileTime) {
63    if time.day != -1 {
64        (*system_time).wDay = time.day as u16;
65    }
66    if time.month != -1 {
67        (*system_time).wMonth = time.month as u16;
68    }
69    if time.year != -1 {
70        (*system_time).wYear = time.year as u16;
71    }
72    if time.hour != -1 {
73        (*system_time).wHour = time.hour as u16;
74    }
75    if time.minute != -1 {
76        (*system_time).wMinute = time.minute as u16;
77    }
78    if time.second != -1 {
79        (*system_time).wSecond = time.second as u16;
80    }
81    if time.milliseconds != -1 {
82        (*system_time).wMilliseconds = time.milliseconds as u16;
83    }
84}
85
86/**
87   Set the creation date of a file.
88
89   ## Params
90   file: &Path -> The path of the file to change.
91   create: &[`FileTime`] -> The new file time for a file.
92
93   ## Returns
94   bool -> True if successful, false if not.
95   False means that the file could not be found or modified. Check to makesure
96   the path is correct.
97
98   ## Examples
99   ```rust
100   use system_extensions::metadata::time::{set_creation_date, FileTime};
101   use std::path::Path;
102
103   set_creation_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
104   ```
105*/
106#[cfg(windows)]
107pub fn set_creation_date(file: &Path, create: &FileTime) -> bool {
108    windows_imports!();
109    unsafe {
110        let mut file_handle: winnt::HANDLE = std::mem::zeroed();
111
112        let wide: Vec<u16> = OsStr::new(file.to_str().unwrap()).encode_wide().chain(once(0)).collect();
113        file_handle = winapi::um::fileapi::CreateFileW(wide.as_ptr(), GENERIC_WRITE,
114                                                       FILE_SHARE_READ | FILE_SHARE_WRITE, null_mut(), OPEN_EXISTING,
115                                                       FILE_ATTRIBUTE_NORMAL | FILE_WRITE_ATTRIBUTES, null_mut());
116        if file_handle == winapi::um::handleapi::INVALID_HANDLE_VALUE {
117            return false;
118        }
119
120        let system_time: *mut SYSTEMTIME = &mut SYSTEMTIME {
121            wYear: 0,
122            wMonth: 0,
123            wDayOfWeek: 0,
124            wDay: 0,
125            wHour: 0,
126            wMinute: 0,
127            wSecond: 0,
128            wMilliseconds: 0,
129        };
130
131        sysinfoapi::GetSystemTime(system_time);
132
133        // Convert FileTime to SystemTime
134        filetime_to_systime(system_time, create);
135
136        let file_time: *mut FILETIME = &mut FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 };
137        SystemTimeToFileTime(system_time, file_time);
138
139        SetFileTime(file_handle, file_time as *const FILETIME, null_mut(), null_mut());
140        CloseHandle(file_handle);
141    }
142
143    true
144}
145
146
147/**
148   Set the accessed date of a file.
149
150   ## Params
151   file: &Path -> The path of the file to change.
152   create: &[`FileTime`] -> The new file time for a file.
153
154   ## Returns
155   bool -> True if successful, false if not.
156   False means that the file could not be found or modified. Check to makesure
157   the path is correct.
158
159   ## Examples
160   ```rust
161   use system_extensions::metadata::time::{set_accessed_date, FileTime};
162   use std::path::Path;
163
164   set_accessed_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
165   ```
166*/
167#[cfg(windows)]
168pub fn set_accessed_date(file: &Path, accessed: &FileTime) -> bool {
169    unsafe {
170        windows_imports!();
171        let mut file_handle: winnt::HANDLE = std::mem::zeroed();
172
173        let wide: Vec<u16> = OsStr::new(file.to_str().unwrap()).encode_wide().chain(once(0)).collect();
174        file_handle = winapi::um::fileapi::CreateFileW(wide.as_ptr(), GENERIC_WRITE,
175                                                       FILE_SHARE_READ | FILE_SHARE_WRITE, null_mut(), OPEN_EXISTING,
176                                                       FILE_ATTRIBUTE_NORMAL | FILE_WRITE_ATTRIBUTES, null_mut());
177
178        if file_handle == winapi::um::handleapi::INVALID_HANDLE_VALUE {
179            return false;
180        }
181
182        let system_time: *mut SYSTEMTIME = &mut SYSTEMTIME {
183            wYear: 0,
184            wMonth: 0,
185            wDayOfWeek: 0,
186            wDay: 0,
187            wHour: 0,
188            wMinute: 0,
189            wSecond: 0,
190            wMilliseconds: 0,
191        };
192
193        sysinfoapi::GetSystemTime(system_time);
194
195        // Convert FileTime to SystemTime
196        filetime_to_systime(system_time, accessed);
197
198        let file_time: *mut FILETIME = &mut FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 };
199        SystemTimeToFileTime(system_time, file_time);
200
201        SetFileTime(file_handle, null_mut(), file_time as *const FILETIME, null_mut());
202        CloseHandle(file_handle);
203    }
204    true
205}
206
207/**
208   Set the modified date of a file.
209
210   ## Params
211   file: &Path -> The path of the file to change.
212   create: &[`FileTime`] -> The new file time for a file.
213
214   ## Returns
215   bool -> True if successful, false if not.
216   False means that the file could not be found or modified. Check to makesure
217   the path is correct.
218
219   ## Examples
220   ```rust
221   use system_extensions::metadata::time::{set_changed_date, FileTime};
222   use std::path::Path;
223
224   set_changed_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
225   ```
226*/
227#[cfg(windows)]
228pub fn set_changed_date(file: &Path, changed: &FileTime) -> bool{
229   unsafe {
230       windows_imports!();
231       let mut file_handle: winnt::HANDLE = std::mem::zeroed();
232
233       let wide: Vec<u16> = OsStr::new(file.to_str().unwrap()).encode_wide().chain(once(0)).collect();
234       file_handle = winapi::um::fileapi::CreateFileW(wide.as_ptr(), GENERIC_WRITE,
235                                                      FILE_SHARE_READ | FILE_SHARE_WRITE, null_mut(), OPEN_EXISTING,
236                                                      FILE_ATTRIBUTE_NORMAL | FILE_WRITE_ATTRIBUTES, null_mut());
237
238       if file_handle == winapi::um::handleapi::INVALID_HANDLE_VALUE {
239           return false;
240       }
241
242       let system_time: *mut SYSTEMTIME = &mut SYSTEMTIME {
243           wYear: 0,
244           wMonth: 0,
245           wDayOfWeek: 0,
246           wDay: 0,
247           wHour: 0,
248           wMinute: 0,
249           wSecond: 0,
250           wMilliseconds: 0,
251       };
252
253       sysinfoapi::GetSystemTime(system_time);
254
255       // Convert FileTime to SystemTime
256       filetime_to_systime(system_time, changed);
257
258       let file_time: *mut FILETIME = &mut FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 };
259       SystemTimeToFileTime(system_time, file_time);
260
261       SetFileTime(file_handle, null_mut(), null_mut(), file_time as *const FILETIME);
262       CloseHandle(file_handle);
263   }
264
265    true
266}
267
268/*
269
270    Linux Section
271
272 */
273/**
274   Set the creation date of a file.
275
276   **Note**: This does nothing on Unix systems and only exists
277   for cross compatibility. (Except on Mac)
278
279   ## Params
280   `file: &Path` -> The path of the file to change. <br>
281   `create: &[`FileTime`]` -> The new file time for a file.
282
283   ## Returns
284   bool -> True if successful, false if not.     <br>
285   False means that the file could not be found or modified. Check to makesure
286   the path is correct.
287
288   ## Examples
289   ```rust
290   use system_extensions::metadata::time::{set_creation_date, FileTime};
291   use std::path::Path;
292
293   set_creation_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
294   ```
295*/
296#[cfg(all(unix, not(target_os = "macos")))]
297pub fn set_creation_date(file: &Path, create: &FileTime) -> bool {
298    //Creation time is not stored by Unix
299    true
300}
301
302/**
303   Set the accessed date of a file.
304
305   ## Params
306   file: &Path -> The path of the file to change. <br>
307   create: &[`FileTime`] -> The new file time for a file.
308
309   ## Returns
310   bool -> True if successful, false if not. <br>
311   False means that the file could not be found or modified. Check to makesure
312   the path is correct.
313
314   ## Examples
315   ```rust
316   use system_extensions::metadata::time::{set_accessed_date, FileTime};
317   use std::path::Path;
318
319   set_accessed_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
320   ```
321*/
322#[cfg(unix)]
323pub fn set_accessed_date(file: &Path, create: &FileTime) -> bool {
324  Command::new("touch").arg("-a").arg("-t").arg(filetime_to_systime(&create)).arg(file.to_str().unwrap()).spawn().is_ok()
325
326}
327
328/**
329   Set the modified date of a file.
330
331   ## Params
332   file: &Path -> The path of the file to change. <br>
333   create: &[`FileTime`] -> The new file time for a file.
334
335   ## Returns
336   bool -> True if successful, false if not. <br>
337   False means that the file could not be found or modified. Check to makesure
338   the path is correct.
339
340   ## Examples
341   ```rust
342   use system_extensions::metadata::time::{set_changed_date, FileTime};
343   use std::path::Path;
344
345   set_changed_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
346   ```
347*/
348#[cfg(unix)]
349pub fn set_changed_date(file: &Path, create: &FileTime) -> bool {
350    Command::new("touch").arg("-m").arg("-t").arg(filetime_to_systime(&create)).arg(file.to_str().unwrap()).spawn().is_ok()
351}
352
353/**
354   Convert the file time to the system time needed for unix.
355   <br>
356   This is primarily used internally.
357
358   ## Params
359   time: &[`FileTime`] -> The new file time for a file.
360
361   ## Returns
362   bool -> True if successful, false if not.
363   False means that the file could not be found or modified. Check to makesure
364   the path is correct.
365
366   ## Examples
367   ```rust
368
369   if cfg!(unix) {
370       use system_extensions::metadata::time::{filetime_to_systime, FileTime};
371       use std::path::Path;
372       filetime_to_systime(&FileTime::new(25, 12, 2021));
373   }
374   ```
375*/
376#[cfg(unix)]
377pub fn filetime_to_systime(time: &FileTime) -> String{
378    use chrono::{Local, Timelike, Datelike};
379
380    let now = Local::now();
381
382    let mut year:String;
383    if time.year!=-1{
384        year = time.year.to_string();
385    }else{
386        year = now.year().to_string();
387    }
388    if year.len()!=2{
389        year = format!("{}", year);
390    }
391
392    let mut month:String;
393    if time.month!=-1{
394        month = time.month.to_string();
395    }else{
396        month = now.month().to_string();
397    }
398    if month.len()!=2{
399        month = format!("0{}", month);
400    }
401
402    let mut day:String;
403    if time.day!=-1{
404        day = time.day.to_string();
405    }else{
406        day = (now.day()).to_string();
407    }
408    if day.len()!=2{
409        day = format!("0{}", day);
410    }
411
412    let mut hour:String;
413    if time.hour!=-1{
414        hour = time.hour.to_string();
415    }else{
416        hour = (now.hour()+1).to_string();
417    }
418    if hour.len()!=2{
419        hour = format!("0{}", hour);
420    }
421
422    let mut minute:String;
423    if time.minute!=-1{
424        minute = time.minute.to_string();
425    }else{
426        minute = (now.minute()+1).to_string();
427    }
428    if minute.len()!=2{
429        minute = format!("0{}", minute);
430    }
431
432    let mut second:String;
433    if time.second!=-1{
434        second = time.second.to_string();
435    }else{
436        second = (now.second()+1).to_string();
437    }
438    if second.len()!=2{
439        second = format!("0{}", second);
440    }
441
442    format!("{}{}{}{}{}.{}", year, month, day, hour, minute, second)
443}
444
445/*
446
447    Mac specific section
448
449 */
450/**
451   Set the creation date of a file.
452
453   **Note:** Requires the `SetFile` command to be on the system.
454
455   ## Params
456   `file: &Path` -> The path of the file to change. <br>
457   `create: &[`FileTime`]` -> The new file time for a file.
458
459   ## Returns
460   bool -> True if successful, false if not.     <br>
461   False means that the file could not be found or modified. Check to makesure
462   the path is correct.
463
464   ## Examples
465   ```rust
466   use system_extensions::metadata::time::{set_creation_date, FileTime};
467   use std::path::Path;
468
469   set_creation_date(Path::new("/test.txt"), &FileTime::new(25, 12, 2021));
470   ```
471*/
472#[cfg(target_os = "macos")]
473pub fn set_creation_date(file: &Path, create: &FileTime) -> bool {
474    let mut time = "AM";
475    if create.hour >= 12 {
476        time = "PM";
477    }
478
479    Command::new("SetFile")
480        .arg("-d")
481        .arg(format!("'{}/{}/{} {}:{}:{} {}'", create.month, create.day, (create.year + 1), create.hour,
482                     create.minute, create.second, time))
483        .arg(file.to_str().unwrap())
484        .spawn().is_ok()
485}