You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bed2d16e67 | |||
| 7ab1ad2f0e | |||
| c1a2bef4a6 | |||
| 673d5b600e | |||
| 83e1431db0 | |||
| 80f8fdf883 | |||
| 22bdd8f49c | |||
| dc508c469b | |||
| e503de7559 | |||
| 7ee33d1fb1 | |||
| bc33770075 | |||
| 70d3e53604 | |||
| 1ba787d1c3 | |||
| 33cefd7db2 | |||
| d0d913e161 | |||
| c784a721ee | |||
| 29e7a5c335 | |||
| 8d797fcbcb | |||
| c177117428 | |||
| 03c8e81a10 | |||
| 7547f31b76 | |||
| 1c32733f38 | |||
| 5278394b7c | |||
| e53aabd914 | |||
| e5d7fbcece | |||
| 060998e90e | |||
| a272f55948 | |||
| 9c5da7959c | |||
| a60f1d89f5 | |||
| 1d78cc5a3f | |||
| 6ba55a075b | |||
| b439960eb1 | |||
| f1a15ab2b3 | |||
| 0bd4dd716a | |||
| 674909c0f9 | |||
| 57bcf7f433 | |||
| 2e80358014 | |||
| 15fb185d95 | |||
| e64c163f47 | |||
| 86378bc326 | |||
| 6a52551dea | |||
| 21e7e01fc8 | |||
| 1359dc9349 | |||
| daec32d5c4 | |||
| 7a3d0bafca | |||
| 537df73bdd | |||
| 3e4c6e3b6c |
@@ -164,3 +164,27 @@ jobs:
|
||||
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
||||
name: codecov-umbrella
|
||||
fail_ci_if_error: false
|
||||
|
||||
fuzz:
|
||||
name: Run the fuzzers
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUN_FOR: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- name: Install `cargo-fuzz`
|
||||
run: cargo install cargo-fuzz
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run from_str for XX seconds
|
||||
shell: bash
|
||||
run: |
|
||||
## Run it
|
||||
cd fuzz
|
||||
cargo +nightly fuzz run fuzz_from_str -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||
- name: Run fuzz_parse_datetime_from_str for XX seconds
|
||||
shell: bash
|
||||
run: |
|
||||
## Run it
|
||||
cd fuzz
|
||||
cargo +nightly fuzz run fuzz_parse_datetime_from_str -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
target/
|
||||
Generated
+289
-18
@@ -12,19 +12,153 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime_to_duration"
|
||||
version = "0.1.4"
|
||||
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 = [
|
||||
"regex",
|
||||
"time",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[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.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[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 = "js-sys"
|
||||
version = "0.3.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
@@ -43,23 +177,160 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"time-core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
name = "unicode-ident"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "humantime_to_duration"
|
||||
name = "parse_datetime"
|
||||
description = " parsing human-readable relative time strings and converting them to a Duration"
|
||||
version = "0.2.0"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/uutils/humantime_to_duration"
|
||||
repository = "https://github.com/uutils/parse_datetime"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
regex = "1.7"
|
||||
time = "0.3.20"
|
||||
regex = "1.8"
|
||||
chrono = { version="0.4", default-features=false, features=["std", "alloc", "clock"] }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 uutils
|
||||
Copyright (c) 2023 Sylvestre Ledru and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
# humantime_to_duration
|
||||
# parse_datetime
|
||||
|
||||
[](https://crates.io/crates/humantime_to_duration)
|
||||
[](https://github.com/uutils/humantime_to_duration/blob/main/LICENSE)
|
||||
[](https://codecov.io/gh/uutils/humantime_to_duration)
|
||||
[](https://crates.io/crates/parse_datetime)
|
||||
[](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 converting them to a `Duration`.
|
||||
A Rust crate for parsing human-readable relative time strings and converting them to a `Duration`, or parsing human-readable datetime strings and converting them to a `DateTime`.
|
||||
|
||||
## Features
|
||||
|
||||
- Parses a variety of human-readable time formats.
|
||||
- Parses a variety of human-readable and standard time formats.
|
||||
- Supports positive and negative durations.
|
||||
- Allows for chaining time units (e.g., "1 hour 2 minutes" or "2 days and 2 hours").
|
||||
- Calculate durations relative to a specified date.
|
||||
- Relies on Chrono
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -19,18 +20,18 @@ Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
humantime_to_duration = "0.1.1"
|
||||
parse_datetime = "0.4.0"
|
||||
```
|
||||
|
||||
Then, import the crate and use the `from_str` and `from_str_at_date` functions:
|
||||
```
|
||||
use humantime_to_duration::{from_str, from_str_at_date};
|
||||
use time::Duration;
|
||||
```rs
|
||||
use parse_datetime::{from_str, from_str_at_date};
|
||||
use chrono::Duration;
|
||||
|
||||
let duration = from_str("+3 days");
|
||||
assert_eq!(duration.unwrap(), Duration::days(3));
|
||||
|
||||
let today = OffsetDateTime::now_utc().date();
|
||||
let today = Utc::today().naive_utc();
|
||||
let yesterday = today - Duration::days(1);
|
||||
assert_eq!(
|
||||
from_str_at_date(yesterday, "2 days").unwrap(),
|
||||
@@ -38,6 +39,15 @@ assert_eq!(
|
||||
);
|
||||
```
|
||||
|
||||
For DateTime parsing, import the `parse_datetime` module:
|
||||
```rs
|
||||
use parse_datetime::parse_datetime::from_str;
|
||||
use chrono::{Local, TimeZone};
|
||||
|
||||
let dt = from_str("2021-02-14 06:37:47");
|
||||
assert_eq!(dt.unwrap(), Local.with_ymd_and_hms(2021, 2, 14, 6, 37, 47).unwrap());
|
||||
```
|
||||
|
||||
### Supported Formats
|
||||
|
||||
The `from_str` and `from_str_at_date` functions support the following formats for relative time:
|
||||
@@ -55,6 +65,8 @@ The `from_str` and `from_str_at_date` functions support the following formats fo
|
||||
|
||||
## Return Values
|
||||
|
||||
### Duration
|
||||
|
||||
The `from_str` and `from_str_at_date` functions return:
|
||||
|
||||
- `Ok(Duration)` - If the input string can be parsed as a relative time
|
||||
@@ -63,6 +75,13 @@ The `from_str` and `from_str_at_date` functions return:
|
||||
This function will return `Err(ParseDurationError::InvalidInput)` if the input string
|
||||
cannot be parsed as a relative time.
|
||||
|
||||
### parse_datetime
|
||||
|
||||
The `from_str` function returns:
|
||||
|
||||
- `Ok(DateTime<FixedOffset>)` - If the input string can be prsed as a datetime
|
||||
- `Err(ParseDurationError::InvalidInput)` - If the input string cannot be parsed
|
||||
|
||||
## Fuzzer
|
||||
|
||||
To run the fuzzer:
|
||||
@@ -73,3 +92,8 @@ $ cargo fuzz run fuzz_from_str
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT License](LICENSE).
|
||||
|
||||
## Note
|
||||
|
||||
At some point, this crate was called humantime_to_duration.
|
||||
It has been renamed to cover more cases.
|
||||
|
||||
Generated
+292
-25
@@ -11,12 +11,39 @@ 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 = "arbitrary"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
@@ -32,15 +59,36 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "fuzz_from_str"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"humantime_to_duration",
|
||||
"chrono",
|
||||
"libfuzzer-sys",
|
||||
"parse_datetime",
|
||||
"rand",
|
||||
"regex",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -51,15 +99,30 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime_to_duration"
|
||||
version = "0.1.0"
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"time",
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
@@ -71,6 +134,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
@@ -88,24 +160,65 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -138,9 +251,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
version = "1.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -149,34 +262,188 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"time-core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
+10
-4
@@ -7,12 +7,12 @@ edition = "2018"
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.4"
|
||||
rand = "0.8.5"
|
||||
libfuzzer-sys = "0.4"
|
||||
regex = "1.8.1"
|
||||
time = "0.3.20"
|
||||
regex = "1.8.4"
|
||||
chrono = "0.4"
|
||||
|
||||
[dependencies.humantime_to_duration]
|
||||
[dependencies.parse_datetime]
|
||||
path = "../"
|
||||
|
||||
[[bin]]
|
||||
@@ -20,3 +20,9 @@ name = "fuzz_from_str"
|
||||
path = "fuzz_targets/from_str.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_parse_datetime_from_str"
|
||||
path = "fuzz_targets/parse_datetime_from_str.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
@@ -4,5 +4,5 @@ use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let s = std::str::from_utf8(data).unwrap_or("");
|
||||
let _ = humantime_to_duration::from_str(s);
|
||||
let _ = parse_datetime::from_str(s);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let s = std::str::from_utf8(data).unwrap_or("");
|
||||
let _ = parse_datetime::parse_datetime::from_str(s);
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
+58
-56
@@ -1,12 +1,15 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// Expose parse_datetime
|
||||
pub mod parse_datetime;
|
||||
|
||||
use chrono::{Duration, Local, NaiveDate, Utc};
|
||||
use regex::{Error as RegexError, Regex};
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display};
|
||||
use time::{Date, Duration, OffsetDateTime};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ParseDurationError {
|
||||
InvalidRegex(RegexError),
|
||||
InvalidInput,
|
||||
@@ -46,8 +49,8 @@ impl From<RegexError> for ParseDurationError {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use time::Duration;
|
||||
/// let duration = humantime_to_duration::from_str("+3 days");
|
||||
/// use chrono::Duration;
|
||||
/// let duration = parse_datetime::from_str("+3 days");
|
||||
/// assert_eq!(duration.unwrap(), Duration::days(3));
|
||||
/// ```
|
||||
///
|
||||
@@ -62,7 +65,7 @@ impl From<RegexError> for ParseDurationError {
|
||||
/// * "tomorrow"
|
||||
/// * use "ago" for the past
|
||||
///
|
||||
/// [num] can be a positive or negative integer.
|
||||
/// `[num]` can be a positive or negative integer.
|
||||
/// [unit] can be one of the following: "fortnight", "week", "day", "hour",
|
||||
/// "minute", "min", "second", "sec" and their plural forms.
|
||||
///
|
||||
@@ -81,14 +84,43 @@ impl From<RegexError> for ParseDurationError {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use time::Duration;
|
||||
/// use humantime_to_duration::{from_str, ParseDurationError};
|
||||
/// use chrono::Duration;
|
||||
/// use parse_datetime::{from_str, ParseDurationError};
|
||||
///
|
||||
/// assert_eq!(from_str("1 hour, 30 minutes").unwrap(), Duration::minutes(90));
|
||||
/// assert_eq!(from_str("tomorrow").unwrap(), Duration::days(1));
|
||||
/// assert!(matches!(from_str("invalid"), Err(ParseDurationError::InvalidInput)));
|
||||
/// ```
|
||||
pub fn from_str(s: &str) -> Result<Duration, ParseDurationError> {
|
||||
from_str_at_date(Utc::now().date_naive(), s)
|
||||
}
|
||||
|
||||
/// Parses a duration string and returns a `Duration` instance, with the duration
|
||||
/// calculated from the specified date.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `date` - A `Date` instance representing the base date for the calculation
|
||||
/// * `s` - A string slice representing the relative time.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return `Err(ParseDurationError::InvalidInput)` if the input string
|
||||
/// cannot be parsed as a relative time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{Duration, NaiveDate, Utc, Local};
|
||||
/// use parse_datetime::{from_str_at_date, ParseDurationError};
|
||||
/// let today = Local::now().date().naive_local();
|
||||
/// let yesterday = today - Duration::days(1);
|
||||
/// assert_eq!(
|
||||
/// from_str_at_date(yesterday, "2 days").unwrap(),
|
||||
/// Duration::days(1) // 1 day from the specified date + 1 day from the input string
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_str_at_date(date: NaiveDate, s: &str) -> Result<Duration, ParseDurationError> {
|
||||
let time_pattern: Regex = Regex::new(
|
||||
r"(?x)
|
||||
(?:(?P<value>[-+]?\d*)\s*)?
|
||||
@@ -97,7 +129,7 @@ pub fn from_str(s: &str) -> Result<Duration, ParseDurationError> {
|
||||
(\s*(?P<ago>ago)?)?",
|
||||
)?;
|
||||
|
||||
let mut total_duration = Duration::ZERO;
|
||||
let mut total_duration = Duration::seconds(0);
|
||||
let mut is_ago = s.contains(" ago");
|
||||
let mut captures_processed = 0;
|
||||
let mut total_length = 0;
|
||||
@@ -136,15 +168,15 @@ pub fn from_str(s: &str) -> Result<Duration, ParseDurationError> {
|
||||
"seconds" | "second" | "secs" | "sec" | "s" => Duration::seconds(value),
|
||||
"yesterday" => Duration::days(-1),
|
||||
"tomorrow" => Duration::days(1),
|
||||
"now" | "today" => Duration::ZERO,
|
||||
"now" | "today" => Duration::zero(),
|
||||
_ => return Err(ParseDurationError::InvalidInput),
|
||||
};
|
||||
|
||||
total_duration = match total_duration.checked_add(if is_ago { -duration } else { duration })
|
||||
{
|
||||
Some(duration) => duration,
|
||||
None => return Err(ParseDurationError::InvalidInput),
|
||||
};
|
||||
let neg_duration = -duration;
|
||||
total_duration =
|
||||
match total_duration.checked_add(if is_ago { &neg_duration } else { &duration }) {
|
||||
Some(duration) => duration,
|
||||
None => return Err(ParseDurationError::InvalidInput),
|
||||
};
|
||||
|
||||
// Calculate the total length of the matched substring
|
||||
if let Some(m) = capture.get(0) {
|
||||
@@ -160,43 +192,19 @@ pub fn from_str(s: &str) -> Result<Duration, ParseDurationError> {
|
||||
if captures_processed == 0 {
|
||||
Err(ParseDurationError::InvalidInput)
|
||||
} else {
|
||||
Ok(total_duration)
|
||||
let time_now = Local::now().date_naive();
|
||||
let date_duration = date - time_now;
|
||||
|
||||
Ok(total_duration + date_duration)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a duration string and returns a `Duration` instance, with the duration
|
||||
/// calculated from the specified date.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `date` - A `Date` instance representing the base date for the calculation
|
||||
/// * `s` - A string slice representing the relative time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use time::{Date, Duration, OffsetDateTime};
|
||||
/// use humantime_to_duration::{from_str_at_date, ParseDurationError};
|
||||
/// let today = OffsetDateTime::now_utc().date();
|
||||
/// let yesterday = today - Duration::days(1);
|
||||
/// assert_eq!(
|
||||
/// from_str_at_date(yesterday, "2 days").unwrap(),
|
||||
/// Duration::days(1) // 1 day from the specified date + 1 day from the input string
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_str_at_date(date: Date, s: &str) -> Result<Duration, ParseDurationError> {
|
||||
let time_now = OffsetDateTime::now_utc().date();
|
||||
let date_duration = date - time_now;
|
||||
|
||||
Ok(from_str(s)? + date_duration)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::ParseDurationError;
|
||||
use super::{from_str, from_str_at_date};
|
||||
use time::{Date, Duration, Month, OffsetDateTime};
|
||||
use chrono::{Duration, Local, NaiveDate};
|
||||
|
||||
#[test]
|
||||
fn test_years() {
|
||||
@@ -321,16 +329,10 @@ mod tests {
|
||||
fn test_invalid_input() {
|
||||
let result = from_str("foobar");
|
||||
println!("{result:?}");
|
||||
match result {
|
||||
Err(ParseDurationError::InvalidInput) => assert!(true),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert_eq!(result, Err(ParseDurationError::InvalidInput));
|
||||
|
||||
let result = from_str("invalid 1");
|
||||
match result {
|
||||
Err(ParseDurationError::InvalidInput) => assert!(true),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert_eq!(result, Err(ParseDurationError::InvalidInput));
|
||||
// Fails for now with a panic
|
||||
/* let result = from_str("777777777777777771m");
|
||||
match result {
|
||||
@@ -341,9 +343,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_from_str_at_date() {
|
||||
let date = Date::from_calendar_date(2014, Month::September, 5).unwrap();
|
||||
let now = OffsetDateTime::now_utc().date();
|
||||
let days_diff = (date - now).whole_days();
|
||||
let date = NaiveDate::from_ymd_opt(2014, 9, 5).unwrap();
|
||||
let now = Local::now().date_naive();
|
||||
let days_diff = (date - now).num_days();
|
||||
|
||||
assert_eq!(
|
||||
from_str_at_date(date, "1 day").unwrap(),
|
||||
@@ -358,7 +360,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_input_at_date() {
|
||||
let date = Date::from_calendar_date(2014, Month::September, 5).unwrap();
|
||||
let date = NaiveDate::from_ymd_opt(2014, 9, 5).unwrap();
|
||||
assert!(matches!(
|
||||
from_str_at_date(date, "invalid"),
|
||||
Err(ParseDurationError::InvalidInput)
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{DateTime, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone};
|
||||
|
||||
use crate::ParseDurationError;
|
||||
|
||||
/// Formats that parse input can take.
|
||||
/// Taken from `touch` coreutils
|
||||
mod format {
|
||||
pub(crate) const ISO_8601: &str = "%Y-%m-%d";
|
||||
pub(crate) const ISO_8601_NO_SEP: &str = "%Y%m%d";
|
||||
pub(crate) const POSIX_LOCALE: &str = "%a %b %e %H:%M:%S %Y";
|
||||
pub(crate) const YYYYMMDDHHMM_DOT_SS: &str = "%Y%m%d%H%M.%S";
|
||||
pub(crate) const YYYYMMDDHHMMSS: &str = "%Y-%m-%d %H:%M:%S.%f";
|
||||
pub(crate) const YYYYMMDDHHMMS: &str = "%Y-%m-%d %H:%M:%S";
|
||||
pub(crate) const YYYY_MM_DD_HH_MM: &str = "%Y-%m-%d %H:%M";
|
||||
pub(crate) const YYYYMMDDHHMM: &str = "%Y%m%d%H%M";
|
||||
pub(crate) const YYYYMMDDHHMM_OFFSET: &str = "%Y%m%d%H%M %z";
|
||||
pub(crate) const YYYYMMDDHHMM_UTC_OFFSET: &str = "%Y%m%d%H%MUTC%z";
|
||||
pub(crate) const YYYYMMDDHHMM_ZULU_OFFSET: &str = "%Y%m%d%H%MZ%z";
|
||||
pub(crate) const YYYYMMDDHHMM_HYPHENATED_OFFSET: &str = "%Y-%m-%d %H:%M %z";
|
||||
pub(crate) const YYYYMMDDHHMMS_T_SEP: &str = "%Y-%m-%dT%H:%M:%S";
|
||||
pub(crate) const UTC_OFFSET: &str = "UTC%#z";
|
||||
pub(crate) const ZULU_OFFSET: &str = "Z%#z";
|
||||
}
|
||||
|
||||
/// Loosely parses a time string and returns a `DateTime` representing the
|
||||
/// absolute time of the string.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `s` - A string slice representing the time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{DateTime, Utc, TimeZone};
|
||||
/// let time = parse_datetime::parse_datetime::from_str("2023-06-03 12:00:01Z");
|
||||
/// assert_eq!(time.unwrap(), Utc.with_ymd_and_hms(2023, 06, 03, 12, 00, 01).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// # Supported formats
|
||||
///
|
||||
/// The function supports the following formats for time:
|
||||
///
|
||||
/// * ISO formats
|
||||
/// * timezone offsets, e.g., "UTC-0100"
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(DateTime<FixedOffset>)` - If the input string can be parsed as a time
|
||||
/// * `Err(ParseDurationError)` - If the input string cannot be parsed as a relative time
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return `Err(ParseDurationError::InvalidInput)` if the input string
|
||||
/// cannot be parsed as a relative time.
|
||||
///
|
||||
pub fn from_str<S: AsRef<str> + Clone>(s: S) -> Result<DateTime<FixedOffset>, ParseDurationError> {
|
||||
// TODO: Replace with a proper customiseable parsing solution using `nom`, `grmtools`, or
|
||||
// similar
|
||||
|
||||
// Formats with offsets don't require NaiveDateTime workaround
|
||||
for fmt in [
|
||||
format::YYYYMMDDHHMM_OFFSET,
|
||||
format::YYYYMMDDHHMM_HYPHENATED_OFFSET,
|
||||
format::YYYYMMDDHHMM_UTC_OFFSET,
|
||||
format::YYYYMMDDHHMM_ZULU_OFFSET,
|
||||
] {
|
||||
if let Ok(parsed) = DateTime::parse_from_str(s.as_ref(), fmt) {
|
||||
return Ok(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse formats with no offset, assume local time
|
||||
for fmt in [
|
||||
format::YYYYMMDDHHMMS_T_SEP,
|
||||
format::YYYYMMDDHHMM,
|
||||
format::YYYYMMDDHHMMS,
|
||||
format::YYYYMMDDHHMMSS,
|
||||
format::YYYY_MM_DD_HH_MM,
|
||||
format::YYYYMMDDHHMM_DOT_SS,
|
||||
format::POSIX_LOCALE,
|
||||
] {
|
||||
if let Ok(parsed) = NaiveDateTime::parse_from_str(s.as_ref(), fmt) {
|
||||
if let Ok(dt) = naive_dt_to_fixed_offset(parsed) {
|
||||
return Ok(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse epoch seconds
|
||||
if s.as_ref().bytes().next() == Some(b'@') {
|
||||
if let Ok(parsed) = NaiveDateTime::parse_from_str(&s.as_ref()[1..], "%s") {
|
||||
if let Ok(dt) = naive_dt_to_fixed_offset(parsed) {
|
||||
return Ok(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ts = s.as_ref().to_owned() + "0000";
|
||||
// Parse date only formats - assume midnight local timezone
|
||||
for fmt in [format::ISO_8601, format::ISO_8601_NO_SEP] {
|
||||
let f = fmt.to_owned() + "%H%M";
|
||||
if let Ok(parsed) = NaiveDateTime::parse_from_str(&ts, &f) {
|
||||
if let Ok(dt) = naive_dt_to_fixed_offset(parsed) {
|
||||
return Ok(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse offsets. chrono doesn't provide any functionality to parse
|
||||
// offsets, so instead we replicate parse_date behaviour by getting
|
||||
// the current date with local, and create a date time string at midnight,
|
||||
// before trying offset suffixes
|
||||
let local = Local::now();
|
||||
let ts = format!("{}", local.format("%Y%m%d")) + "0000" + s.as_ref();
|
||||
for fmt in [format::UTC_OFFSET, format::ZULU_OFFSET] {
|
||||
let f = format::YYYYMMDDHHMM.to_owned() + fmt;
|
||||
if let Ok(parsed) = DateTime::parse_from_str(&ts, &f) {
|
||||
return Ok(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
// Default parse and failure
|
||||
s.as_ref()
|
||||
.parse()
|
||||
.map_err(|_| (ParseDurationError::InvalidInput))
|
||||
}
|
||||
|
||||
// Convert NaiveDateTime to DateTime<FixedOffset> by assuming the offset
|
||||
// is local time
|
||||
fn naive_dt_to_fixed_offset(dt: NaiveDateTime) -> Result<DateTime<FixedOffset>, ()> {
|
||||
let now = Local::now();
|
||||
match now.offset().from_local_datetime(&dt) {
|
||||
LocalResult::Single(dt) => Ok(dt),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
static TEST_TIME: i64 = 1613371067;
|
||||
|
||||
#[cfg(test)]
|
||||
mod iso_8601 {
|
||||
use std::env;
|
||||
|
||||
use crate::{
|
||||
parse_datetime::from_str, parse_datetime::tests::TEST_TIME, ParseDurationError,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_t_sep() {
|
||||
env::set_var("TZ", "UTC");
|
||||
let dt = "2021-02-15T06:37:47";
|
||||
let actual = from_str(dt);
|
||||
assert_eq!(actual.unwrap().timestamp(), TEST_TIME);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_space_sep() {
|
||||
env::set_var("TZ", "UTC");
|
||||
let dt = "2021-02-15 06:37:47";
|
||||
let actual = from_str(dt);
|
||||
assert_eq!(actual.unwrap().timestamp(), TEST_TIME);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_space_sep_offset() {
|
||||
env::set_var("TZ", "UTC");
|
||||
let dt = "2021-02-14 22:37:47 -0800";
|
||||
let actual = from_str(dt);
|
||||
assert_eq!(actual.unwrap().timestamp(), TEST_TIME);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_t_sep_offset() {
|
||||
env::set_var("TZ", "UTC");
|
||||
let dt = "2021-02-14T22:37:47 -0800";
|
||||
let actual = from_str(dt);
|
||||
assert_eq!(actual.unwrap().timestamp(), TEST_TIME);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_formats() {
|
||||
let invalid_dts = vec!["NotADate", "202104", "202104-12T22:37:47"];
|
||||
for dt in invalid_dts {
|
||||
assert_eq!(from_str(dt), Err(ParseDurationError::InvalidInput));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod offsets {
|
||||
use chrono::Local;
|
||||
|
||||
use crate::{parse_datetime::from_str, ParseDurationError};
|
||||
|
||||
#[test]
|
||||
fn test_positive_offsets() {
|
||||
let offsets = vec![
|
||||
"UTC+07:00",
|
||||
"UTC+0700",
|
||||
"UTC+07",
|
||||
"Z+07:00",
|
||||
"Z+0700",
|
||||
"Z+07",
|
||||
];
|
||||
|
||||
let expected = format!("{}{}", Local::now().format("%Y%m%d"), "0000+0700");
|
||||
for offset in offsets {
|
||||
let actual = from_str(offset).unwrap();
|
||||
assert_eq!(expected, format!("{}", actual.format("%Y%m%d%H%M%z")));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_offset() {
|
||||
let offsets = vec!["UTC+00:15", "UTC+0015", "Z+00:15", "Z+0015"];
|
||||
let expected = format!("{}{}", Local::now().format("%Y%m%d"), "0000+0015");
|
||||
for offset in offsets {
|
||||
let actual = from_str(offset).unwrap();
|
||||
assert_eq!(expected, format!("{}", actual.format("%Y%m%d%H%M%z")));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_offset_format() {
|
||||
let invalid_offsets = vec!["+0700", "UTC+2", "Z-1", "UTC+01005"];
|
||||
for offset in invalid_offsets {
|
||||
assert_eq!(from_str(offset), Err(ParseDurationError::InvalidInput));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to test example code presented in the README.
|
||||
mod readme_test {
|
||||
use crate::parse_datetime::from_str;
|
||||
use chrono::{Local, TimeZone};
|
||||
|
||||
#[test]
|
||||
fn test_readme_code() {
|
||||
let dt = from_str("2021-02-14 06:37:47");
|
||||
assert_eq!(
|
||||
dt.unwrap(),
|
||||
Local.with_ymd_and_hms(2021, 2, 14, 6, 37, 47).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-28
@@ -1,20 +1,14 @@
|
||||
use humantime_to_duration::{from_str, from_str_at_date, ParseDurationError};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use chrono::{Duration, Utc};
|
||||
use parse_datetime::{from_str, from_str_at_date, ParseDurationError};
|
||||
|
||||
#[test]
|
||||
fn test_invalid_input() {
|
||||
let result = from_str("foobar");
|
||||
println!("{result:?}");
|
||||
match result {
|
||||
Err(ParseDurationError::InvalidInput) => assert!(true),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert_eq!(result, Err(ParseDurationError::InvalidInput));
|
||||
|
||||
let result = from_str("invalid 1");
|
||||
match result {
|
||||
Err(ParseDurationError::InvalidInput) => assert!(true),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert_eq!(result, Err(ParseDurationError::InvalidInput));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -101,8 +95,8 @@ fn test_duration_parsing() {
|
||||
assert_eq!(from_str("2 sec").unwrap(), Duration::seconds(2));
|
||||
assert_eq!(from_str("sec").unwrap(), Duration::seconds(1));
|
||||
|
||||
assert_eq!(from_str("now").unwrap(), Duration::ZERO);
|
||||
assert_eq!(from_str("today").unwrap(), Duration::ZERO);
|
||||
assert_eq!(from_str("now").unwrap(), Duration::seconds(0));
|
||||
assert_eq!(from_str("today").unwrap(), Duration::seconds(0));
|
||||
|
||||
assert_eq!(
|
||||
from_str("1 year 2 months 4 weeks 3 days and 2 seconds").unwrap(),
|
||||
@@ -115,14 +109,10 @@ fn test_duration_parsing() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_display_parse_duration_error_through_from_str() {
|
||||
let invalid_input = "9223372036854775807 seconds and 1 second";
|
||||
let error = from_str(invalid_input).unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
format!("{error}"),
|
||||
"Invalid input string: cannot be parsed as a relative time"
|
||||
);
|
||||
let _ = from_str(invalid_input).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -138,7 +128,7 @@ fn test_display_should_fail() {
|
||||
|
||||
#[test]
|
||||
fn test_from_str_at_date_day() {
|
||||
let today = OffsetDateTime::now_utc().date();
|
||||
let today = Utc::now().date_naive();
|
||||
let yesterday = today - Duration::days(1);
|
||||
assert_eq!(
|
||||
from_str_at_date(yesterday, "2 days").unwrap(),
|
||||
@@ -148,17 +138,11 @@ fn test_from_str_at_date_day() {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_input_at_date() {
|
||||
let today = OffsetDateTime::now_utc().date();
|
||||
let today = Utc::now().date_naive();
|
||||
let result = from_str_at_date(today, "foobar");
|
||||
println!("{result:?}");
|
||||
match result {
|
||||
Err(ParseDurationError::InvalidInput) => assert!(true),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert_eq!(result, Err(ParseDurationError::InvalidInput));
|
||||
|
||||
let result = from_str_at_date(today, "invalid 1r");
|
||||
match result {
|
||||
Err(ParseDurationError::InvalidInput) => assert!(true),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert_eq!(result, Err(ParseDurationError::InvalidInput));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user