Repository: sharkdp/fd (language: rust)

Issue:
Time functions assume UTC and don't allow for RFC 3339 offsets
`--change-newer-than` assumes given time is in UTC, not local timezone. Furthermore, offsets like '2020-07-19T00:38:00-07:00' throw errors.

fd version 8.1.1 on Arch Linux


Discussion / hints:
Thank you for reporting this. That should definitely be fixed.

Is there an easy way to reproduce this bug?
I am also experiencing this problem. 

I have been looking at the source code and I have found that [ humantime::parse_rfc3339_weak] only supports UTC and timezone conversions are not supported(https://docs.rs/humantime/2.0.1/humantime/fn.parse_rfc3339_weak.html) which is used to parse the date in https://github.com/sharkdp/fd/blob/74c3431a2bf74ce78c26974f3cc07570e1d3baf3/src/filter/time.rs#L14 

Was surprizing! 

I have done a small search and the [chrono library](https://docs.rs/chrono/0.4.15/chrono/) seems to be better suited for our usecase. 

@sharkdp Would you be interested in a PR with an attempt to solve this?
Thank you for the feedback. Yes, I would be interested in a fix for this!

But let's please make sure that:
- we don't break any existing functionality (it should hopefully be well tested)
- we add regression tests for the new expected behavior.

Also, it would be great if someone could post a simple way to reproduce this error. I haven't looked into it in detail, so far.
The `humantime` docs state that indeed only UTC is supported: https://docs.rs/humantime/2.0.1/humantime/fn.parse_rfc3339_weak.html

It's pretty straightforward to reproduce, here's an example using this repo that I just cloned:

```
$ ll
.rw-r--r-- mischka users  2.3 KB Sun Oct 25 04:55:02 2020   appveyor.yml   
.rw-r--r-- mischka users  792 B  Sun Oct 25 04:55:02 2020   build.rs       
.rw-r--r-- mischka users 14.9 KB Sun Oct 25 04:55:18 2020   Cargo.lock     
.rw-r--r-- mischka users  1.6 KB Sun Oct 25 04:55:02 2020   Cargo.toml     
.rw-r--r-- mischka users 19.3 KB Sun Oct 25 04:55:02 2020   CHANGELOG.md   
drwxr-xr-x mischka users    4 KB Sun Oct 25 04:55:02 2020   ci             
drwxr-xr-x mischka users    4 KB Sun Oct 25 04:55:02 2020   contrib        
.rw-r--r-- mischka users  877 B  Sun Oct 25 04:55:02 2020   CONTRIBUTING.md
drwxr-xr-x mischka users    4 KB Sun Oct 25 04:55:02 2020   doc            
.rw-r--r-- mischka users 10.6 KB Sun Oct 25 04:55:02 2020   LICENSE-APACHE 
.rw-r--r-- mischka users  1.1 KB Sun Oct 25 04:55:02 2020   LICENSE-MIT    
.rw-r--r-- mischka users 19.6 KB Sun Oct 25 04:55:02 2020   README.md      
drwxr-xr-x mischka users    4 KB Sun Oct 25 04:55:02 2020   src            
drwxr-xr-x mischka users    4 KB Sun Oct 25 04:55:24 2020   target         
drwxr-xr-x mischka users    4 KB Sun Oct 25 04:55:02 2020   tests     
     
$ fd --change-newer-than 2020-10-25T04:55:03-07:00
[fd error]: '2020-10-25T04:55:03-07:00' is not a valid date or duration. See 'fd --help'.

$ fd -d 1 --change-newer-than '2020-10-25 04:55:03'
CHANGELOG.md
CONTRIBUTING.md
Cargo.lock
Cargo.toml
LICENSE-APACHE
LICENSE-MIT
README.md
appveyor.yml
build.rs
ci
contrib
doc
src
tests

```

_Regarding the reproduction_

For me there are actually two problems that need to addressed:
- When a user adds a time without an offset/timezone it is reasonable for him to assume that the time he entered would be interpreted according to his local system timezone. This is especially dangerous and important to me because it can produce subtle unexpected results. This is actually the issue that I have experienced.
- The inability to influence the interpretation of time by adding an explicit offset or timezone. If someone deduced the first problem, then he could use this as workaround to pass his local time with an offset and not have to do a calculation manually to UTC.

The second problem is a actually ease to reproduce as @jacobmischka has demonstrated above. Thanks @jacobmischka for sharing!

The first problem is more difficult to reproduce in an automated regression test. They would depend on a the locally setup timezone which is a global system property. The system that is running the tests should be setup in a non UTC timezone & the tests should be able to know or deduce what the setup timezone is. 

_Regarding the implementation_

I did an effort to find a library that would properly parse local times to solve the first problem. My best result was to use chrono 0.4. I did my tests when we were in EEST but it would assume EET and so still interpreted the local times wrongly. _(Just today we switched to EET. If I retested now it would work OK :) )_

If anyone knows how to successfully parse time strings appropriately for local times in Rust it would be really helpful if he shared his experience. 


Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08e4e60fe..d9d98d616 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 dede4b7ac..85e727922 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 = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
+
+[[package]]
+name = "base64"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
 
 [[package]]
 name = "bitflags"
@@ -58,20 +76,31 @@ version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 
+[[package]]
+name = "blake2b_simd"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "constant_time_eq",
+]
+
 [[package]]
 name = "bstr"
-version = "0.2.14"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
+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"
@@ -85,11 +114,24 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "chrono"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+dependencies = [
+ "libc",
+ "num-integer",
+ "num-traits",
+ "time",
+ "winapi",
+]
+
 [[package]]
 name = "clap"
-version = "2.33.3"
+version = "2.33.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
 dependencies = [
  "ansi_term 0.11.0",
  "atty",
@@ -101,22 +143,28 @@ dependencies = [
  "vec_map",
 ]
 
+[[package]]
+name = "constant_time_eq"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
+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"
@@ -494,11 +576,22 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
+
 [[package]]
 name = "unicode-width"
-version = "0.1.8"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+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 = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
+
+[[package]]
+name = "void"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 
 [[package]]
 name = "walkdir"
@@ -539,11 +638,17 @@ version = "0.9.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
 [[package]]
 name = "winapi"
-version = "0.3.9"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
 dependencies = [
  "winapi-i686-pc-windows-gnu",
  "winapi-x86_64-pc-windows-gnu",
diff --git a/Cargo.toml b/Cargo.toml
index a4d479d79..f7e0f67eb 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 78249877e..a0c933066 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 ca37f9457..39979afaf 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<SystemTime> {
         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<TimeFilter> {
@@ -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));
     }
 }


Test patch: