You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
refactor: replace chrono with jiff
This commit is contained in:
Generated
+50
-184
@@ -11,66 +11,18 @@ 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 = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -132,30 +84,6 @@ version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"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 = "indexmap"
|
||||
version = "2.9.0"
|
||||
@@ -173,10 +101,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -191,20 +121,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
name = "jiff-tzdb-platform"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
|
||||
dependencies = [
|
||||
"jiff-tzdb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -227,17 +156,10 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"jiff",
|
||||
"num-traits",
|
||||
"regex",
|
||||
@@ -372,12 +294,6 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
@@ -404,12 +320,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
@@ -451,121 +361,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.100"
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.100"
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.100"
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.100"
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.0"
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.2"
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
||||
+1
-2
@@ -10,10 +10,9 @@ rust-version = "1.71.1"
|
||||
|
||||
[dependencies]
|
||||
regex = "1.10.4"
|
||||
chrono = { version="0.4.38", default-features=false, features=["std", "alloc", "clock"] }
|
||||
winnow = "0.7.10"
|
||||
num-traits = "0.2.19"
|
||||
jiff = { version = "0.2.15", default-features = false, features = ["std"] }
|
||||
jiff = { version = "0.2.15", default-features = false, features = ["tz-system", "tzdb-bundle-platform", "tzdb-zoneinfo"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = "0.26"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[](https://github.com/uutils/parse_datetime/blob/main/LICENSE)
|
||||
[](https://codecov.io/gh/uutils/parse_datetime)
|
||||
|
||||
A Rust crate for parsing human-readable relative time strings and human-readable datetime strings and converting them to a `DateTime`.
|
||||
A Rust crate for parsing human-readable relative time strings and human-readable datetime strings and converting them to a jiff's `Zoned` object.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -12,7 +12,7 @@ A Rust crate for parsing human-readable relative time strings and human-readable
|
||||
- Supports positive and negative durations.
|
||||
- Allows for chaining time units (e.g., "1 hour 2 minutes" or "2 days 2 hours ago").
|
||||
- Calculate durations relative to a specified date.
|
||||
- Relies on Chrono
|
||||
- Relies on Jiff
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -25,26 +25,26 @@ cargo add parse_datetime
|
||||
Then, import the crate and use the `parse_datetime_at_date` function:
|
||||
|
||||
```rs
|
||||
use chrono::{Duration, Local};
|
||||
use jiff::{ToSpan, Zoned};
|
||||
use parse_datetime::parse_datetime_at_date;
|
||||
|
||||
let now = Local::now();
|
||||
let after = parse_datetime_at_date(now, "+3 days");
|
||||
let now = Zoned::now();
|
||||
let after = parse_datetime_at_date(now.clone(), "+3 days");
|
||||
|
||||
assert_eq!(
|
||||
(now + Duration::days(3)).naive_utc(),
|
||||
after.unwrap().naive_utc()
|
||||
now.checked_add(3.days()).unwrap(),
|
||||
after.unwrap()
|
||||
);
|
||||
```
|
||||
|
||||
For DateTime parsing, import the `parse_datetime` function:
|
||||
|
||||
```rs
|
||||
use jiff::{civil::{date, time} ,Zoned};
|
||||
use parse_datetime::parse_datetime;
|
||||
use chrono::{Local, TimeZone};
|
||||
|
||||
let dt = parse_datetime("2021-02-14 06:37:47");
|
||||
assert_eq!(dt.unwrap(), Local.with_ymd_and_hms(2021, 2, 14, 6, 37, 47).unwrap());
|
||||
assert_eq!(dt.unwrap(), Zoned::now().with().date(date(2021, 2, 14)).time(time(6, 37, 47, 0)).build().unwrap());
|
||||
```
|
||||
|
||||
### Supported Formats
|
||||
@@ -69,7 +69,7 @@ The `parse_datetime` and `parse_datetime_at_date` functions support absolute dat
|
||||
|
||||
The `parse_datetime` and `parse_datetime_at_date` function return:
|
||||
|
||||
- `Ok(DateTime<FixedOffset>)` - If the input string can be parsed as a datetime
|
||||
- `Ok(Zoned)` - If the input string can be parsed as a `Zoned` object
|
||||
- `Err(ParseDateTimeError::InvalidInput)` - If the input string cannot be parsed
|
||||
|
||||
## Fuzzer
|
||||
|
||||
Generated
+80
-11
@@ -112,6 +112,31 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.32"
|
||||
@@ -148,9 +173,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.18"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@@ -175,28 +200,43 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"jiff",
|
||||
"num-traits",
|
||||
"regex",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.59"
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[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.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -230,6 +270,26 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -238,9 +298,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -323,6 +383,15 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
|
||||
+43
-150
@@ -1,7 +1,7 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{DateTime, Datelike, FixedOffset, NaiveDate, TimeZone, Timelike};
|
||||
use jiff::{civil, Span, Zoned};
|
||||
|
||||
use super::{date, epoch, relative, time, timezone, weekday, year};
|
||||
|
||||
@@ -12,7 +12,7 @@ use super::{date, epoch, relative, time, timezone, weekday, year};
|
||||
/// leave it unset to use the current date and time as the base.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct DateTimeBuilder {
|
||||
base: Option<DateTime<FixedOffset>>,
|
||||
base: Option<Zoned>,
|
||||
timestamp: Option<epoch::Timestamp>,
|
||||
date: Option<date::Date>,
|
||||
time: Option<time::Time>,
|
||||
@@ -28,7 +28,7 @@ impl DateTimeBuilder {
|
||||
|
||||
/// Sets the base date and time for the builder. If not set, the current
|
||||
/// date and time will be used.
|
||||
pub(super) fn set_base(mut self, base: DateTime<FixedOffset>) -> Self {
|
||||
pub(super) fn set_base(mut self, base: Zoned) -> Self {
|
||||
self.base = Some(base);
|
||||
self
|
||||
}
|
||||
@@ -148,27 +148,20 @@ impl DateTimeBuilder {
|
||||
self.set_time(time)
|
||||
}
|
||||
|
||||
fn build_from_timestamp(
|
||||
ts: epoch::Timestamp,
|
||||
tz: &FixedOffset,
|
||||
) -> Option<DateTime<FixedOffset>> {
|
||||
match chrono::Utc.timestamp_opt(ts.second, ts.nanosecond) {
|
||||
chrono::MappedLocalTime::Single(t) => Some(t.with_timezone(tz)),
|
||||
chrono::MappedLocalTime::Ambiguous(earliest, _latest) => {
|
||||
// When there is a fold in the local time, we use the earliest
|
||||
// one.
|
||||
Some(earliest.with_timezone(tz))
|
||||
}
|
||||
chrono::MappedLocalTime::None => None, // Invalid timestamp
|
||||
}
|
||||
fn build_from_timestamp(ts: epoch::Timestamp, tz: jiff::tz::TimeZone) -> Option<Zoned> {
|
||||
Some(
|
||||
jiff::Timestamp::new(ts.second, ts.nanosecond as i32)
|
||||
.ok()?
|
||||
.to_zoned(tz),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn build(self) -> Option<DateTime<FixedOffset>> {
|
||||
let base = self.base.unwrap_or_else(|| chrono::Local::now().into());
|
||||
pub(super) fn build(self) -> Option<Zoned> {
|
||||
let base = self.base.unwrap_or(Zoned::now());
|
||||
|
||||
// If a timestamp is set, we use it to build the DateTime object.
|
||||
// If a timestamp is set, we use it to build the `Zoned` object.
|
||||
if let Some(ts) = self.timestamp {
|
||||
return Self::build_from_timestamp(ts, base.offset());
|
||||
return Self::build_from_timestamp(ts, base.offset().to_time_zone());
|
||||
}
|
||||
|
||||
// If any of the following items are set, we truncate the time portion
|
||||
@@ -181,59 +174,30 @@ impl DateTimeBuilder {
|
||||
{
|
||||
base
|
||||
} else {
|
||||
new_date(
|
||||
base.year(),
|
||||
base.month(),
|
||||
base.day(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
*base.offset(),
|
||||
)?
|
||||
base.with().time(civil::time(0, 0, 0, 0)).build().ok()?
|
||||
};
|
||||
|
||||
if let Some(date::Date { year, month, day }) = self.date {
|
||||
dt = new_date(
|
||||
year.map(|x| x as i32).unwrap_or(dt.year()),
|
||||
month as u32,
|
||||
day as u32,
|
||||
dt.hour(),
|
||||
dt.minute(),
|
||||
dt.second(),
|
||||
dt.nanosecond(),
|
||||
*dt.offset(),
|
||||
)?;
|
||||
if let Some(date) = self.date {
|
||||
let d: civil::Date = if date.year.is_some() {
|
||||
date.try_into().ok()?
|
||||
} else {
|
||||
date.with_year(dt.date().year() as u16).try_into().ok()?
|
||||
};
|
||||
dt = dt.with().date(d).build().ok()?;
|
||||
}
|
||||
|
||||
if let Some(time::Time {
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
nanosecond,
|
||||
ref offset,
|
||||
}) = self.time
|
||||
{
|
||||
let offset = offset
|
||||
.clone()
|
||||
.and_then(|o| chrono::FixedOffset::try_from(o).ok())
|
||||
.unwrap_or(*dt.offset());
|
||||
if let Some(time) = self.time.clone() {
|
||||
if let Some(offset) = &time.offset {
|
||||
dt = dt.datetime().to_zoned(offset.try_into().ok()?).ok()?;
|
||||
}
|
||||
|
||||
dt = new_date(
|
||||
dt.year(),
|
||||
dt.month(),
|
||||
dt.day(),
|
||||
hour as u32,
|
||||
minute as u32,
|
||||
second as u32,
|
||||
nanosecond,
|
||||
offset,
|
||||
)?;
|
||||
let t: civil::Time = time.try_into().ok()?;
|
||||
dt = dt.with().time(t).build().ok()?;
|
||||
}
|
||||
|
||||
if let Some(weekday::Weekday { offset, day }) = self.weekday {
|
||||
if self.time.is_none() {
|
||||
dt = new_date(dt.year(), dt.month(), dt.day(), 0, 0, 0, 0, *dt.offset())?;
|
||||
dt = dt.with().time(civil::time(0, 0, 0, 0)).build().ok()?;
|
||||
}
|
||||
|
||||
let mut offset = offset;
|
||||
@@ -245,7 +209,7 @@ impl DateTimeBuilder {
|
||||
// Consider this:
|
||||
// Assuming today is Monday, next Friday is actually THIS Friday;
|
||||
// but next Monday is indeed NEXT Monday.
|
||||
if dt.weekday() != day && offset > 0 {
|
||||
if dt.date().weekday() != day && offset > 0 {
|
||||
offset -= 1;
|
||||
}
|
||||
|
||||
@@ -265,101 +229,30 @@ impl DateTimeBuilder {
|
||||
//
|
||||
// Example 4: next Thursday (x = 1, day = Thursday)
|
||||
// delta = (3 - 3) % 7 + (1) * 7 = 7
|
||||
let delta = (day.num_days_from_monday() as i32
|
||||
- dt.weekday().num_days_from_monday() as i32)
|
||||
let delta = (day.since(civil::Weekday::Monday) as i32
|
||||
- dt.date().weekday().since(civil::Weekday::Monday) as i32)
|
||||
.rem_euclid(7)
|
||||
+ offset.checked_mul(7)?;
|
||||
|
||||
dt = if delta < 0 {
|
||||
dt.checked_sub_days(chrono::Days::new((-delta) as u64))?
|
||||
} else {
|
||||
dt.checked_add_days(chrono::Days::new(delta as u64))?
|
||||
}
|
||||
dt = dt.checked_add(Span::new().try_days(delta).ok()?).ok()?;
|
||||
}
|
||||
|
||||
for rel in self.relative {
|
||||
// TODO: Handle potential overflows in the addition operations.
|
||||
match rel {
|
||||
relative::Relative::Years(x) => {
|
||||
dt = dt.with_year(dt.year() + x)?;
|
||||
}
|
||||
relative::Relative::Months(x) => {
|
||||
// *NOTE* This is done in this way to conform to
|
||||
// GNU behavior.
|
||||
let days = last_day_of_month(dt.year(), dt.month());
|
||||
if x >= 0 {
|
||||
dt += dt
|
||||
.date_naive()
|
||||
.checked_add_days(chrono::Days::new((days * x as u32) as u64))?
|
||||
.signed_duration_since(dt.date_naive());
|
||||
} else {
|
||||
dt += dt
|
||||
.date_naive()
|
||||
.checked_sub_days(chrono::Days::new((days * -x as u32) as u64))?
|
||||
.signed_duration_since(dt.date_naive());
|
||||
}
|
||||
}
|
||||
relative::Relative::Days(x) => dt += chrono::Duration::days(x.into()),
|
||||
relative::Relative::Hours(x) => dt += chrono::Duration::hours(x.into()),
|
||||
relative::Relative::Minutes(x) => {
|
||||
dt += chrono::Duration::try_minutes(x.into())?;
|
||||
}
|
||||
relative::Relative::Seconds(s, ns) => {
|
||||
dt += chrono::Duration::seconds(s);
|
||||
dt += chrono::Duration::nanoseconds(ns.into());
|
||||
}
|
||||
}
|
||||
dt = dt
|
||||
.checked_add::<Span>(if let relative::Relative::Months(x) = rel {
|
||||
// *NOTE* This is done in this way to conform to GNU behavior.
|
||||
let days = dt.date().last_of_month().day() as i32;
|
||||
Span::new().try_days(days.checked_mul(x)?).ok()?
|
||||
} else {
|
||||
rel.try_into().ok()?
|
||||
})
|
||||
.ok()?;
|
||||
}
|
||||
|
||||
if let Some(offset) = self.timezone {
|
||||
dt = with_timezone_restore(offset, dt)?;
|
||||
if let Some(offset) = &self.timezone {
|
||||
dt = dt.datetime().to_zoned(offset.try_into().ok()?).ok()?;
|
||||
}
|
||||
|
||||
Some(dt)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments, deprecated)]
|
||||
fn new_date(
|
||||
year: i32,
|
||||
month: u32,
|
||||
day: u32,
|
||||
hour: u32,
|
||||
minute: u32,
|
||||
second: u32,
|
||||
nano: u32,
|
||||
offset: FixedOffset,
|
||||
) -> Option<DateTime<FixedOffset>> {
|
||||
let newdate = NaiveDate::from_ymd_opt(year, month, day)
|
||||
.and_then(|naive| naive.and_hms_nano_opt(hour, minute, second, nano))?;
|
||||
|
||||
Some(DateTime::<FixedOffset>::from_local(newdate, offset))
|
||||
}
|
||||
|
||||
/// Restores year, month, day, etc after applying the timezone
|
||||
/// returns None if timezone overflows the date
|
||||
fn with_timezone_restore(
|
||||
offset: timezone::Offset,
|
||||
at: DateTime<FixedOffset>,
|
||||
) -> Option<DateTime<FixedOffset>> {
|
||||
let offset: FixedOffset = chrono::FixedOffset::try_from(offset).ok()?;
|
||||
let copy = at;
|
||||
let x = at
|
||||
.with_timezone(&offset)
|
||||
.with_day(copy.day())?
|
||||
.with_month(copy.month())?
|
||||
.with_year(copy.year())?
|
||||
.with_hour(copy.hour())?
|
||||
.with_minute(copy.minute())?
|
||||
.with_second(copy.second())?
|
||||
.with_nanosecond(copy.nanosecond())?;
|
||||
Some(x)
|
||||
}
|
||||
|
||||
fn last_day_of_month(year: i32, month: u32) -> u32 {
|
||||
NaiveDate::from_ymd_opt(year, month + 1, 1)
|
||||
.unwrap_or(NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap())
|
||||
.pred_opt()
|
||||
.unwrap()
|
||||
.day()
|
||||
}
|
||||
|
||||
@@ -47,6 +47,16 @@ pub(crate) struct Date {
|
||||
pub(crate) year: Option<u16>,
|
||||
}
|
||||
|
||||
impl Date {
|
||||
pub(super) fn with_year(self, year: u16) -> Self {
|
||||
Date {
|
||||
day: self.day,
|
||||
month: self.month,
|
||||
year: Some(year),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(&str, u8, u8)> for Date {
|
||||
type Error = &'static str;
|
||||
|
||||
|
||||
+55
-60
@@ -45,7 +45,7 @@ mod ordinal;
|
||||
mod primitive;
|
||||
|
||||
use builder::DateTimeBuilder;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use jiff::Zoned;
|
||||
use primitive::space;
|
||||
use winnow::{
|
||||
combinator::{alt, eof, terminated, trace},
|
||||
@@ -68,22 +68,17 @@ pub(crate) enum Item {
|
||||
Pure(String),
|
||||
}
|
||||
|
||||
/// Build a `DateTime<FixedOffset>` from a `DateTimeBuilder` and a base date.
|
||||
pub(crate) fn at_date(
|
||||
builder: DateTimeBuilder,
|
||||
base: DateTime<FixedOffset>,
|
||||
) -> Result<DateTime<FixedOffset>, ParseDateTimeError> {
|
||||
/// Build a `Zoned` object from a `DateTimeBuilder` and a base `Zoned` object.
|
||||
pub(crate) fn at_date(builder: DateTimeBuilder, base: Zoned) -> Result<Zoned, ParseDateTimeError> {
|
||||
builder
|
||||
.set_base(base)
|
||||
.build()
|
||||
.ok_or(ParseDateTimeError::InvalidInput)
|
||||
}
|
||||
|
||||
/// Build a `DateTime<FixedOffset>` from a `DateTimeBuilder` and the current
|
||||
/// time.
|
||||
pub(crate) fn at_local(
|
||||
builder: DateTimeBuilder,
|
||||
) -> Result<DateTime<FixedOffset>, ParseDateTimeError> {
|
||||
/// Build a `Zoned` object from a `DateTimeBuilder` and a default `Zoned` object
|
||||
/// (the current time in the local timezone).
|
||||
pub(crate) fn at_local(builder: DateTimeBuilder) -> Result<Zoned, ParseDateTimeError> {
|
||||
builder.build().ok_or(ParseDateTimeError::InvalidInput)
|
||||
}
|
||||
|
||||
@@ -292,14 +287,12 @@ fn expect_error(input: &mut &str, reason: &'static str) -> ErrMode<ContextError>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{at_date, parse, DateTimeBuilder};
|
||||
use chrono::{
|
||||
DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike,
|
||||
Utc,
|
||||
};
|
||||
use jiff::{civil::DateTime, tz::TimeZone, ToSpan, Zoned};
|
||||
|
||||
fn at_utc(builder: DateTimeBuilder) -> DateTime<FixedOffset> {
|
||||
at_date(builder, Utc::now().fixed_offset()).unwrap()
|
||||
use super::{at_date, parse, DateTimeBuilder};
|
||||
|
||||
fn at_utc(builder: DateTimeBuilder) -> Zoned {
|
||||
at_date(builder, Zoned::now().with_time_zone(TimeZone::UTC)).unwrap()
|
||||
}
|
||||
|
||||
fn test_eq_fmt(fmt: &str, input: &str) -> String {
|
||||
@@ -308,7 +301,7 @@ mod tests {
|
||||
.map(at_utc)
|
||||
.map_err(|e| eprintln!("TEST FAILED AT:\n{e}"))
|
||||
.expect("parsing failed during tests")
|
||||
.format(fmt)
|
||||
.strftime(fmt)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
@@ -458,75 +451,77 @@ mod tests {
|
||||
#[test]
|
||||
fn relative_weekday() {
|
||||
// Jan 1 2025 is a Wed
|
||||
let now = Utc
|
||||
.from_utc_datetime(&NaiveDateTime::new(
|
||||
NaiveDate::from_ymd_opt(2025, 1, 1).unwrap(),
|
||||
NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
|
||||
))
|
||||
.fixed_offset();
|
||||
let now = "2025-01-01 00:00:00"
|
||||
.parse::<DateTime>()
|
||||
.unwrap()
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "last wed").unwrap(), now).unwrap(),
|
||||
now - chrono::Duration::days(7)
|
||||
);
|
||||
assert_eq!(at_date(parse(&mut "this wed").unwrap(), now).unwrap(), now);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "next wed").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(7)
|
||||
at_date(parse(&mut "last wed").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_sub(7.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "last thu").unwrap(), now).unwrap(),
|
||||
now - chrono::Duration::days(6)
|
||||
at_date(parse(&mut "this wed").unwrap(), now.clone()).unwrap(),
|
||||
now
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "this thu").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(1)
|
||||
at_date(parse(&mut "next wed").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(7.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "next thu").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(1)
|
||||
at_date(parse(&mut "last thu").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_sub(6.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "1 wed").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(7)
|
||||
at_date(parse(&mut "this thu").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(1.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "1 thu").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(1)
|
||||
at_date(parse(&mut "next thu").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(1.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "2 wed").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(14)
|
||||
at_date(parse(&mut "1 wed").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(7.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "2 thu").unwrap(), now).unwrap(),
|
||||
now + chrono::Duration::days(8)
|
||||
at_date(parse(&mut "1 thu").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(1.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "2 wed").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(14.days()).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
at_date(parse(&mut "2 thu").unwrap(), now.clone()).unwrap(),
|
||||
now.checked_add(8.days()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relative_date_time() {
|
||||
let now = Utc::now().fixed_offset();
|
||||
let now = Zoned::now().with_time_zone(TimeZone::UTC);
|
||||
|
||||
let result = at_date(parse(&mut "2 days ago").unwrap(), now).unwrap();
|
||||
assert_eq!(result, now - chrono::Duration::days(2));
|
||||
let result = at_date(parse(&mut "2 days ago").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result, now.checked_sub(2.days()).unwrap());
|
||||
assert_eq!(result.hour(), now.hour());
|
||||
assert_eq!(result.minute(), now.minute());
|
||||
assert_eq!(result.second(), now.second());
|
||||
|
||||
let result = at_date(parse(&mut "2 days 3 days ago").unwrap(), now).unwrap();
|
||||
assert_eq!(result, now - chrono::Duration::days(1));
|
||||
let result = at_date(parse(&mut "2 days 3 days ago").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result, now.checked_sub(1.days()).unwrap());
|
||||
assert_eq!(result.hour(), now.hour());
|
||||
assert_eq!(result.minute(), now.minute());
|
||||
assert_eq!(result.second(), now.second());
|
||||
|
||||
let result = at_date(parse(&mut "2025-01-01 2 days ago").unwrap(), now).unwrap();
|
||||
let result = at_date(parse(&mut "2025-01-01 2 days ago").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result.hour(), 0);
|
||||
assert_eq!(result.minute(), 0);
|
||||
assert_eq!(result.second(), 0);
|
||||
|
||||
let result = at_date(parse(&mut "3 weeks").unwrap(), now).unwrap();
|
||||
assert_eq!(result, now + chrono::Duration::days(21));
|
||||
let result = at_date(parse(&mut "3 weeks").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result, now.checked_add(21.days()).unwrap());
|
||||
assert_eq!(result.hour(), now.hour());
|
||||
assert_eq!(result.minute(), now.minute());
|
||||
assert_eq!(result.second(), now.second());
|
||||
@@ -539,26 +534,26 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pure() {
|
||||
let now = Utc::now().fixed_offset();
|
||||
let now = Zoned::now().with_time_zone(TimeZone::UTC);
|
||||
|
||||
// Pure number as year.
|
||||
let result = at_date(parse(&mut "jul 18 12:30 2025").unwrap(), now).unwrap();
|
||||
let result = at_date(parse(&mut "jul 18 12:30 2025").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result.year(), 2025);
|
||||
|
||||
// Pure number as time.
|
||||
let result = at_date(parse(&mut "1230").unwrap(), now).unwrap();
|
||||
let result = at_date(parse(&mut "1230").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result.hour(), 12);
|
||||
assert_eq!(result.minute(), 30);
|
||||
|
||||
let result = at_date(parse(&mut "123").unwrap(), now).unwrap();
|
||||
let result = at_date(parse(&mut "123").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result.hour(), 1);
|
||||
assert_eq!(result.minute(), 23);
|
||||
|
||||
let result = at_date(parse(&mut "12").unwrap(), now).unwrap();
|
||||
let result = at_date(parse(&mut "12").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result.hour(), 12);
|
||||
assert_eq!(result.minute(), 0);
|
||||
|
||||
let result = at_date(parse(&mut "1").unwrap(), now).unwrap();
|
||||
let result = at_date(parse(&mut "1").unwrap(), now.clone()).unwrap();
|
||||
assert_eq!(result.hour(), 1);
|
||||
assert_eq!(result.minute(), 0);
|
||||
}
|
||||
|
||||
+15
-10
@@ -37,6 +37,8 @@ use winnow::{
|
||||
ModalResult, Parser,
|
||||
};
|
||||
|
||||
use crate::ParseDateTimeError;
|
||||
|
||||
use super::{epoch::sec_and_nsec, ordinal::ordinal, primitive::s};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -49,18 +51,21 @@ pub(crate) enum Relative {
|
||||
Seconds(i64, u32),
|
||||
}
|
||||
|
||||
impl From<Relative> for jiff::Span {
|
||||
fn from(relative: Relative) -> Self {
|
||||
impl TryFrom<Relative> for jiff::Span {
|
||||
type Error = ParseDateTimeError;
|
||||
|
||||
fn try_from(relative: Relative) -> Result<Self, Self::Error> {
|
||||
match relative {
|
||||
Relative::Years(years) => jiff::Span::new().years(years),
|
||||
Relative::Months(months) => jiff::Span::new().months(months),
|
||||
Relative::Days(days) => jiff::Span::new().days(days),
|
||||
Relative::Hours(hours) => jiff::Span::new().hours(hours),
|
||||
Relative::Minutes(minutes) => jiff::Span::new().minutes(minutes),
|
||||
Relative::Seconds(seconds, nanoseconds) => {
|
||||
jiff::Span::new().seconds(seconds).nanoseconds(nanoseconds)
|
||||
}
|
||||
Relative::Years(years) => jiff::Span::new().try_years(years),
|
||||
Relative::Months(months) => jiff::Span::new().try_months(months),
|
||||
Relative::Days(days) => jiff::Span::new().try_days(days),
|
||||
Relative::Hours(hours) => jiff::Span::new().try_hours(hours),
|
||||
Relative::Minutes(minutes) => jiff::Span::new().try_minutes(minutes),
|
||||
Relative::Seconds(seconds, nanoseconds) => jiff::Span::new()
|
||||
.try_seconds(seconds)
|
||||
.and_then(|span| span.try_nanoseconds(nanoseconds)),
|
||||
}
|
||||
.map_err(|_| ParseDateTimeError::InvalidInput)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-29
@@ -27,11 +27,10 @@
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use chrono::FixedOffset;
|
||||
use winnow::{
|
||||
combinator::{alt, peek, seq},
|
||||
error::{ContextError, ErrMode},
|
||||
stream::AsChar,
|
||||
stream::{AsChar, Stream},
|
||||
token::take_while,
|
||||
ModalResult, Parser,
|
||||
};
|
||||
@@ -78,7 +77,7 @@ impl Offset {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Offset> for chrono::FixedOffset {
|
||||
impl TryFrom<&Offset> for jiff::tz::TimeZone {
|
||||
type Error = ParseDateTimeError;
|
||||
|
||||
fn try_from(
|
||||
@@ -86,37 +85,16 @@ impl TryFrom<Offset> for chrono::FixedOffset {
|
||||
negative,
|
||||
hours,
|
||||
minutes,
|
||||
}: Offset,
|
||||
}: &Offset,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let secs = (hours as i32) * 3600 + (minutes as i32) * 60;
|
||||
|
||||
let offset = if negative {
|
||||
FixedOffset::west_opt(secs).ok_or(ParseDateTimeError::InvalidInput)?
|
||||
} else {
|
||||
FixedOffset::east_opt(secs).ok_or(ParseDateTimeError::InvalidInput)?
|
||||
};
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Offset> for jiff::tz::Offset {
|
||||
type Error = ParseDateTimeError;
|
||||
|
||||
fn try_from(
|
||||
Offset {
|
||||
negative,
|
||||
hours,
|
||||
minutes,
|
||||
}: Offset,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let secs = (hours as i32) * 3600 + (minutes as i32) * 60;
|
||||
let secs = if negative { -secs } else { secs };
|
||||
let secs = (*hours as i32) * 3600 + (*minutes as i32) * 60;
|
||||
let secs = if *negative { -secs } else { secs };
|
||||
|
||||
let offset =
|
||||
jiff::tz::Offset::from_seconds(secs).map_err(|_| ParseDateTimeError::InvalidInput)?;
|
||||
let tz = jiff::tz::TimeZone::fixed(offset);
|
||||
|
||||
Ok(offset)
|
||||
Ok(tz)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,11 +195,13 @@ fn timezone_name_offset(input: &mut &str) -> ModalResult<Offset> {
|
||||
//
|
||||
// Only process if the input cannot be parsed as a relative time.
|
||||
if peek(relative::parse).parse_next(input).is_err() {
|
||||
let start = input.checkpoint();
|
||||
if let Ok(other_tz) = timezone_num.parse_next(input) {
|
||||
let newtz = tz.merge(other_tz);
|
||||
|
||||
return Ok(newtz);
|
||||
};
|
||||
input.reset(&start);
|
||||
}
|
||||
|
||||
Ok(tz)
|
||||
|
||||
@@ -46,20 +46,6 @@ pub(crate) struct Weekday {
|
||||
pub(crate) day: Day,
|
||||
}
|
||||
|
||||
impl From<Day> for chrono::Weekday {
|
||||
fn from(value: Day) -> Self {
|
||||
match value {
|
||||
Day::Monday => chrono::Weekday::Mon,
|
||||
Day::Tuesday => chrono::Weekday::Tue,
|
||||
Day::Wednesday => chrono::Weekday::Wed,
|
||||
Day::Thursday => chrono::Weekday::Thu,
|
||||
Day::Friday => chrono::Weekday::Fri,
|
||||
Day::Saturday => chrono::Weekday::Sat,
|
||||
Day::Sunday => chrono::Weekday::Sun,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Day> for jiff::civil::Weekday {
|
||||
fn from(value: Day) -> Self {
|
||||
match value {
|
||||
|
||||
+178
-193
File diff suppressed because it is too large
Load Diff
+10
-6
@@ -3,7 +3,7 @@
|
||||
|
||||
use std::env;
|
||||
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use jiff::Zoned;
|
||||
use parse_datetime::{parse_datetime, parse_datetime_at_date};
|
||||
|
||||
pub fn check_absolute(input: &str, expected: &str) {
|
||||
@@ -15,19 +15,23 @@ pub fn check_absolute(input: &str, expected: &str) {
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
&parsed.to_rfc3339().replace("T", " "),
|
||||
parsed.strftime("%Y-%m-%d %H:%M:%S%:z").to_string(),
|
||||
expected,
|
||||
"Input value: {input}"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn check_relative(now: DateTime<FixedOffset>, input: &str, expected: &str) {
|
||||
pub fn check_relative(now: Zoned, input: &str, expected: &str) {
|
||||
env::set_var("TZ", "UTC0");
|
||||
|
||||
let parsed = match parse_datetime_at_date(now.into(), input) {
|
||||
let parsed = match parse_datetime_at_date(now, input) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("Failed to parse date from value '{input}': {e}"),
|
||||
};
|
||||
let expected_parsed = DateTime::parse_from_rfc3339(expected).unwrap();
|
||||
assert_eq!(parsed, expected_parsed, "Input value: {input}");
|
||||
|
||||
assert_eq!(
|
||||
parsed.strftime("%Y-%m-%d %H:%M:%S%:z").to_string(),
|
||||
expected,
|
||||
"Input value: {input}"
|
||||
);
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,10 +1,11 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use jiff::{civil::DateTime, tz::TimeZone};
|
||||
use rstest::rstest;
|
||||
|
||||
mod common;
|
||||
use common::check_absolute;
|
||||
use common::{check_absolute, check_relative};
|
||||
|
||||
// The expected values are produced by GNU date version 8.32
|
||||
// export LC_TIME=en_US.UTF-8
|
||||
@@ -87,9 +88,10 @@ fn test_absolute_date_numeric(#[case] input: &str, #[case] expected: &str) {
|
||||
#[case::alphabetical_long_month_at_back_hyphen("14-november", 2022, "2022-11-14 00:00:00+00:00")]
|
||||
#[case::alphabetical_short_month_at_back_hyphen("14-nov", 2022, "2022-11-14 00:00:00+00:00")]
|
||||
fn test_date_omitting_year(#[case] input: &str, #[case] year: u32, #[case] expected: &str) {
|
||||
use chrono::DateTime;
|
||||
use common::check_relative;
|
||||
|
||||
let now = DateTime::parse_from_rfc3339(&format!("{year}-06-01T00:00:00+00:00")).unwrap();
|
||||
let now = format!("{year}-06-01 00:00:00")
|
||||
.parse::<DateTime>()
|
||||
.unwrap()
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap();
|
||||
check_relative(now, input, expected);
|
||||
}
|
||||
|
||||
+11
-7
@@ -1,7 +1,7 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use jiff::{civil::DateTime, tz::TimeZone, Zoned};
|
||||
use parse_datetime::parse_datetime_at_date;
|
||||
use rstest::rstest;
|
||||
|
||||
@@ -13,17 +13,17 @@ use rstest::rstest;
|
||||
// Documentation for the date format can be found at:
|
||||
// https://www.gnu.org/software/coreutils/manual/html_node/Time-of-day-items.html
|
||||
|
||||
pub fn check_time(input: &str, expected: &str, format: &str, base: Option<DateTime<Local>>) {
|
||||
pub fn check_time(input: &str, expected: &str, format: &str, base: Option<Zoned>) {
|
||||
std::env::set_var("TZ", "UTC0");
|
||||
let now = base.unwrap_or_else(|| std::time::SystemTime::now().into());
|
||||
let now = base.unwrap_or(Zoned::now());
|
||||
let parsed = match parse_datetime_at_date(now, input) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("Failed to parse time from value '{input}': {e}"),
|
||||
}
|
||||
.to_utc();
|
||||
.with_time_zone(TimeZone::UTC);
|
||||
|
||||
assert_eq!(
|
||||
&format!("{}", parsed.format(format)),
|
||||
format!("{}", parsed.strftime(format)),
|
||||
expected,
|
||||
"Input value: {input}"
|
||||
);
|
||||
@@ -97,8 +97,12 @@ fn test_time_correction(#[case] input: &str, #[case] expected: &str) {
|
||||
#[case::minus_13("12:34:56-13:00", "2022-06-11 01:34:56")]
|
||||
*/
|
||||
fn test_time_correction_with_overflow(#[case] input: &str, #[case] expected: &str) {
|
||||
let now = DateTime::parse_from_rfc3339("2022-06-10T00:00:00+00:00").unwrap();
|
||||
check_time(input, expected, "%Y-%m-%d %H:%M:%S", Some(now.into()));
|
||||
let now = "2022-06-10 00:00:00"
|
||||
.parse::<DateTime>()
|
||||
.unwrap()
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap();
|
||||
check_time(input, expected, "%Y-%m-%d %H:%M:%S", Some(now));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
||||
Reference in New Issue
Block a user