diff --git a/src/platform.rs b/src/platform.rs index 72a3a18a..5deb68b6 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -13,6 +13,14 @@ fn get_block_size() -> u64 { type InodeAndDevice = (u64, u64); type FileTime = (i64, i64, i64); +#[cfg(target_family = "windows")] +fn filetime_to_unix_seconds(filetime: u64) -> i64 { + const TICKS_PER_SECOND: i128 = 10_000_000; + const UNIX_EPOCH_FILETIME: i128 = 116_444_736_000_000_000; + + ((i128::from(filetime) - UNIX_EPOCH_FILETIME).div_euclid(TICKS_PER_SECOND)) as i64 +} + #[cfg(target_family = "unix")] pub fn get_metadata>( path: P, @@ -146,9 +154,9 @@ pub fn get_metadata>( path.size_on_disk().ok()?, Some((info.file_index(), info.volume_serial_number())), ( - info.last_write_time().unwrap() as i64, - info.last_access_time().unwrap() as i64, - info.creation_time().unwrap() as i64, + filetime_to_unix_seconds(info.last_write_time().unwrap()), + filetime_to_unix_seconds(info.last_access_time().unwrap()), + filetime_to_unix_seconds(info.creation_time().unwrap()), ), )) } else { @@ -156,9 +164,9 @@ pub fn get_metadata>( info.file_size(), Some((info.file_index(), info.volume_serial_number())), ( - info.last_write_time().unwrap() as i64, - info.last_access_time().unwrap() as i64, - info.creation_time().unwrap() as i64, + filetime_to_unix_seconds(info.last_write_time().unwrap()), + filetime_to_unix_seconds(info.last_access_time().unwrap()), + filetime_to_unix_seconds(info.creation_time().unwrap()), ), )) } @@ -203,9 +211,9 @@ pub fn get_metadata>( md.len(), None, ( - md.last_write_time() as i64, - md.last_access_time() as i64, - md.creation_time() as i64, + filetime_to_unix_seconds(md.last_write_time()), + filetime_to_unix_seconds(md.last_access_time()), + filetime_to_unix_seconds(md.creation_time()), ), )) } else { diff --git a/tests/test_flags.rs b/tests/test_flags.rs index 0b27bdc0..ae653012 100644 --- a/tests/test_flags.rs +++ b/tests/test_flags.rs @@ -1,5 +1,7 @@ use assert_cmd::cargo_bin_cmd; +use chrono::{Local, TimeZone}; use std::ffi::OsStr; +use std::fs::{FileTimes, OpenOptions}; use std::str; /** @@ -22,6 +24,59 @@ fn build_command>(command_args: Vec) -> String { str::from_utf8(&finished.stdout).unwrap().into() } +#[test] +fn test_filetime_output_uses_unix_timestamp() { + let temp_dir = tempfile::tempdir().unwrap(); + std::fs::write(temp_dir.path().join("recent.txt"), b"recent").unwrap(); + + let mut cmd = cargo_bin_cmd!("dust"); + let output = cmd + .arg("-P") + .arg("-c") + .arg("--filetime") + .arg("modified") + .arg(temp_dir.path()) + .unwrap(); + + assert!( + output.status.success(), + "{}", + str::from_utf8(&output.stderr).unwrap() + ); +} + +#[test] +fn test_mtime_filter_uses_unix_timestamp() { + let temp_dir = tempfile::tempdir().unwrap(); + let file_path = temp_dir.path().join("yesterday.txt"); + std::fs::write(&file_path, b"yesterday").unwrap(); + + let yesterday = Local::now().date_naive().pred_opt().unwrap(); + let yesterday_noon = Local + .from_local_datetime(&yesterday.and_hms_opt(12, 0, 0).unwrap()) + .single() + .unwrap(); + let file = OpenOptions::new().write(true).open(&file_path).unwrap(); + file.set_times(FileTimes::new().set_modified(yesterday_noon.into())) + .unwrap(); + + let mut cmd = cargo_bin_cmd!("dust"); + let output = cmd + .arg("-P") + .arg("-c") + .arg("--mtime") + .arg("0") + .arg(temp_dir.path()) + .unwrap(); + + assert!(output.status.success()); + assert!( + str::from_utf8(&output.stdout) + .unwrap() + .contains("yesterday.txt") + ); +} + // We can at least test the file names are there #[test] pub fn test_basic_output() {