From ba60a163fe0b8808a87118a7967c5e17da45a9d8 Mon Sep 17 00:00:00 2001 From: Jacob Mischka Date: Sun, 25 Oct 2020 06:30:38 -0500 Subject: [PATCH] Use local times for time functions This patch uses Chrono for explicit date or datetime parsing, only using humantime for its relative time parsing. The following formats are accepted: 1. Full RFC3339 parsing, requiring an explicit timezone 2. `YY-MM-DD`, defaulting to time `00:00:00` for the given date in the local time zone 3. `YY-MM-DD HH:MM:SS` in the local time zone Fixes #631, #794 --- CHANGELOG.md | 1 + Cargo.lock | 219 +++++++++++++++++++++++++++++++++------------ Cargo.toml | 1 + doc/fd.1 | 14 ++- src/filter/time.rs | 57 +++++++++++- 5 files changed, 228 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08e4e60..d9d98d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - fd cannot search files under a RAM disk, see #752 - fd doesn't show substituted drive on Windows, see #365 - Properly handle write errors to devices that are full, see #737 +- Use local time zone for time functions (`--change-newer-than`, `--change-older-than`), see #631 (@jacobmischka) ## Changes diff --git a/Cargo.lock b/Cargo.lock index dede4b7..85e7279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,21 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.42" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "atty" @@ -48,9 +60,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "bitflags" @@ -59,19 +77,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] -name = "bstr" -version = "0.2.14" +name = "blake2b_simd" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "bstr" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ "memchr", ] [[package]] name = "cc" -version = "1.0.66" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" [[package]] name = "cfg-if" @@ -86,10 +115,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "clap" -version = "2.33.3" +name = "chrono" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "ansi_term 0.11.0", "atty", @@ -102,21 +144,27 @@ dependencies = [ ] [[package]] -name = "crossbeam-utils" -version = "0.8.1" +name = "constant_time_eq" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg", - "cfg-if 1.0.0", + "cfg-if 0.1.10", "lazy_static", ] [[package]] name = "ctrlc" -version = "3.1.9" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232295399409a8b7ae41276757b5a1cc21032848d42bff2352261f958b3ca29a" +checksum = "7a4ba686dff9fa4c1c9636ce1010b0cf98ceb421361b0bb3d6faeec43bd217a7" dependencies = [ "nix", "winapi", @@ -140,9 +188,9 @@ dependencies = [ [[package]] name = "dirs-sys-next" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d" +checksum = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a" dependencies = [ "libc", "redox_users", @@ -156,6 +204,7 @@ dependencies = [ "ansi_term 0.12.1", "anyhow", "atty", + "chrono", "clap", "ctrlc", "diff", @@ -197,9 +246,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fs_extra" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" [[package]] name = "fuchsia-cprng" @@ -209,20 +258,20 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "getrandom" -version = "0.1.15" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ "cfg-if 0.1.10", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "globset" -version = "0.4.8" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" dependencies = [ "aho-corasick", "bstr", @@ -233,24 +282,24 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.17" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] [[package]] name = "humantime" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" +checksum = "b9b6c53306532d3c8e8087b44e6580e10db51a023cf9b433cea2ac38066b92da" [[package]] name = "ignore" -version = "0.4.17" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" +checksum = "128b9e89d15a3faa642ee164c998fd4fae3d89d054463cddb2c25a7baad3a352" dependencies = [ "crossbeam-utils", "globset", @@ -293,24 +342,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.98" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "log" -version = "0.4.11" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ "cfg-if 0.1.10", ] [[package]] name = "lscolors" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e" +checksum = "1f77452267149eac960ded529fe5f5460ddf792845a1d71b5d0cfcee5642e47e" dependencies = [ "ansi_term 0.12.1", ] @@ -323,14 +372,15 @@ checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "nix" -version = "0.20.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" dependencies = [ "bitflags", "cc", - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", + "void", ] [[package]] @@ -342,6 +392,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -391,9 +460,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "redox_syscall" @@ -406,12 +475,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.3.5" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" dependencies = [ "getrandom", - "redox_syscall 0.1.57", + "redox_syscall 0.1.56", + "rust-argon2", ] [[package]] @@ -433,13 +503,25 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "remove_dir_all" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" dependencies = [ "winapi", ] +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "same-file" version = "1.0.6" @@ -495,10 +577,21 @@ dependencies = [ ] [[package]] -name = "unicode-width" -version = "0.1.8" +name = "time" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" [[package]] name = "users" @@ -518,9 +611,15 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" @@ -540,10 +639,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] -name = "winapi" -version = "0.3.9" +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", diff --git a/Cargo.toml b/Cargo.toml index a4d479d..f7e0f67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ globset = "0.4" anyhow = "1.0" dirs-next = "2.0" normpath = "0.3" +chrono = "0.4" [dependencies.clap] version = "2.31.2" diff --git a/doc/fd.1 b/doc/fd.1 index bff3b0e..94feaa1 100644 --- a/doc/fd.1 +++ b/doc/fd.1 @@ -216,8 +216,11 @@ tebibytes .RE .TP .BI "\-\-changed-within " date|duration -Filter results based on the file modification time. The argument can be provided as a specific -point in time (\fIYYYY-MM-DD HH:MM:SS\fR) or as a duration (\fI10h, 1d, 35min\fR). +Filter results based on the file modification time. +Files with modification times greater than or equal to the argument will be returned. +The argument can be provided as a duration (\fI10h, 1d, 35min\fR) or as a specific point +in time in either full RFC3339 format with time zone, or as a date or datetime in the +local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR). .B --change-newer-than can be used as an alias. @@ -226,8 +229,11 @@ Examples: \-\-change-newer-than "2018-10-27 10:00:00" .TP .BI "\-\-changed-before " date|duration -Filter results based on the file modification time. The argument can be provided as a specific -point in time (\fIYYYY-MM-DD HH:MM:SS\fR) or as a duration (\fI10h, 1d, 35min\fR). +Filter results based on the file modification time. +Files with modification times less than or equal to the argument will be returned. +The argument can be provided as a duration (\fI10h, 1d, 35min\fR) or as a specific point +in time in either full RFC3339 format with time zone, or as a date or datetime in the +local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR). .B --change-older-than can be used as an alias. diff --git a/src/filter/time.rs b/src/filter/time.rs index ca37f94..39979af 100644 --- a/src/filter/time.rs +++ b/src/filter/time.rs @@ -1,3 +1,5 @@ +use chrono::{offset::TimeZone, DateTime, Local, NaiveDate}; + use std::time::SystemTime; /// Filter based on time ranges. @@ -11,9 +13,20 @@ impl TimeFilter { fn from_str(ref_time: &SystemTime, s: &str) -> Option { humantime::parse_duration(s) .map(|duration| *ref_time - duration) - .or_else(|_| humantime::parse_rfc3339_weak(s)) - .or_else(|_| humantime::parse_rfc3339_weak(&(s.to_owned() + " 00:00:00"))) .ok() + .or_else(|| { + DateTime::parse_from_rfc3339(s) + .map(|dt| dt.into()) + .ok() + .or_else(|| { + NaiveDate::parse_from_str(s, "%F") + .map(|nd| nd.and_hms(0, 0, 0)) + .ok() + .and_then(|ndt| Local.from_local_datetime(&ndt).single()) + }) + .or_else(|| Local.datetime_from_str(s, "%F %T").ok()) + .map(|dt| dt.into()) + }) } pub fn before(ref_time: &SystemTime, s: &str) -> Option { @@ -39,7 +52,11 @@ mod tests { #[test] fn is_time_filter_applicable() { - let ref_time = humantime::parse_rfc3339("2010-10-10T10:10:10Z").unwrap(); + let ref_time = Local + .datetime_from_str("2010-10-10 10:10:10", "%F %T") + .unwrap() + .into(); + assert!(TimeFilter::after(&ref_time, "1min") .unwrap() .applies_to(&ref_time)); @@ -76,5 +93,39 @@ mod tests { assert!(!TimeFilter::after(&ref_time, t10s_before) .unwrap() .applies_to(&t1m_ago)); + + let same_day = "2010-10-10"; + assert!(!TimeFilter::before(&ref_time, same_day) + .unwrap() + .applies_to(&ref_time)); + assert!(!TimeFilter::before(&ref_time, same_day) + .unwrap() + .applies_to(&t1m_ago)); + + assert!(TimeFilter::after(&ref_time, same_day) + .unwrap() + .applies_to(&ref_time)); + assert!(TimeFilter::after(&ref_time, same_day) + .unwrap() + .applies_to(&t1m_ago)); + + let ref_time = DateTime::parse_from_rfc3339("2010-10-10T10:10:10+00:00") + .unwrap() + .into(); + let t1m_ago = ref_time - Duration::from_secs(60); + let t10s_before = "2010-10-10T10:10:00+00:00"; + assert!(!TimeFilter::before(&ref_time, t10s_before) + .unwrap() + .applies_to(&ref_time)); + assert!(TimeFilter::before(&ref_time, t10s_before) + .unwrap() + .applies_to(&t1m_ago)); + + assert!(TimeFilter::after(&ref_time, t10s_before) + .unwrap() + .applies_to(&ref_time)); + assert!(!TimeFilter::after(&ref_time, t10s_before) + .unwrap() + .applies_to(&t1m_ago)); } }