Repository: sharkdp/fd (rust)

Issue:
Replace `humantime` crate
[`humantime`](https://crates.io/crates/humantime) crate seems to be unmaintained and [`jiff`](https://crates.io/crates/jiff) crate provides the same functionality.

ref: rustsec/advisory-db#2249

Pull request:
chore(deps): Replace `humantime` and `chrono` with `jiff`
Closes #1689

I think the behavior of `fd` is probably the same as before this change, but I don't know if [`jiff`](https://crates.io/crates/jiff) crate is a drop-in replacement for [`humantime`](https://crates.io/crates/humantime) crate, so I added an entry to the changelog.

see also: <https://docs.rs/jiff/0.2.4/jiff/fmt/friendly/index.html#comparison-with-the-humantime-crate>

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e891da1..57b34b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 
 ## Changes
 
+- Replace `humantime` crate and `chrono` crate with `jiff` crate, see #1690 (@sorairolake)
 
 ## Other
 
diff --git a/Cargo.lock b/Cargo.lock
index 8fbb8ee..3783f3d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,21 +11,6 @@ dependencies = [
  "memchr",
 ]
 
-[[package]]
-name = "android-tzdata"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
-
-[[package]]
-name = "android_system_properties"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "anstream"
 version = "0.6.15"
@@ -92,12 +77,6 @@ dependencies = [
  "nix 0.24.3",
 ]
 
-[[package]]
-name = "autocfg"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
-
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -120,12 +99,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "bumpalo"
-version = "3.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
-
 [[package]]
 name = "cc"
 version = "1.1.13"
@@ -147,18 +120,6 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
-[[package]]
-name = "chrono"
-version = "0.4.39"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
-dependencies = [
- "android-tzdata",
- "iana-time-zone",
- "num-traits",
- "windows-targets",
-]
-
 [[package]]
 name = "clap"
 version = "4.5.31"
@@ -215,12 +176,6 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
 
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-
 [[package]]
 name = "crossbeam-channel"
 version = "0.5.14"
@@ -316,7 +271,6 @@ dependencies = [
  "aho-corasick",
  "anyhow",
  "argmax",
- "chrono",
  "clap",
  "clap_complete",
  "crossbeam-channel",
@@ -326,9 +280,9 @@ dependencies = [
  "faccess",
  "filetime",
  "globset",
- "humantime",
  "ignore",
  "jemallocator",
+ "jiff",
  "libc",
  "lscolors",
  "nix 0.29.0",
@@ -393,35 +347,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
-name = "iana-time-zone"
-version = "0.1.60"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
-dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "wasm-bindgen",
- "windows-core",
-]
-
-[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
-dependencies = [
- "cc",
-]
-
 [[package]]
 name = "ignore"
 version = "0.4.23"
@@ -465,12 +390,44 @@ dependencies = [
 ]
 
 [[package]]
-name = "js-sys"
-version = "0.3.70"
+name = "jiff"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e"
+dependencies = [
+ "jiff-static",
+ "jiff-tzdb-platform",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
+checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9"
 dependencies = [
- "wasm-bindgen",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "962e1dfe9b2d75a84536cf5bf5eaaa4319aa7906c7160134a22883ac316d5f31"
+
+[[package]]
+name = "jiff-tzdb-platform"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a63c62e404e7b92979d2792352d885a7f8f83fd1d0d31eea582d77b2ceca697e"
+dependencies = [
+ "jiff-tzdb",
 ]
 
 [[package]]
@@ -565,35 +522,41 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "num-traits"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
-
 [[package]]
 name = "once_cell"
 version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
+[[package]]
+name = "portable-atomic"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
 [[package]]
 name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.36"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 dependencies = [
  "proc-macro2",
 ]
@@ -692,9 +655,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.75"
+version = "2.0.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -795,61 +758,6 @@ dependencies = [
  "wit-bindgen-rt",
 ]
 
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.93"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
-dependencies = [
- "cfg-if",
- "once_cell",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.93"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
-dependencies = [
- "bumpalo",
- "log",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.93"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.93"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.93"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -881,15 +789,6 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
-[[package]]
-name = "windows-core"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
-dependencies = [
- "windows-targets",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.52.0"
diff --git a/Cargo.toml b/Cargo.toml
index 8244aa7..81f22db 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,7 +41,6 @@ ignore = "0.4.23"
 regex = "1.11.1"
 regex-syntax = "0.8"
 ctrlc = "3.2"
-humantime = "2.1"
 globset = "0.4"
 anyhow = "1.0"
 etcetera = "0.9"
@@ -49,16 +48,12 @@ normpath = "1.1.1"
 crossbeam-channel = "0.5.14"
 clap_complete = {version = "4.5.44", optional = true}
 faccess = "0.2.4"
+jiff = "0.2.4"
 
 [dependencies.clap]
 version = "4.5.31"
 features = ["suggestions", "color", "wrap_help", "cargo", "derive"]
 
-[dependencies.chrono]
-version = "0.4.39"
-default-features = false
-features = ["std", "clock"]
-
 [dependencies.lscolors]
 version = "0.20"
 default-features = false
diff --git a/src/filter/time.rs b/src/filter/time.rs
index cd17542..b70509d 100644
--- a/src/filter/time.rs
+++ b/src/filter/time.rs
@@ -1,4 +1,4 @@
-use chrono::{DateTime, Local, NaiveDate, NaiveDateTime};
+use jiff::{civil::DateTime, tz::TimeZone, Span, Timestamp, Zoned};
 
 use std::time::SystemTime;
 
@@ -11,32 +11,30 @@ pub enum TimeFilter {
 
 impl TimeFilter {
     fn from_str(ref_time: &SystemTime, s: &str) -> Option<SystemTime> {
-        humantime::parse_duration(s)
-            .map(|duration| *ref_time - duration)
+        s.parse::<Span>()
+            .and_then(|duration| {
+                Zoned::try_from(*ref_time).and_then(|zdt| zdt.checked_sub(duration))
+            })
             .ok()
             .or_else(|| {
-                DateTime::parse_from_rfc3339(s)
-                    .map(|dt| dt.into())
+                let local_tz = TimeZone::system();
+                s.parse::<Timestamp>()
+                    .map(|ts| ts.to_zoned(TimeZone::UTC))
                     .ok()
                     .or_else(|| {
-                        NaiveDate::parse_from_str(s, "%F")
-                            .ok()?
-                            .and_hms_opt(0, 0, 0)?
-                            .and_local_timezone(Local)
-                            .latest()
-                    })
-                    .or_else(|| {
-                        NaiveDateTime::parse_from_str(s, "%F %T")
-                            .ok()?
-                            .and_local_timezone(Local)
-                            .latest()
+                        s.parse::<DateTime>()
+                            .map(|dt| local_tz.to_ambiguous_zoned(dt))
+                            .and_then(|zdt| zdt.later())
+                            .ok()
                     })
                     .or_else(|| {
                         let timestamp_secs = s.strip_prefix('@')?.parse().ok()?;
-                        DateTime::from_timestamp(timestamp_secs, 0).map(Into::into)
+                        Timestamp::from_second(timestamp_secs)
+                            .map(|ts| ts.to_zoned(TimeZone::UTC))
+                            .ok()
                     })
-                    .map(|dt| dt.into())
             })
+            .map(SystemTime::from)
     }
 
     pub fn before(ref_time: &SystemTime, s: &str) -> Option<TimeFilter> {
@@ -62,10 +60,10 @@ mod tests {
 
     #[test]
     fn is_time_filter_applicable() {
-        let ref_time = NaiveDateTime::parse_from_str("2010-10-10 10:10:10", "%F %T")
-            .unwrap()
-            .and_local_timezone(Local)
-            .latest()
+        let local_tz = TimeZone::system();
+        let ref_time = local_tz
+            .to_ambiguous_zoned("2010-10-10 10:10:10".parse::<DateTime>().unwrap())
+            .later()
             .unwrap()
             .into();
 
@@ -121,7 +119,8 @@ mod tests {
             .unwrap()
             .applies_to(&t1m_ago));
 
-        let ref_time = DateTime::parse_from_rfc3339("2010-10-10T10:10:10+00:00")
+        let ref_time = "2010-10-10T10:10:10+00:00"
+            .parse::<Timestamp>()
             .unwrap()
             .into();
         let t1m_ago = ref_time - Duration::from_secs(60);
@@ -141,7 +140,8 @@ mod tests {
             .applies_to(&t1m_ago));
 
         let ref_timestamp = 1707723412u64; // Mon Feb 12 07:36:52 UTC 2024
-        let ref_time = DateTime::parse_from_rfc3339("2024-02-12T07:36:52+00:00")
+        let ref_time = "2024-02-12T07:36:52+00:00"
+            .parse::<Timestamp>()
             .unwrap()
             .into();
         let t1m_ago = ref_time - Duration::from_secs(60);


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 81010cf..8bba405 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -8,6 +8,7 @@ use std::path::Path;
 use std::time::{Duration, SystemTime};
 use test_case::test_case;
 
+use jiff::Timestamp;
 use normpath::PathExt;
 use regex::escape;
 
@@ -2288,7 +2289,10 @@ fn test_modified_relative() {
 
 #[cfg(test)]
 fn change_file_modified<P: AsRef<Path>>(path: P, iso_date: &str) {
-    let st = humantime::parse_rfc3339(iso_date).expect("invalid date");
+    let st = iso_date
+        .parse::<Timestamp>()
+        .map(SystemTime::from)
+        .expect("invalid date");
     let ft = filetime::FileTime::from_system_time(st);
     filetime::set_file_times(path, ft, ft).expect("time modification failde");
 }