From 72a24c9c567d066925f1e225c7e040a7b0823fc7 Mon Sep 17 00:00:00 2001 From: adithya-n05 Date: Sat, 15 Aug 2026 22:48:26 +0100 Subject: [PATCH 1/3] fix(windows): normalize file times --- src/platform.rs | 26 +++++++++++++++++--------- tests/test_flags.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) 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..31c1ddb1 100644 --- a/tests/test_flags.rs +++ b/tests/test_flags.rs @@ -22,6 +22,51 @@ fn build_command>(command_args: Vec) -> String { str::from_utf8(&finished.stdout).unwrap().into() } +#[cfg(target_os = "windows")] +#[test] +fn test_windows_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() + ); +} + +#[cfg(target_os = "windows")] +#[test] +fn test_windows_mtime_filter_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("--mtime") + .arg("0") + .arg(temp_dir.path()) + .unwrap(); + + assert!(output.status.success()); + assert!( + str::from_utf8(&output.stdout) + .unwrap() + .contains("recent.txt") + ); +} + // We can at least test the file names are there #[test] pub fn test_basic_output() { From ae60c075f9001bda693afe2997fd7c3cefd078d6 Mon Sep 17 00:00:00 2001 From: adithya-n05 Date: Sun, 16 Aug 2026 13:53:40 +0100 Subject: [PATCH 2/3] test(windows): make mtime regression deterministic --- tests/test_flags.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/test_flags.rs b/tests/test_flags.rs index 31c1ddb1..395201d7 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; /** @@ -44,11 +46,20 @@ fn test_windows_filetime_output_uses_unix_timestamp() { ); } -#[cfg(target_os = "windows")] #[test] -fn test_windows_mtime_filter_uses_unix_timestamp() { +fn test_mtime_filter_uses_unix_timestamp() { let temp_dir = tempfile::tempdir().unwrap(); - std::fs::write(temp_dir.path().join("recent.txt"), b"recent").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 @@ -63,7 +74,7 @@ fn test_windows_mtime_filter_uses_unix_timestamp() { assert!( str::from_utf8(&output.stdout) .unwrap() - .contains("recent.txt") + .contains("yesterday.txt") ); } From 436d0b45d2d4d055eb79971edfeeff6ab4ee44a2 Mon Sep 17 00:00:00 2001 From: adithya-n05 Date: Sun, 16 Aug 2026 13:57:24 +0100 Subject: [PATCH 3/3] test(filetime): run output regression on all platforms --- tests/test_flags.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_flags.rs b/tests/test_flags.rs index 395201d7..ae653012 100644 --- a/tests/test_flags.rs +++ b/tests/test_flags.rs @@ -24,9 +24,8 @@ fn build_command>(command_args: Vec) -> String { str::from_utf8(&finished.stdout).unwrap().into() } -#[cfg(target_os = "windows")] #[test] -fn test_windows_filetime_output_uses_unix_timestamp() { +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();