91 Commits

Author SHA1 Message Date
Sylvestre Ledru a05bd1d06e Merge pull request #77 from cakebaker/release_0_6_0
Bump version to 0.6.0
2024-05-20 18:50:45 +02:00
Daniel Hofstetter a75107c69d Bump version to 0.6.0 2024-05-20 18:40:34 +02:00
Daniel Hofstetter 0f5b6bf16e Merge pull request #76 from RenjiSann/main
Fix single digit date parsing
2024-05-20 17:32:41 +02:00
Dorian Péron 1bb93d7fe9 Fix single digit date parsing 2024-05-20 17:24:33 +02:00
Sylvestre Ledru 138999131b Merge pull request #73 from cakebaker/ci_use_clippy_all_targets
ci: use `clippy --all-targets`
2024-05-03 15:52:55 +02:00
Daniel Hofstetter fc14854394 clippy: fix warnings from zero_prefixed_literal 2024-05-03 15:35:24 +02:00
Daniel Hofstetter be45c2734d ci: use clippy --all-targets 2024-05-03 15:33:11 +02:00
Daniel Hofstetter acf9b9c76e Merge pull request #72 from uutils/renovate/regex-1.x
fix(deps): update rust crate regex to 1.10.4
2024-05-01 13:47:54 +02:00
renovate[bot] 04494f6ddb fix(deps): update rust crate regex to 1.10.4 2024-05-01 11:32:23 +00:00
Daniel Hofstetter 20a39748f0 Merge pull request #71 from uutils/renovate/libfuzzer-sys-0.x
fix(deps): update rust crate libfuzzer-sys to 0.4.7
2024-05-01 13:31:17 +02:00
renovate[bot] 113100e669 fix(deps): update rust crate libfuzzer-sys to 0.4.7 2024-05-01 10:08:38 +00:00
Terts Diepraam ff2113eabe Merge pull request #67 from matrixhead/main
epoch timezone fix
2024-03-24 18:51:36 +01:00
Sylvestre Ledru ad9a056826 Merge pull request #64 from cakebaker/ci_use_codecov_token
ci: use codecov token
2024-03-24 17:09:56 +01:00
Sylvestre Ledru d1263e0e7e Merge pull request #69 from cakebaker/clippy_fix_warnings
clippy: fix warnings
2024-03-24 17:05:35 +01:00
Daniel Hofstetter 5b81981a8a clippy: fix warning from bool_assert_comparison 2024-03-24 16:54:43 +01:00
Daniel Hofstetter 85957480b3 clippy: fix warnings from zero_prefixed_literal 2024-03-24 16:52:29 +01:00
Daniel Hofstetter 0d04eeb08d Merge pull request #68 from uutils/renovate/regex-1.x
fix(deps): update rust crate regex to 1.10.4
2024-03-23 07:12:53 +01:00
renovate[bot] 0dddb4b726 fix(deps): update rust crate regex to 1.10.4 2024-03-23 05:16:50 +00:00
mhead deffff9803 epoch timezone fix 2024-03-04 21:23:25 +05:30
Terts Diepraam 96d8c7c294 Merge pull request #65 from matrixhead/main
support for parsing date string with only time
2024-03-03 23:10:44 +01:00
mhead 249c079790 parse time only strings 2024-03-03 22:54:01 +05:30
Daniel Hofstetter 261b28db22 ci: use codecov token 2024-02-06 09:58:43 +01:00
Terts Diepraam cdc1b64fcf Merge pull request #63 from drmorr0/drmorr/fix_readme
fix readme
2024-02-01 15:41:47 +01:00
Daniel Hofstetter 9e27be1e1a Merge pull request #62 from uutils/renovate/codecov-codecov-action-4.x
chore(deps): update codecov/codecov-action action to v4
2024-02-01 07:06:07 +01:00
David Morrison 9bb047eeab fix readme 2024-01-31 15:13:23 -08:00
renovate[bot] 040561c235 chore(deps): update codecov/codecov-action action to v4 2024-01-31 19:43:11 +00:00
Sylvestre Ledru 9009bfc4ed Merge pull request #60 from cakebaker/ci_replace_actions_rs_cargo
ci: remove actions-rs/cargo & use cargo directly
2024-01-22 09:42:42 +01:00
Daniel Hofstetter c39e01748c ci: remove actions-rs/cargo & use cargo directly 2024-01-22 09:26:32 +01:00
Sylvestre Ledru 325ae3a408 Merge pull request #59 from cakebaker/ci_use_dtolnay_rust_toolchain
ci: use dtolnay/rust-toolchain instead of action_rs/toolchain
2024-01-22 08:52:34 +01:00
Daniel Hofstetter 1ded0a062c ci: use dtolnay/rust-toolchain instead of
action_rs/toolchain to get rid of some CI warnings
2024-01-22 08:46:20 +01:00
Daniel Hofstetter 0047868a35 Merge pull request #58 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.10.3
2024-01-21 17:00:08 +01:00
renovate[bot] 5d11c142b5 Update Rust crate regex to 1.10.3 2024-01-21 15:53:29 +00:00
Sylvestre Ledru 8ef448acb5 Update of the version in the readme 2023-11-26 15:58:00 +01:00
Daniel Hofstetter 0ad9ef3e6b Merge pull request #54 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.10.2
2023-10-17 07:10:05 +02:00
renovate[bot] 4dfa20c7b0 Update Rust crate regex to 1.10.2 2023-10-16 17:22:22 +00:00
Daniel Hofstetter e8ab1a46e2 Merge pull request #53 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.10.1
2023-10-15 10:32:42 +02:00
renovate[bot] 57d3527d81 Update Rust crate regex to 1.10.1 2023-10-14 16:17:34 +00:00
Daniel Hofstetter 48255280aa Merge pull request #52 from uutils/renovate/regex-1.x
Update Rust crate regex to v1.10.0
2023-10-10 06:52:42 +02:00
renovate[bot] bddcc6067f Update Rust crate regex to v1.10.0 2023-10-09 21:57:23 +00:00
Daniel Hofstetter be7fe1dc12 Merge pull request #51 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.9.6
2023-09-30 16:18:10 +02:00
renovate[bot] 70fde9fa18 Update Rust crate regex to 1.9.6 2023-09-30 13:41:06 +00:00
Sylvestre Ledru 2d05b14a2d Merge pull request #34 from philolo1/fix/23-weekday
Support weekdays in parse_datetime
2023-09-19 11:44:14 +02:00
Patrick Klitzke 96db1a281e Support weekdays in parse_datetime
This commit resolves issue #23.

Adds parse_weekday function that uses chrono weekday parser with a map for edge cases.
Adds tests cases to make sure it works correctly.
Use nom for parsing.
2023-09-19 17:47:18 +09:00
Patrick Klitzke 93c02dc0b7 Fixes Negative numbers for @ not being recognized (#43)
* Fixes Negative numbers for @ not being recognized

This commit resolves #40.
Adds new file and functions parse_timestamp.
Adds tests for handling negative numbers.
2023-09-15 11:40:27 +02:00
Daniel Hofstetter 29d641389d Merge pull request #49 from uutils/revert-48-renovate/codecov-codecov-action-4.x
Revert "Update codecov/codecov-action action to v4"
2023-09-15 10:11:25 +02:00
Daniel Hofstetter adb5fabfa7 Revert "Update codecov/codecov-action action to v4" 2023-09-15 09:30:33 +02:00
Sylvestre Ledru 02e5c22985 Merge pull request #46 from philolo1/feature/update-version
update version to 0.5.0
2023-09-14 17:51:04 +02:00
Daniel Hofstetter c6feb9a251 Merge pull request #48 from uutils/renovate/codecov-codecov-action-4.x
Update codecov/codecov-action action to v4
2023-09-14 17:44:30 +02:00
renovate[bot] 852638fef0 Update codecov/codecov-action action to v4 2023-09-14 15:32:52 +00:00
Daniel Hofstetter 5c34517469 Merge pull request #47 from uutils/renovate/actions-checkout-4.x
Update actions/checkout action to v4
2023-09-04 17:10:51 +02:00
renovate[bot] 47c5f3620b Update actions/checkout action to v4 2023-09-04 14:54:00 +00:00
Patrick Klitzke 1e43cc945f update parse_datetime to version to 0.5.0
update fuzz to version to 0.2.0
2023-09-04 17:50:42 +09:00
Sylvestre Ledru f9ce5047a5 Merge pull request #45 from cakebaker/use_rust_2021_edition
Use Rust 2021 edition for fuzzing
2023-09-02 18:05:41 +02:00
Daniel Hofstetter 4c3fd2b1ed Merge pull request #44 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.9.5
2023-09-02 17:54:09 +02:00
Daniel Hofstetter 9a7f87ac09 Use Rust 2021 edition for fuzzing 2023-09-02 17:49:26 +02:00
renovate[bot] 0b0aa70193 Update Rust crate regex to 1.9.5 2023-09-02 15:29:56 +00:00
Daniel Hofstetter 0c44becec8 Merge pull request #42 from cakebaker/remove_time_crate
Adapt chrono settings to remove old time crate
2023-08-30 11:38:15 +02:00
Daniel Hofstetter 64dfea4b47 Adapt chrono settings to remove old time crate 2023-08-30 08:12:18 +02:00
Terts Diepraam 0bdabd5303 Merge pull request #41 from philolo1/fix/spelling
Fix small smelling error in readme
2023-08-30 07:46:09 +02:00
Patrick Klitzke 2112830d8b Fix small smelling error in readme 2023-08-30 14:39:34 +09:00
Terts Diepraam 2737b4a0f2 Merge pull request #33 from philolo1/fix/datetime-should-parse-relative
Make parse_datetime::parse_datetime::from_str accept relative time
2023-08-29 10:06:09 +02:00
Patrick Klitzke f10749eade Refactor parse_datetime to only expose parse_datetime function
Create new file parse_relative_time.rs with the relative time helper function.
Renames from_str to parse_datetime and parse_relative time.
Adds function parse_datetime_at_date.
2023-08-29 09:03:17 +09:00
Terts Diepraam a7380508bc Merge pull request #38 from cakebaker/use_timestamp_opt_instead_of_timestamp
Use timestamp_opt() instead of timestamp() in test
2023-08-28 10:06:16 +02:00
Daniel Hofstetter 7f0a909752 Use timestamp_opt() instead of timestamp() in test 2023-08-28 09:34:38 +02:00
Daniel Hofstetter ff0f99bea5 Merge pull request #35 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.9.4
2023-08-27 11:30:45 +02:00
renovate[bot] dc0d8a3aa0 Update Rust crate regex to 1.9.4 2023-08-26 17:26:24 +00:00
Sylvestre Ledru b615effc4e Merge pull request #32 from philolo1/fix/add-unix-timestamp-test
Add tests for unix timestamp
2023-08-22 13:25:05 -04:00
Patrick Klitzke cebf55cf82 Add a unix timestamp
This pr adds tests to support the @ unix timestamp.
2023-08-21 19:40:43 +09:00
Sylvestre Ledru efee20bf01 Merge pull request #29 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.9.3
2023-08-21 07:10:25 +02:00
Sylvestre Ledru c084001056 Merge pull request #31 from Benjscho/main
Add test for parsing epoch seconds
2023-08-21 07:10:15 +02:00
Ben Schofield a35530ed4a Add test for parsing epoch seconds
Add test to verify epoch seconds can be parsed by `parse_datetime`.
2023-08-20 18:41:39 -07:00
renovate[bot] 6cfcc985a8 Update Rust crate regex to 1.9.3 2023-08-06 00:15:26 +00:00
Sylvestre Ledru 497bebb9e7 Merge pull request #27 from uutils/renovate/regex-1.x
Update Rust crate regex to v1.9.1
2023-07-08 14:21:03 +02:00
renovate[bot] dfd43a6d65 Update Rust crate regex to v1.9.1 2023-07-07 20:20:30 +00:00
Sylvestre Ledru 51dd843ded Merge pull request #26 from undali/f19
add 'next' and 'last' support
2023-07-02 22:10:16 +02:00
undali c24d593821 add 'next' and 'last' support 2023-06-26 16:00:57 +04:00
Sylvestre Ledru bed2d16e67 Release version 0.4.0 2023-06-09 13:43:54 +02:00
Sylvestre Ledru 7ab1ad2f0e document the old name 2023-06-09 13:43:04 +02:00
Sylvestre Ledru c1a2bef4a6 Merge pull request #22 from sylvestre/rename
rename from humantime_to_duration to parse_datetime
2023-06-09 13:41:17 +02:00
Sylvestre Ledru 673d5b600e rename from humantime_to_duration to parse_datetime 2023-06-09 10:16:26 +02:00
Sylvestre Ledru 83e1431db0 Merge pull request #21 from sylvestre/fuzz
Also fuzz fuzz_parse_datetime_from_str for a minute
2023-06-08 11:27:07 +02:00
Sylvestre Ledru 80f8fdf883 README: syntax highlighting 2023-06-07 15:01:25 +02:00
Sylvestre Ledru 22bdd8f49c run the fuzzers in the CI 2023-06-07 13:52:00 +02:00
Sylvestre Ledru dc508c469b add a fuzzer for parse_datetime_from_str 2023-06-07 13:51:40 +02:00
Sylvestre Ledru e503de7559 Merge pull request #12 from Benjscho/main
add datetime parser
2023-06-07 10:03:17 +02:00
Ben Schofield 7ee33d1fb1 cargo fmt 2023-06-06 10:26:05 -07:00
Ben Schofield bc33770075 Update README
Update the README and add a test module to parse_datetime for any
examples presented in the README.
2023-06-06 10:05:14 -07:00
Ben Schofield 29e7a5c335 Update src/parse_datetime.rs
Co-authored-by: Sylvestre Ledru <sledru@mozilla.com>
2023-06-05 08:04:22 -07:00
Ben Schofield 8d797fcbcb typo fix 2023-06-05 08:00:07 -07:00
Ben Schofield c177117428 Fixes for comments 2023-06-05 07:58:44 -07:00
Ben Schofield e53aabd914 add datetime parser
Add a relaxed datetime parser. This datetime parser functions by using `chrono`s
own parsing utilities and a try/succeed approach to parsing.

This implementation of the datetime parser has some drawbacks and some
positives. On the positive side:
- it was easy to implement
- it is easy to add more datetime formats to

In order to add additionally supported formats, a developer can add the
required format string to the `format` mod in `parse_datetime.rs`, and
then add it as a potential format to the relevant `fmts` vec.

On the negative:
- It is not easily customiseable beyond the supported `chrono` parsing
  formats. E.g., `chrono` does not currently support parsing offsets
  without trailing zeros. `from_str("UTC+1")` should return a valid response
  but `chrono` fails to parse this.
- Because it is an attempt driven parser, it is likely not that
  performant. I have not done any performance testing as part of this
change, but I would expect a custom parser to perform much better.
2023-06-03 09:58:36 -07:00
14 changed files with 1583 additions and 569 deletions
+35 -50
View File
@@ -13,15 +13,9 @@ jobs:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: check
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check
test:
name: cargo test
@@ -30,31 +24,18 @@ jobs:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test
fmt:
name: cargo fmt --all -- --check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- run: cargo fmt --all -- --check
clippy:
name: cargo clippy -- -D warnings
@@ -63,17 +44,10 @@ jobs:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
- run: cargo clippy --all-targets -- -D warnings
coverage:
name: Code Coverage
@@ -86,7 +60,7 @@ jobs:
- { os: macos-latest , features: macos }
- { os: windows-latest , features: windows }
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Initialize workflow variables
id: vars
shell: bash
@@ -108,16 +82,9 @@ jobs:
outputs CODECOV_FLAGS
- name: rust toolchain ~ install
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
uses: dtolnay/rust-toolchain@nightly
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
run: cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
env:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: ""
@@ -155,12 +122,30 @@ jobs:
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
- name: Upload coverage results (to Codecov.io)
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
# if: steps.vars.outputs.HAS_CODECOV_TOKEN
with:
# token: ${{ secrets.CODECOV_TOKEN }}
token: ${{ secrets.CODECOV_TOKEN }}
file: ${{ steps.coverage.outputs.report }}
## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }}
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@v4
- 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_parse_datetime -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
Generated
+43 -14
View File
@@ -68,14 +68,6 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "humantime_to_duration"
version = "0.3.1"
dependencies = [
"chrono",
"regex",
]
[[package]]
name = "iana-time-zone"
version = "0.1.56"
@@ -122,9 +114,25 @@ checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
@@ -141,6 +149,15 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parse_datetime"
version = "0.6.0"
dependencies = [
"chrono",
"nom",
"regex",
]
[[package]]
name = "proc-macro2"
version = "1.0.59"
@@ -161,9 +178,21 @@ dependencies = [
[[package]]
name = "regex"
version = "1.8.1"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
@@ -172,9 +201,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.1"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "syn"
+6 -5
View File
@@ -1,12 +1,13 @@
[package]
name = "humantime_to_duration"
description = " parsing human-readable relative time strings and converting them to a Duration"
version = "0.3.1"
name = "parse_datetime"
description = "parsing human-readable time strings and converting them to a DateTime"
version = "0.6.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.8"
regex = "1.10.4"
chrono = { version="0.4", default-features=false, features=["std", "alloc", "clock"] }
nom = "7.1.3"
+41 -23
View File
@@ -1,14 +1,14 @@
# humantime_to_duration
# parse_datetime
[![Crates.io](https://img.shields.io/crates/v/humantime_to_duration.svg)](https://crates.io/crates/humantime_to_duration)
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/humantime_to_duration/blob/main/LICENSE)
[![CodeCov](https://codecov.io/gh/uutils/humantime_to_duration/branch/main/graph/badge.svg)](https://codecov.io/gh/uutils/humantime_to_duration)
[![Crates.io](https://img.shields.io/crates/v/parse_datetime.svg)](https://crates.io/crates/parse_datetime)
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/parse_datetime/blob/main/LICENSE)
[![CodeCov](https://codecov.io/gh/uutils/parse_datetime/branch/main/graph/badge.svg)](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 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.
@@ -20,28 +20,37 @@ Add this to your `Cargo.toml`:
```toml
[dependencies]
humantime_to_duration = "0.3.0"
parse_datetime = "0.5.0"
```
Then, import the crate and use the `from_str` and `from_str_at_date` functions:
Then, import the crate and use the `parse_datetime_at_date` function:
```rs
use humantime_to_duration::{from_str, from_str_at_date};
use chrono::Duration;
use chrono::{Duration, Local};
use parse_datetime::parse_datetime_at_date;
let duration = from_str("+3 days");
assert_eq!(duration.unwrap(), Duration::days(3));
let now = Local::now();
let after = parse_datetime_at_date(now, "+3 days");
let today = Utc::today().naive_utc();
let yesterday = today - Duration::days(1);
assert_eq!(
from_str_at_date(yesterday, "2 days").unwrap(),
Duration::days(1)
(now + Duration::days(3)).naive_utc(),
after.unwrap().naive_utc()
);
```
For DateTime parsing, import the `parse_datetime` function:
```rs
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());
```
### Supported Formats
The `from_str` and `from_str_at_date` functions support the following formats for relative time:
The `parse_datetime` and `parse_datetime_at_date` functions support absolute datetime and the following relative times:
- `num` `unit` (e.g., "-1 hour", "+3 days")
- `unit` (e.g., "hour", "day")
@@ -49,28 +58,37 @@ The `from_str` and `from_str_at_date` functions support the following formats fo
- "yesterday"
- "tomorrow"
- use "ago" for the past
- use "next" or "last" with `unit` (e.g., "next week", "last year")
- combined units with "and" or "," (e.g., "2 years and 1 month", "1 day, 2 hours" or "2 weeks 1 second")
- unix timestamps (for example "@0" "@1344000")
`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.
## Return Values
The `from_str` and `from_str_at_date` functions return:
### parse_datetime and parse_datetime_at_date
- `Ok(Duration)` - If the input string can be parsed as a relative time
- `Err(ParseDurationError)` - If the input string cannot be parsed as a relative time
The `parse_datetime` and `parse_datetime_at_date` function return:
This function will return `Err(ParseDurationError::InvalidInput)` if the input string
cannot be parsed as a relative time.
- `Ok(DateTime<FixedOffset>)` - If the input string can be parsed as a datetime
- `Err(ParseDateTimeError::InvalidInput)` - If the input string cannot be parsed
## Fuzzer
To run the fuzzer:
```
$ cargo fuzz run fuzz_from_str
$ cd fuzz
$ cargo install cargo-fuzz
$ cargo +nightly fuzz run fuzz_parse_datetime
```
## 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.
+1
View File
@@ -0,0 +1 @@
corpus
+49 -40
View File
@@ -67,10 +67,7 @@ checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
@@ -81,12 +78,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "fuzz_from_str"
version = "0.1.0"
name = "fuzz_parse_datetime"
version = "0.2.0"
dependencies = [
"chrono",
"humantime_to_duration",
"libfuzzer-sys",
"parse_datetime",
"rand",
"regex",
]
@@ -99,15 +96,7 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "humantime_to_duration"
version = "0.3.0"
dependencies = [
"chrono",
"regex",
"wasi",
]
[[package]]
@@ -159,9 +148,9 @@ checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]]
name = "libfuzzer-sys"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
dependencies = [
"arbitrary",
"cc",
@@ -176,9 +165,25 @@ checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
@@ -195,6 +200,15 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "parse_datetime"
version = "0.5.0"
dependencies = [
"chrono",
"nom",
"regex",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -251,9 +265,21 @@ dependencies = [
[[package]]
name = "regex"
version = "1.8.4"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
dependencies = [
"aho-corasick",
"memchr",
@@ -262,9 +288,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
@@ -277,29 +303,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
+9 -9
View File
@@ -1,22 +1,22 @@
[package]
name = "fuzz_from_str"
version = "0.1.0"
edition = "2018"
name = "fuzz_parse_datetime"
version = "0.2.0"
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
rand = "0.8.5"
libfuzzer-sys = "0.4"
regex = "1.8.4"
chrono = "0.4"
libfuzzer-sys = "0.4.7"
regex = "1.10.4"
chrono = { version="0.4", default-features=false, features=["std", "alloc", "clock"] }
[dependencies.humantime_to_duration]
[dependencies.parse_datetime]
path = "../"
[[bin]]
name = "fuzz_from_str"
path = "fuzz_targets/from_str.rs"
name = "fuzz_parse_datetime"
path = "fuzz_targets/parse_datetime.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::parse_datetime(s);
});
+446 -280
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+120
View File
@@ -0,0 +1,120 @@
use chrono::{DateTime, FixedOffset, Local, NaiveTime, TimeZone};
use regex::Regex;
mod time_only_formats {
pub const HH_MM: &str = "%R";
pub const HH_MM_SS: &str = "%T";
pub const TWELVEHOUR: &str = "%r";
}
pub(crate) fn parse_time_only(date: DateTime<Local>, s: &str) -> Option<DateTime<FixedOffset>> {
let re =
Regex::new(r"^(?<time>.*?)(?:(?<sign>\+|-)(?<h>[0-9]{1,2}):?(?<m>[0-9]{0,2}))?$").unwrap();
let captures = re.captures(s)?;
let parsed_offset = match captures.name("h") {
Some(hours) if !(hours.as_str().is_empty()) => {
let mut offset_in_sec = hours.as_str().parse::<i32>().unwrap() * 3600;
match captures.name("m") {
Some(minutes) if !(minutes.as_str().is_empty()) => {
offset_in_sec += minutes.as_str().parse::<i32>().unwrap() * 60;
}
_ => (),
};
offset_in_sec *= if &captures["sign"] == "-" { -1 } else { 1 };
FixedOffset::east_opt(offset_in_sec)
}
_ => None,
};
for fmt in [
time_only_formats::HH_MM,
time_only_formats::HH_MM_SS,
time_only_formats::TWELVEHOUR,
] {
if let Ok(parsed) = NaiveTime::parse_from_str(captures["time"].trim(), fmt) {
let parsed_dt = date.date_naive().and_time(parsed);
let offset = match parsed_offset {
Some(offset) => offset,
None => *date.offset(),
};
return offset.from_local_datetime(&parsed_dt).single();
}
}
None
}
#[cfg(test)]
mod tests {
use crate::parse_time_only_str::parse_time_only;
use chrono::{DateTime, Local, TimeZone};
use std::env;
fn get_test_date() -> DateTime<Local> {
Local.with_ymd_and_hms(2024, 3, 3, 0, 0, 0).unwrap()
}
#[test]
fn test_time_only() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "21:04")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709499840)
}
#[test]
fn test_time_with_offset() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "21:04 +0530")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709480040);
}
#[test]
fn test_time_with_hour_only_offset() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "22:04 +01")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709499840);
}
#[test]
fn test_time_with_hour_only_neg_offset() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "17:04 -04")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709499840);
}
#[test]
fn test_time_with_seconds() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "21:04:30")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709499870)
}
#[test]
fn test_time_with_seconds_with_offset() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "21:04:30 +0530")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709480070)
}
#[test]
fn test_twelve_hour_time() {
env::set_var("TZ", "UTC");
let parsed_time = parse_time_only(get_test_date(), "9:04:00 PM")
.unwrap()
.timestamp();
assert_eq!(parsed_time, 1709499840)
}
}
+111
View File
@@ -0,0 +1,111 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use core::fmt;
use std::error::Error;
use std::fmt::Display;
use std::num::ParseIntError;
use nom::branch::alt;
use nom::character::complete::{char, digit1};
use nom::combinator::all_consuming;
use nom::multi::fold_many0;
use nom::sequence::preceded;
use nom::sequence::tuple;
use nom::{self, IResult};
#[derive(Debug, PartialEq)]
pub enum ParseTimestampError {
InvalidNumber(ParseIntError),
InvalidInput,
}
impl Display for ParseTimestampError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidInput => {
write!(f, "Invalid input string: cannot be parsed as a timestamp")
}
Self::InvalidNumber(err) => {
write!(f, "Invalid timestamp number: {err}")
}
}
}
}
impl Error for ParseTimestampError {}
// TODO is this necessary
impl From<ParseIntError> for ParseTimestampError {
fn from(err: ParseIntError) -> Self {
Self::InvalidNumber(err)
}
}
type NomError<'a> = nom::Err<nom::error::Error<&'a str>>;
impl<'a> From<NomError<'a>> for ParseTimestampError {
fn from(_err: NomError<'a>) -> Self {
Self::InvalidInput
}
}
pub(crate) fn parse_timestamp(s: &str) -> Result<i64, ParseTimestampError> {
let s = s.trim().to_lowercase();
let s = s.as_str();
let res: IResult<&str, (char, &str)> = all_consuming(preceded(
char('@'),
tuple((
// Note: to stay compatible with gnu date this code allows
// multiple + and - and only considers the last one
fold_many0(
// parse either + or -
alt((char('+'), char('-'))),
// start with a +
|| '+',
// whatever we get (+ or -), update the accumulator to that value
|_, c| c,
),
digit1,
)),
))(s);
let (_, (sign, number_str)) = res?;
let mut number = number_str.parse::<i64>()?;
if sign == '-' {
number *= -1;
}
Ok(number)
}
#[cfg(test)]
mod tests {
use crate::parse_timestamp::parse_timestamp;
#[test]
fn test_valid_timestamp() {
assert_eq!(parse_timestamp("@1234"), Ok(1234));
assert_eq!(parse_timestamp("@99999"), Ok(99999));
assert_eq!(parse_timestamp("@-4"), Ok(-4));
assert_eq!(parse_timestamp("@-99999"), Ok(-99999));
assert_eq!(parse_timestamp("@+4"), Ok(4));
assert_eq!(parse_timestamp("@0"), Ok(0));
// gnu date accepts numbers signs and uses the last sign
assert_eq!(parse_timestamp("@---+12"), Ok(12));
assert_eq!(parse_timestamp("@+++-12"), Ok(-12));
assert_eq!(parse_timestamp("@+----+12"), Ok(12));
assert_eq!(parse_timestamp("@++++-123"), Ok(-123));
}
#[test]
fn test_invalid_timestamp() {
assert!(parse_timestamp("@").is_err());
assert!(parse_timestamp("@+--+").is_err());
assert!(parse_timestamp("@+1ab2").is_err());
}
}
+99
View File
@@ -0,0 +1,99 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use chrono::Weekday;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::value;
use nom::{self, IResult};
// Helper macro to simplify tag matching
macro_rules! tag_match {
($day:expr, $($pattern:expr),+) => {
value($day, alt(($(tag($pattern)),+)))
};
}
pub(crate) fn parse_weekday(s: &str) -> Option<Weekday> {
let s = s.trim().to_lowercase();
let s = s.as_str();
let parse_result: IResult<&str, Weekday> = nom::combinator::all_consuming(alt((
tag_match!(Weekday::Mon, "monday", "mon"),
tag_match!(Weekday::Tue, "tuesday", "tues", "tue"),
tag_match!(Weekday::Wed, "wednesday", "wednes", "wed"),
tag_match!(Weekday::Thu, "thursday", "thurs", "thur", "thu"),
tag_match!(Weekday::Fri, "friday", "fri"),
tag_match!(Weekday::Sat, "saturday", "sat"),
tag_match!(Weekday::Sun, "sunday", "sun"),
)))(s);
match parse_result {
Ok((_, weekday)) => Some(weekday),
Err(_) => None,
}
}
#[cfg(test)]
mod tests {
use chrono::Weekday::*;
use crate::parse_weekday::parse_weekday;
#[test]
fn test_valid_weekdays() {
let days = [
("mon", Mon),
("monday", Mon),
("tue", Tue),
("tues", Tue),
("tuesday", Tue),
("wed", Wed),
("wednes", Wed),
("wednesday", Wed),
("thu", Thu),
("thursday", Thu),
("fri", Fri),
("friday", Fri),
("sat", Sat),
("saturday", Sat),
("sun", Sun),
("sunday", Sun),
];
for (name, weekday) in days {
assert_eq!(parse_weekday(name), Some(weekday));
assert_eq!(parse_weekday(&format!(" {}", name)), Some(weekday));
assert_eq!(parse_weekday(&format!(" {} ", name)), Some(weekday));
assert_eq!(parse_weekday(&format!("{} ", name)), Some(weekday));
let (left, right) = name.split_at(1);
let (test_str1, test_str2) = (
format!("{}{}", left.to_uppercase(), right.to_lowercase()),
format!("{}{}", left.to_lowercase(), right.to_uppercase()),
);
assert_eq!(parse_weekday(&test_str1), Some(weekday));
assert_eq!(parse_weekday(&test_str2), Some(weekday));
}
}
#[test]
fn test_invalid_weekdays() {
let days = [
"mond",
"tuesda",
"we",
"th",
"fr",
"sa",
"su",
"garbageday",
"tomorrow",
"yesterday",
];
for day in days {
assert!(parse_weekday(day).is_none());
}
}
}
-147
View File
@@ -1,148 +1 @@
use chrono::{Duration, Utc};
use humantime_to_duration::{from_str, from_str_at_date, ParseDurationError};
#[test]
fn test_invalid_input() {
let result = from_str("foobar");
println!("{result:?}");
assert_eq!(result, Err(ParseDurationError::InvalidInput));
let result = from_str("invalid 1");
assert_eq!(result, Err(ParseDurationError::InvalidInput));
}
#[test]
fn test_duration_parsing() {
assert_eq!(from_str("1 year").unwrap(), Duration::seconds(31_536_000));
assert_eq!(
from_str("-2 years").unwrap(),
Duration::seconds(-63_072_000)
);
assert_eq!(
from_str("2 years ago").unwrap(),
Duration::seconds(-63_072_000)
);
assert_eq!(from_str("year").unwrap(), Duration::seconds(31_536_000));
assert_eq!(from_str("1 month").unwrap(), Duration::seconds(2_592_000));
assert_eq!(
from_str("1 month and 2 weeks").unwrap(),
Duration::seconds(3_801_600)
);
assert_eq!(
from_str("1 month, 2 weeks").unwrap(),
Duration::seconds(3_801_600)
);
assert_eq!(
from_str("1 months 2 weeks").unwrap(),
Duration::seconds(3_801_600)
);
assert_eq!(
from_str("1 month and 2 weeks ago").unwrap(),
Duration::seconds(-3_801_600)
);
assert_eq!(from_str("2 months").unwrap(), Duration::seconds(5_184_000));
assert_eq!(from_str("month").unwrap(), Duration::seconds(2_592_000));
assert_eq!(
from_str("1 fortnight").unwrap(),
Duration::seconds(1_209_600)
);
assert_eq!(
from_str("3 fortnights").unwrap(),
Duration::seconds(3_628_800)
);
assert_eq!(from_str("fortnight").unwrap(), Duration::seconds(1_209_600));
assert_eq!(from_str("1 week").unwrap(), Duration::seconds(604_800));
assert_eq!(
from_str("1 week 3 days").unwrap(),
Duration::seconds(864_000)
);
assert_eq!(
from_str("1 week 3 days ago").unwrap(),
Duration::seconds(-864_000)
);
assert_eq!(from_str("-2 weeks").unwrap(), Duration::seconds(-1_209_600));
assert_eq!(
from_str("2 weeks ago").unwrap(),
Duration::seconds(-1_209_600)
);
assert_eq!(from_str("week").unwrap(), Duration::seconds(604_800));
assert_eq!(from_str("1 day").unwrap(), Duration::seconds(86_400));
assert_eq!(from_str("2 days ago").unwrap(), Duration::seconds(-172_800));
assert_eq!(from_str("-2 days").unwrap(), Duration::seconds(-172_800));
assert_eq!(from_str("day").unwrap(), Duration::seconds(86_400));
assert_eq!(from_str("1 hour").unwrap(), Duration::seconds(3_600));
assert_eq!(from_str("1 h").unwrap(), Duration::seconds(3_600));
assert_eq!(from_str("1 hour ago").unwrap(), Duration::seconds(-3_600));
assert_eq!(from_str("-2 hours").unwrap(), Duration::seconds(-7_200));
assert_eq!(from_str("hour").unwrap(), Duration::seconds(3_600));
assert_eq!(from_str("1 minute").unwrap(), Duration::seconds(60));
assert_eq!(from_str("1 min").unwrap(), Duration::seconds(60));
assert_eq!(from_str("2 minutes").unwrap(), Duration::seconds(120));
assert_eq!(from_str("2 mins").unwrap(), Duration::seconds(120));
assert_eq!(from_str("2m").unwrap(), Duration::seconds(120));
assert_eq!(from_str("min").unwrap(), Duration::seconds(60));
assert_eq!(from_str("1 second").unwrap(), Duration::seconds(1));
assert_eq!(from_str("1 s").unwrap(), Duration::seconds(1));
assert_eq!(from_str("2 seconds").unwrap(), Duration::seconds(2));
assert_eq!(from_str("2 secs").unwrap(), Duration::seconds(2));
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::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(),
Duration::seconds(39_398_402)
);
assert_eq!(
from_str("1 year 2 months 4 weeks 3 days and 2 seconds ago").unwrap(),
Duration::seconds(-39_398_402)
);
}
#[test]
#[should_panic]
fn test_display_parse_duration_error_through_from_str() {
let invalid_input = "9223372036854775807 seconds and 1 second";
let _ = from_str(invalid_input).unwrap();
}
#[test]
fn test_display_should_fail() {
let invalid_input = "Thu Jan 01 12:34:00 2015";
let error = from_str(invalid_input).unwrap_err();
assert_eq!(
format!("{error}"),
"Invalid input string: cannot be parsed as a relative time"
);
}
#[test]
fn test_from_str_at_date_day() {
let today = Utc::now().date_naive();
let yesterday = today - Duration::days(1);
assert_eq!(
from_str_at_date(yesterday, "2 days").unwrap(),
Duration::days(1)
);
}
#[test]
fn test_invalid_input_at_date() {
let today = Utc::now().date_naive();
let result = from_str_at_date(today, "foobar");
println!("{result:?}");
assert_eq!(result, Err(ParseDurationError::InvalidInput));
let result = from_str_at_date(today, "invalid 1r");
assert_eq!(result, Err(ParseDurationError::InvalidInput));
}