160 Commits

Author SHA1 Message Date
Sylvestre Ledru 52dca03c56 README.md: adjust the doc to avoid the hardcoded version 2025-02-17 11:24:36 +01:00
Daniel Hofstetter d4384ceec9 Merge pull request #119 from sylvestre/new-release
Prepare the new relase + clippy
2025-02-17 08:58:27 +01:00
Sylvestre Ledru 46cc4147c7 fix clippy pedantic 2025-02-16 21:52:29 +01:00
Sylvestre Ledru 93e1c9bfe7 prepare 0.8.0 2025-02-16 21:32:13 +01:00
Daniel Hofstetter e7a7a0b07f Merge pull request #110 from jfinkels/parse-ref-date-and-delta
Parse reference datetime and timedelta from the same string
2025-02-16 17:20:29 +01:00
jfinkels d4353d0888 Use str.ends_with for patterns ending in Z
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
2025-02-16 11:16:23 -05:00
Jeffrey Finkelstein 8aee979de5 Parse reference date and delta from same string
Change the `parse_datetime()` function so that it parses both a
reference date and a time delta from one string. The new implementation
attempts to parse the datetime from the longest possible prefix of the
string. The remainder of the string is parsed as the time delta. This
allows us to parse more combinations of reference dates and time deltas
more easily.

Fixes #104
2025-02-14 22:00:09 -05:00
Jeffrey Finkelstein f1fada62a6 Add additional pattern for datetime w/o timezone 2025-02-14 22:00:09 -05:00
Daniel Hofstetter ca6a39c0a7 Merge pull request #117 from jfinkels/add-time-to-date-after-parsing
Add time to parsed date after parsing not before
2025-02-14 10:10:14 +01:00
Jeffrey Finkelstein db18db4e1d Add time to parsed date after parsing not before
Change the way dates without times are parsed by just parsing the date
as-is and applying the placeholder time (00:00:00) after parsing
instead of before.
2025-02-13 18:48:47 -05:00
Sylvestre Ledru c337aba9cb Merge pull request #116 from jfinkels/remove-add-zero-months
Remove more useless calls to add zero months to date
2025-02-13 07:55:18 +01:00
Sylvestre Ledru 63cfc01e80 Merge pull request #111 from cakebaker/fuzz_remove_rand_dependency
fuzz: remove `rand` dependency
2025-02-13 07:54:53 +01:00
Sylvestre Ledru 1daaeb42a2 Merge pull request #118 from jfinkels/add-more-tests
Add a lot more unit tests
2025-02-13 07:54:36 +01:00
Jeffrey Finkelstein 3fcbac6fae Add a lot more unit tests
Add more unit tests for `parse_datetime()` when given an input without
a time delta (like "+1 year" or similar).
2025-02-12 22:04:09 -05:00
Jeffrey Finkelstein 65cd15dba9 Remove useless call to add zero months to date 2025-02-12 21:49:23 -05:00
Daniel Hofstetter 65c88f3586 Merge pull request #115 from jfinkels/patterns-with-length
Greedy parsing of datetime before time delta
2025-02-12 07:15:23 +01:00
jfinkels 2c55db21fa Avoid unnecessary slicing when parsing offset
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
2025-02-11 20:05:55 -05:00
Jeffrey Finkelstein badc887606 Greedy parsing of datetime before time delta
Simulate greedy parsing of an absolute datetime in the prefix of a
string before parsing a subsequent time delta in the suffix of the
string. This does not change the behavior of `parse_datetime`, it just
prepares the code for a future change that allows parsing both the
absolute datetime and the time delta from the same string.

Greedy parsing is implemented by iterating over a list of patterns in
decreasing order of length so that longer patterns are tried before
shorter patterns. This guarantees that if there is an absolute datetime
present at the beginning of the string, then it will definitely be
parsed and the remaining part of the string is assumed to contain a time
delta.
2025-02-09 17:31:25 -05:00
Jeffrey Finkelstein f8adcb6c88 Add passing test for parsing datetime ending in Z
Add a passing unit test for parsing a datetime ending in the letter Z,
as in

    2023-06-03 12:00:01Z

This is treated as a datetime in the UTC time zone.
2025-02-09 17:31:25 -05:00
Daniel Hofstetter d0dceb6eaf Merge pull request #114 from jfinkels/parse-empty-string
Allow empty string in parse_relative_time_at_date
2025-02-09 16:18:43 +01:00
Jeffrey Finkelstein 45b9d9f09a Allow empty string in parse_relative_time_at_date
Allow the empty string when parsing the relative time in
`parse_relative_time_at_date()`. The empty string represents zero time
delta.
2025-02-08 15:05:50 -05:00
Daniel Hofstetter 0965130c1b fuzz: remove rand dependency 2025-02-08 20:59:19 +01:00
Sylvestre Ledru 51ddcb793f Merge pull request #113 from jfinkels/set-utc-tz-in-test
Explicitly set timezone UTC0 in a unit test
2025-02-08 20:59:03 +01:00
Sylvestre Ledru 7985aa04ba Merge pull request #112 from jfinkels/fix-add-months-tests
Make tests of adding months more resilient
2025-02-08 20:58:39 +01:00
Jeffrey Finkelstein 5c0c9c4405 Explicitly set timezone UTC0 in a unit test 2025-02-08 14:48:06 -05:00
Jeffrey Finkelstein 8dd8051eb7 Make tests of adding months more resilient
Protect against a situation where adding one month to the current day
would cause an overflow in the next month in some unit tests of
`parse_relative_time`. For example, adding one month to March 31
should overflow to May 1 because April only has 30 days. This is a
difference in the behavior we want for our library compared with the
behavior of `chrono`.
2025-02-08 14:42:38 -05:00
Daniel Hofstetter 073b9f7b70 Merge pull request #109 from uutils/renovate/libfuzzer-sys-0.x-lockfile
fix(deps): update rust crate libfuzzer-sys to v0.4.9
2025-01-29 07:16:20 +01:00
renovate[bot] 0318dabe3a fix(deps): update rust crate libfuzzer-sys to v0.4.9 2025-01-28 18:33:49 +00:00
Sylvestre Ledru 57fe123e21 Merge pull request #107 from cakebaker/bump_nom
Bump `nom` & adapt to API changes
2025-01-27 11:14:31 +01:00
Daniel Hofstetter 2fe83cabf1 Adapt to API changes in nom 8.0.0 2025-01-27 10:53:08 +01:00
Daniel Hofstetter 94d4112707 Bump nom from 7.1.3 to 8.0.0 2025-01-27 10:48:23 +01:00
Sylvestre Ledru eda2422bb3 Merge pull request #103 from jfinkels/parse-datetime-with-single-digit-offset-t-sep
Parse datetime with T separator and single digit offset
2025-01-19 23:07:12 +01:00
Jeffrey Finkelstein c948274c42 Parse datetime with T separator and digit offset
Parse a datetime with T separator between date and time, single digit
timezone offset, and no space between time and offset.
2025-01-19 16:38:06 -05:00
Sylvestre Ledru b209341259 Merge pull request #105 from sylvestre/0.7.0
prepare new release 0.7.0
2025-01-19 22:29:25 +01:00
Sylvestre Ledru 9cd22afa29 prepare new release 0.7.0 2025-01-19 22:25:25 +01:00
Sylvestre Ledru a7061bce12 Merge pull request #102 from jfinkels/parse-datetime-with-single-digit-offset
Parse datetimes with single digit timezone offset
2025-01-19 22:25:10 +01:00
Jeffrey Finkelstein ea49dc94f0 Parse datetimes with single digit timezone offset
Parse full datetimes with single digit timezone offsets like +3 or -5.
2025-01-18 18:45:22 -05:00
Sylvestre Ledru 938aa9694b Merge pull request #100 from jfinkels/add-months-overflow-surplus-days
Overflow surplus days when adding months to a date
2025-01-18 23:30:10 +01:00
Sylvestre Ledru 51e294724c Merge pull request #99 from jfinkels/parse-dates-with-slashes
Parse calendar dates with slashes, like 2/29/1996
2025-01-18 23:27:22 +01:00
Sylvestre Ledru fa53ec79bd Merge pull request #78 from cakebaker/readme_bump_version
readme: bump version to 0.6.0
2025-01-18 23:27:02 +01:00
Jeffrey Finkelstein 90d22a2c37 Overflow surplus days when adding months to a date
Change the behavior of `parse_relative_time_at_date()` so that adding
months to a given date causes surplus days to overflow to the next
month. For example, adding 1 month to 1996-03-31 would be 1996-04-31,
but April only has 30 days, so the surplus day rolls over to the next
month, resulting in 1996-05-01.

Closes #98
2025-01-18 23:23:37 +01:00
Jeffrey Finkelstein 06aac3f56f Parse calendar dates with slashes, like 2/29/1996 2025-01-18 23:23:34 +01:00
Sylvestre Ledru 240abf791e Merge pull request #101 from jfinkels/single-digit-tz-offset
Parse single digit timezone offsets
2025-01-18 23:22:50 +01:00
Jeffrey Finkelstein 34f226fd98 Parse single digit timezone offsets
Add parsing for timezone offsets of the form +N or -N. As a side effect
of this change, parsing timezone offsets like +0700, UTC+2, and Z-1 are
now supported.
2025-01-18 14:49:16 -05:00
Sylvestre Ledru 00dc90dd10 Merge pull request #97 from jfinkels/military-time-zones
Add support for military time zones
2025-01-09 07:51:39 +01:00
Jeffrey Finkelstein 3c67f4b5c9 Add support for military time zones
Fixes #39
2025-01-08 21:38:46 -05:00
Daniel Hofstetter faf363c2e6 Merge pull request #96 from uutils/renovate/chrono-0.x-lockfile
fix(deps): update rust crate chrono to v0.4.39
2024-12-09 13:34:01 +01:00
renovate[bot] 56410cebcf fix(deps): update rust crate chrono to v0.4.39 2024-12-09 12:12:42 +00:00
Daniel Hofstetter d777be1bb6 Merge pull request #94 from uutils/renovate/migrate-config
chore(config): migrate renovate config
2024-11-29 16:05:53 +01:00
Daniel Hofstetter 6429ef26c5 Merge pull request #93 from uutils/renovate/codecov-codecov-action-5.x
chore(deps): update codecov/codecov-action action to v5
2024-11-29 16:03:30 +01:00
Daniel Hofstetter fa311c0dd4 ci: fix deprecated codecov argument 2024-11-29 15:59:28 +01:00
renovate[bot] 7ab9a02761 chore(deps): update codecov/codecov-action action to v5 2024-11-29 14:49:20 +00:00
Sylvestre Ledru 78877a1b98 Merge pull request #92 from cakebaker/ci_fix_code_coverage
ci: use `-Cinstrument-coverage` instead of `-Zprofile`
2024-11-29 15:49:10 +01:00
Sylvestre Ledru 623c626b3a Merge pull request #95 from cakebaker/clippy_fix_warning
clippy: fix warning from `needless_return` lint
2024-11-29 09:53:27 +01:00
Daniel Hofstetter a9bccfa20a clippy: fix warning from needless_return lint 2024-11-29 09:44:40 +01:00
renovate[bot] dba1374f8e chore(config): migrate config renovate.json 2024-11-18 14:38:21 +00:00
Daniel Hofstetter 9b266bcb17 ci: use -Cinstrument-coverage instead of -Zprofile
Support for -Zprofile has been removed: https://github.com/rust-lang/rust/pull/131829
2024-11-11 09:48:09 +01:00
Sylvestre Ledru 5e3fc539a3 Merge pull request #88 from cakebaker/bump_iana_time_zone_in_fuzz
Bump `iana-time-zone` in `fuzz` from `0.1.56` to `0.1.61`
2024-11-08 10:28:05 +01:00
Sylvestre Ledru c26d83fd6e Merge pull request #91 from uutils/renovate/libfuzzer-sys-0.x-lockfile
fix(deps): update rust crate libfuzzer-sys to v0.4.8
2024-11-08 10:27:56 +01:00
renovate[bot] a841ad0fc4 fix(deps): update rust crate libfuzzer-sys to v0.4.8 2024-11-08 01:44:41 +00:00
Daniel Hofstetter 8ca89bdb55 Merge pull request #90 from uutils/renovate/regex-1.x-lockfile
fix(deps): update rust crate regex to v1.11.1
2024-10-25 07:06:26 +02:00
renovate[bot] 48852068de fix(deps): update rust crate regex to v1.11.1 2024-10-24 17:07:28 +00:00
Daniel Hofstetter 3813a3bae9 Merge pull request #89 from uutils/renovate/regex-1.x-lockfile
fix(deps): update rust crate regex to v1.11.0
2024-09-30 06:59:39 +02:00
renovate[bot] 313de7ba7c fix(deps): update rust crate regex to v1.11.0 2024-09-29 16:46:09 +00:00
Daniel Hofstetter 7913ecd888 Merge pull request #87 from jetlime/main
`parse_relative_time`: Include 'this' direction
2024-09-17 16:11:30 +02:00
Paul Houssel 8a528468ae include 'this' direction 2024-09-17 15:31:00 +02:00
Daniel Hofstetter 212c6f7c24 Merge pull request #85 from ysthakur/fix-relative-time
Don't use Durations for calculating relative times
2024-09-17 11:06:59 +02:00
Daniel Hofstetter cd7020f038 Bump iana-time-zone in fuzz from 0.1.56 to 0.1.61 2024-09-17 09:49:38 +02:00
Daniel Hofstetter e543ee64a7 Merge pull request #75 from uutils/renovate/chrono-0.x-lockfile
fix(deps): update rust crate chrono to v0.4.38
2024-09-17 09:43:55 +02:00
renovate[bot] 19d49cfe73 fix(deps): update rust crate chrono to v0.4.38 2024-09-17 07:39:50 +00:00
Sylvestre Ledru 86d72fc867 Merge pull request #74 from cakebaker/bump_chrono
Bump `chrono` and `iana-time-zone`; fix use of deprecated function
2024-09-17 09:38:40 +02:00
Daniel Hofstetter bbcafc42f9 Replace use of NativeDateTime::from_timestamp_opt
with DateTime::from_timestamp due to deprecation
2024-09-17 09:34:25 +02:00
Daniel Hofstetter 1570d92fc5 Bump chrono and iana-time-zone
chrono: 0.4.26 -> 0.4.38
iana-time-zone: 0.1.56 -> 0.1.61
2024-09-17 09:34:14 +02:00
ysthakur e41e26fef1 Replace unnecessary return with None 2024-09-04 23:55:47 -04:00
ysthakur d5015a641e Format 2024-08-28 02:24:49 -04:00
ysthakur 5e6bddb6f5 Get rid of parse_relative_time 2024-08-27 22:13:47 -04:00
ysthakur ced645a4f4 Make helpers timezone-generic 2024-08-27 21:46:49 -04:00
ysthakur d75338a51b Make tests for months without 30 days and leap years 2024-08-27 19:02:08 -04:00
ysthakur d4f67bbde0 Don't use Durations for calculating relative times 2024-08-27 18:51:10 -04:00
Daniel Hofstetter ba19b94acd Merge pull request #81 from uutils/renovate/regex-1.x-lockfile
fix(deps): update rust crate regex to v1.10.6
2024-08-02 18:32:25 +02:00
renovate[bot] f7f789913a fix(deps): update rust crate regex to v1.10.6 2024-08-02 16:04:11 +00:00
Daniel Hofstetter 7e84a62b9e Merge pull request #79 from uutils/renovate/regex-1.x-lockfile
fix(deps): update rust crate regex to v1.10.5
2024-06-09 15:59:12 +02:00
renovate[bot] db8c1d364f fix(deps): update rust crate regex to v1.10.5 2024-06-09 13:29:07 +00:00
Daniel Hofstetter e1aa18d263 readme: bump version to 0.6.0 2024-05-21 07:22:43 +02:00
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
17 changed files with 2235 additions and 1009 deletions
+25 -64
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
@@ -101,28 +75,22 @@ jobs:
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
outputs TOOLCHAIN
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='--all -- --check' ; ## default to '--all-features' for code coverage
# * CODECOV_FLAGS
CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' )
outputs CODECOV_FLAGS
- name: rust toolchain ~ install
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@nightly
with:
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: llvm-tools-preview
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
run: cargo test --no-fail-fast
env:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: ""
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
RUSTFLAGS: "-Cinstrument-coverage -Zcoverage-options=branch -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
RUSTDOCFLAGS: "-Cpanic=abort"
LLVM_PROFILE_FILE: "parse_datetime-%p-%m.profraw"
- name: "`grcov` ~ install"
id: build_grcov
shell: bash
@@ -150,16 +118,15 @@ jobs:
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
mkdir -p "${COVERAGE_REPORT_DIR}"
# display coverage files
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
# generate coverage report
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\()"
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --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
# if: steps.vars.outputs.HAS_CODECOV_TOKEN
uses: codecov/codecov-action@v5
with:
# token: ${{ secrets.CODECOV_TOKEN }}
file: ${{ steps.coverage.outputs.report }}
token: ${{ secrets.CODECOV_TOKEN }}
files: ${{ steps.coverage.outputs.report }}
## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }}
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
name: codecov-umbrella
@@ -171,7 +138,7 @@ jobs:
env:
RUN_FOR: 60
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Install `cargo-fuzz`
run: cargo install cargo-fuzz
@@ -181,10 +148,4 @@ jobs:
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
cargo +nightly fuzz run fuzz_parse_datetime -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
Generated
+75 -67
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "aho-corasick"
@@ -52,14 +52,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.26"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"winapi",
"windows-targets",
]
[[package]]
@@ -70,16 +70,16 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "iana-time-zone"
version = "0.1.56"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
"windows-core",
]
[[package]]
@@ -93,9 +93,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.63"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
@@ -114,9 +114,18 @@ 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 = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "num-traits"
@@ -135,9 +144,10 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parse_datetime"
version = "0.4.0"
version = "0.8.0"
dependencies = [
"chrono",
"nom",
"regex",
]
@@ -161,9 +171,21 @@ dependencies = [
[[package]]
name = "regex"
version = "1.8.1"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
@@ -172,9 +194,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.1"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "syn"
@@ -195,19 +217,20 @@ checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "wasm-bindgen"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
@@ -220,9 +243,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -230,9 +253,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
@@ -243,50 +266,29 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "winapi"
version = "0.3.9"
name = "windows-core"
version = "0.52.0"
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"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
@@ -295,42 +297,48 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+5 -4
View File
@@ -1,12 +1,13 @@
[package]
name = "parse_datetime"
description = " parsing human-readable relative time strings and converting them to a Duration"
version = "0.4.0"
description = "parsing human-readable time strings and converting them to a DateTime"
version = "0.8.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/uutils/parse_datetime"
readme = "README.md"
[dependencies]
regex = "1.8"
chrono = { version="0.4", default-features=false, features=["std", "alloc", "clock"] }
regex = "1.10.4"
chrono = { version="0.4.38", default-features=false, features=["std", "alloc", "clock"] }
nom = "8.0.0"
+26 -32
View File
@@ -4,7 +4,7 @@
[![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`, or parsing human-readable datetime strings and converting them to a `DateTime`.
A Rust crate for parsing human-readable relative time strings and human-readable datetime strings and converting them to a `DateTime`.
## Features
@@ -18,39 +18,38 @@ A Rust crate for parsing human-readable relative time strings and converting the
Add this to your `Cargo.toml`:
```toml
[dependencies]
parse_datetime = "0.4.0"
```
cargo add parse_datetime
```
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 parse_datetime::{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` module:
For DateTime parsing, import the `parse_datetime` function:
```rs
use parse_datetime::parse_datetime::from_str;
use parse_datetime::parse_datetime;
use chrono::{Local, TimeZone};
let dt = from_str("2021-02-14 06:37:47");
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")
@@ -58,35 +57,30 @@ 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
### Duration
### parse_datetime and parse_datetime_at_date
The `from_str` and `from_str_at_date` functions return:
The `parse_datetime` and `parse_datetime_at_date` function return:
- `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
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
- `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
+1
View File
@@ -0,0 +1 @@
corpus
+91 -150
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "aho-corasick"
@@ -46,11 +46,13 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "cc"
version = "1.0.79"
version = "1.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
@@ -61,17 +63,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.26"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
"windows-targets",
]
[[package]]
@@ -81,39 +80,27 @@ 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",
"libfuzzer-sys",
"parse_datetime",
"rand",
"regex",
]
[[package]]
name = "getrandom"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "iana-time-zone"
version = "0.1.56"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
"windows-core",
]
[[package]]
@@ -127,18 +114,18 @@ dependencies = [
[[package]]
name = "jobserver"
version = "0.1.26"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.63"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
@@ -151,13 +138,12 @@ checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]]
name = "libfuzzer-sys"
version = "0.4.6"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
@@ -168,9 +154,18 @@ 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 = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "num-traits"
@@ -189,18 +184,13 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "parse_datetime"
version = "0.4.0"
version = "0.7.0"
dependencies = [
"chrono",
"nom",
"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"
@@ -219,41 +209,23 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "regex"
version = "1.8.4"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
@@ -262,9 +234,15 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.2"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
@@ -277,50 +255,28 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
@@ -333,9 +289,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -343,9 +299,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
@@ -356,50 +312,29 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "winapi"
version = "0.3.9"
name = "windows-core"
version = "0.52.0"
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"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
@@ -408,42 +343,48 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+8 -15
View File
@@ -1,28 +1,21 @@
[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.parse_datetime]
path = "../"
[[bin]]
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"
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 _ = parse_datetime::from_str(s);
let _ = parse_datetime::parse_datetime(s);
});
@@ -1,8 +0,0 @@
#![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);
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
"config:recommended"
]
}
+784 -268
View File
File diff suppressed because it is too large Load Diff
-252
View File
@@ -1,252 +0,0 @@
// 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()
);
}
}
}
File diff suppressed because it is too large Load Diff
+210
View File
@@ -0,0 +1,210 @@
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";
}
/// Convert a military time zone string to a time zone offset.
///
/// Military time zones are the letters A through Z except J. They are
/// described in RFC 5322.
fn to_offset(tz: &str) -> Option<FixedOffset> {
let hour = match tz {
"A" => 1,
"B" => 2,
"C" => 3,
"D" => 4,
"E" => 5,
"F" => 6,
"G" => 7,
"H" => 8,
"I" => 9,
"K" => 10,
"L" => 11,
"M" => 12,
"N" => -1,
"O" => -2,
"P" => -3,
"Q" => -4,
"R" => -5,
"S" => -6,
"T" => -7,
"U" => -8,
"V" => -9,
"W" => -10,
"X" => -11,
"Y" => -12,
"Z" => 0,
_ => return None,
};
let offset_in_sec = hour * 3600;
FixedOffset::east_opt(offset_in_sec)
}
/// Parse a time string without an offset and apply an offset to it.
///
/// Multiple formats are attempted when parsing the string.
fn parse_time_with_offset_multi(
date: DateTime<Local>,
offset: FixedOffset,
s: &str,
) -> Option<DateTime<FixedOffset>> {
for fmt in [
time_only_formats::HH_MM,
time_only_formats::HH_MM_SS,
time_only_formats::TWELVEHOUR,
] {
let parsed = match NaiveTime::parse_from_str(s, fmt) {
Ok(t) => t,
Err(_) => continue,
};
let parsed_dt = date.date_naive().and_time(parsed);
match offset.from_local_datetime(&parsed_dt).single() {
Some(dt) => return Some(dt),
None => continue,
}
}
None
}
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)?;
// Parse the sign, hour, and minute to get a `FixedOffset`, if possible.
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,
};
// Parse the time and apply the parsed offset.
let s = captures["time"].trim();
let offset = match parsed_offset {
Some(offset) => offset,
None => *date.offset(),
};
if let Some(result) = parse_time_with_offset_multi(date, offset, s) {
return Some(result);
}
// Military time zones are specified in RFC 5322, Section 4.3
// "Obsolete Date and Time".
// <https://datatracker.ietf.org/doc/html/rfc5322>
//
// We let the parsing above handle "5:00 AM" so at this point we
// should be guaranteed that we don't have an AM/PM suffix. That
// way, we can safely parse "5:00M" here without interference.
let re = Regex::new(r"(?<time>.*?)(?<tz>[A-IKLMN-YZ])").unwrap();
let captures = re.captures(s)?;
if let Some(tz) = captures.name("tz") {
let s = captures["time"].trim();
let offset = match to_offset(tz.as_str()) {
Some(offset) => offset,
None => *date.offset(),
};
if let Some(result) = parse_time_with_offset_multi(date, offset, s) {
return Some(result);
}
}
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_military_time_zones() {
env::set_var("TZ", "UTC");
let date = get_test_date();
let actual = parse_time_only(date, "05:00C").unwrap().timestamp();
// Computed via `date -u -d "2024-03-03 05:00:00C" +%s`, using a
// version of GNU date after v8.32 (earlier versions had a bug).
let expected = 1709431200;
assert_eq!(actual, expected);
}
#[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::{self, IResult, Parser};
#[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('@'),
(
// 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,
),
))
.parse(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());
}
}
+100
View File
@@ -0,0 +1,100 @@
// 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, Parser};
// 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"),
)))
.parse(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 parse_datetime::{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));
}