413 Commits

Author SHA1 Message Date
Daniel Hofstetter 6462d410d8 Merge pull request #248 from cakebaker/bump_version_to_0_13_3
chore: bump version to `0.13.3`
2025-11-21 10:12:43 +01:00
Daniel Hofstetter 92d095b53b chore: bump version to 0.13.3 2025-11-21 09:52:19 +01:00
Daniel Hofstetter ea143af391 Merge pull request #247 from cakebaker/bump_dependencies
chore: update dependencies
2025-11-21 09:49:15 +01:00
Daniel Hofstetter 835e46e6f1 chore: update dependencies 2025-11-21 09:44:58 +01:00
Daniel Hofstetter 31b8e58cc5 Merge pull request #246 from 3v1n0/fix-parsing-relative-times
relative: Support parsing floating relative values with spaces
2025-11-21 09:16:18 +01:00
Daniel Hofstetter 9de30f5fc3 Merge pull request #245 from uutils/renovate/actions-checkout-6.x
chore(deps): update actions/checkout action to v6
2025-11-21 06:58:39 +01:00
Marco Trevisan (Treviño) d6ab892c29 relative: Add more tests for parsing signed values with spaces 2025-11-21 06:37:09 +01:00
Marco Trevisan (Treviño) 1e4fc30f7b relative: Support parsing floating relative values with spaces
In case a string such as "now + 1.5 seconds" was parsed we were failing.

This happened because after processing now, the parser was getting
to the point in which the string contained "+ 1.5", and once the sign
was processed, the remaining " 1.5" string conained a space that was
causing sec_and_nsec to fail.

Instead of failing at this point, just strip the spaces after the sign has
been processed.

Note in fact that "0+0.0 seconds" was working fine

Closes: https://github.com/uutils/coreutils/issues/8618
2025-11-21 06:37:09 +01:00
renovate[bot] 6ce151fb58 chore(deps): update actions/checkout action to v6 2025-11-20 18:43:38 +00:00
Daniel Hofstetter 9f6605f20d Merge pull request #244 from cakebaker/bump_version_to_0_13_2
chore: bump version to `0.13.2`
2025-11-09 18:27:31 +01:00
Daniel Hofstetter a025a04d72 chore: bump version to 0.13.2 2025-11-09 17:49:27 +01:00
Sylvestre Ledru dbae631ff0 Merge pull request #241 from cakebaker/literal3
feat: support formats `Nov-14-2024` and `November-14-2024`
2025-11-09 15:03:15 +01:00
Daniel Hofstetter 83278f0ddc Merge pull request #243 from uutils/renovate/jiff-0.x-lockfile
chore(deps): update rust crate jiff to v0.2.16
2025-11-07 17:39:07 +01:00
renovate[bot] ae518cac37 chore(deps): update rust crate jiff to v0.2.16 2025-11-07 15:41:39 +00:00
Daniel Hofstetter bc5d201b68 Merge pull request #242 from cakebaker/update_dependencies
chore: update dependencies
2025-10-30 09:29:46 +01:00
Daniel Hofstetter 8c56c5cb13 chore: update dependencies 2025-10-29 11:13:04 +01:00
Daniel Hofstetter 9c95c9549b feat: support formats Nov-14-2024/November-14-2024 2025-10-29 10:40:22 +01:00
Daniel Hofstetter ba69fb8772 Merge pull request #239 from cakebaker/bump_version_to_0_13_1
chore: bump version to 0.13.1
2025-10-04 17:27:00 +02:00
Daniel Hofstetter 912bc4e706 chore: bump version to 0.13.1 2025-10-04 17:19:18 +02:00
Daniel Hofstetter c239ed0b36 Merge pull request #238 from stillbeingnick/bugfix/issue/coreutils-date-invalid
Changing ErrMode::Cut to ErrMode::Backtrack to allow alt parser flow (Fix for uutils/coreutils issue 8754)
2025-10-04 17:00:00 +02:00
Nicholas Still 3074fe3baa Change ErrMode::Cut to ErrMode::Backtrack to allow alt parser flow 2025-10-04 16:55:43 +02:00
Daniel Hofstetter 29e368662c Merge pull request #237 from yuankunzhang/add-comments-for-build-function
docs: add comments for the build function
2025-09-28 15:21:47 +02:00
yuankunzhang 1e757df39b docs: add comments for the build function 2025-09-27 21:34:09 +08:00
Daniel Hofstetter 7a32b1ee3d Merge pull request #236 from yuankunzhang/remove-regex-dependency
chore: remove regex crate as dependency
2025-09-27 14:29:14 +02:00
yuankunzhang c898cd84de chore: remove regex crate as dependency 2025-09-27 20:10:25 +08:00
Daniel Hofstetter eb610870e1 Merge pull request #235 from cakebaker/bump_version_to_0_13_0
chore: bump version to `0.13.0`
2025-09-26 14:20:54 +02:00
Daniel Hofstetter 9db7a54926 chore: bump version to 0.13.0 2025-09-26 09:18:35 +02:00
Daniel Hofstetter e43652c0ee Merge pull request #232 from yuankunzhang/tz-rule
add support for `TZ="timezone"` date spec
2025-09-25 16:03:41 +02:00
Daniel Hofstetter d9c2ed2b11 Merge pull request #234 from uutils/renovate/regex-1.x-lockfile
fix(deps): update rust crate regex to v1.11.3
2025-09-25 14:32:35 +02:00
renovate[bot] 6a16e61ab1 fix(deps): update rust crate regex to v1.11.3 2025-09-25 12:26:47 +00:00
yuankunzhang f0c499593e feat: add support for TZ="timezone" date spec 2025-09-25 00:09:54 +08:00
Daniel Hofstetter 5453d6777a Merge pull request #231 from yuankunzhang/rename-timezone-to-offset
refactor: rename the timezone module to offset
2025-09-23 17:51:28 +02:00
yuankunzhang 0c09d5bd17 refactor: rename the timezone module to offset 2025-09-23 22:57:59 +08:00
Daniel Hofstetter 5662a1b47f Merge pull request #228 from cakebaker/bump_version_to_0_12_0
Bump version to `0.12.0`
2025-09-22 14:29:38 +02:00
Daniel Hofstetter f75ec46d63 Merge pull request #230 from yuankunzhang/parser-combinators
refactor: replace imperative parsing loop with functional combinators
2025-09-15 16:09:46 +02:00
yuankunzhang 2dc4fa3ffb refactor: replace imperative parsing loop with functional combinators
- Add `TryFrom<Vec<Items>>` implementation for `DateTimeBuilder`
  to improve code separation and reusability.
- Replace manual parsing loop in `parse_items()` with
  `repeat_until` combinator for cleaner approach.
- Allow timestamp strings ending with whitespaces.
2025-09-15 21:49:24 +08:00
Daniel Hofstetter 69649bf6c6 chore: bump version to 0.12.0 2025-09-14 16:44:42 +02:00
Daniel Hofstetter 583eafe62c Merge pull request #229 from yuankunzhang/update-deps
chore: update dependencies
2025-09-14 16:38:36 +02:00
yuankunzhang 6815ba1406 chore: update dependencies 2025-09-14 22:29:31 +08:00
Sylvestre Ledru a4c29bafcb Merge pull request #227 from cakebaker/fuzz_remove_regex_and_chrono
fuzz/Cargo.toml: remove unused `regex` & `chrono`
2025-09-08 14:27:10 +02:00
Daniel Hofstetter e70d066e03 fuzz/Cargo.toml: remove unused regex & chrono 2025-09-08 13:58:12 +02:00
Daniel Hofstetter e5f9894b11 Merge pull request #225 from yuankunzhang/result-based-error-handling
refactor: replace Option-based error handling with Result-based error handling
2025-08-26 10:34:35 +02:00
yuankunzhang 1f4ebd93c6 refactor: replace Option-based error handling with Result-based error handling 2025-08-25 22:38:14 +08:00
Daniel Hofstetter 74dd77db53 Merge pull request #224 from uutils/renovate/regex-1.x-lockfile
fix(deps): update rust crate regex to v1.11.2
2025-08-25 07:58:18 +02:00
renovate[bot] 6f15b90e36 fix(deps): update rust crate regex to v1.11.2 2025-08-24 18:02:53 +00:00
Daniel Hofstetter 4db8bf4b22 Merge pull request #223 from yuankunzhang/support-timezone-offset-larger-than-24-hour
fix: support timezone offsets that exceed 24-hour range
2025-08-24 15:29:11 +02:00
yuankunzhang 84d27e6386 fix: support timezone offsets that exceed 24-hour range 2025-08-24 19:08:48 +08:00
Daniel Hofstetter a0f1376d2f Merge pull request #222 from yuankunzhang/improve-timezone
refactor: improve timezone offset parsing and validation
2025-08-23 17:56:53 +02:00
yuankunzhang 59eac72c44 refactor: improve timezone offset parsing and validation 2025-08-23 16:51:38 +02:00
Daniel Hofstetter d6bde134df Merge pull request #221 from yuankunzhang/misc-refactors
refactor: misc minor refactors
2025-08-23 16:47:26 +02:00
yuankunzhang 43842ebf8e refactor: misc minor refactors 2025-08-23 22:22:33 +08:00
Sylvestre Ledru c31711d351 Merge pull request #214 from cakebaker/refactor_use_better_names
refactor: rename a function name and a var name
2025-08-22 16:52:41 +02:00
Sylvestre Ledru ea5d61b3eb Merge pull request #216 from cakebaker/refactor_rename_var_to_fix_typo
refactor: rename var to fix typo
2025-08-22 16:52:24 +02:00
Daniel Hofstetter c55bea1d8b Merge pull request #220 from uutils/renovate/winnow-0.x-lockfile
fix(deps): update rust crate winnow to v0.7.13
2025-08-22 16:51:18 +02:00
Daniel Hofstetter 6baf7e301d refactor: rename var to fix typo 2025-08-22 16:48:19 +02:00
renovate[bot] 795d7e6c43 fix(deps): update rust crate winnow to v0.7.13 2025-08-22 14:43:38 +00:00
Sylvestre Ledru 3f4bd53eba Merge pull request #219 from cakebaker/fix_use_checked_mul
fix: use `checked_mul()` to avoid overflow with "week"
2025-08-22 16:43:00 +02:00
Daniel Hofstetter ec30e0e1f2 Merge pull request #218 from yuankunzhang/improve-epoch
refactor: improve timestamp handling and test organization
2025-08-22 15:28:10 +02:00
yuankunzhang 70055eaa21 refactor: improve timestamp handling and test organization 2025-08-22 20:47:48 +08:00
Daniel Hofstetter 871cae962e fix: use checked_mul() to avoid overflow with week 2025-08-22 14:46:54 +02:00
Sylvestre Ledru 91692a17e0 Merge pull request #217 from yuankunzhang/checked-mul
fix: use `checked_mul()` to avoid overflow panic
2025-08-22 14:25:52 +02:00
Daniel Hofstetter e225cf0557 Merge pull request #215 from yuankunzhang/improve-api-encapsulation
refactor: improve api encapsulation
2025-08-22 14:14:51 +02:00
yuankunzhang 8e2816d187 fix: use checked_mul() to avoid overflow panic 2025-08-22 20:10:50 +08:00
yuankunzhang 84d4e294df refactor: improve api encapsulation 2025-08-22 19:21:29 +08:00
Daniel Hofstetter c87e103745 refactor: rename a function name and a var name 2025-08-21 13:38:08 +02:00
Daniel Hofstetter 7d0c3e4500 Merge pull request #213 from yuankunzhang/replace-chrono-with-jiff-6
(jiff series #6) refactor: replace chrono with jiff
2025-08-20 17:18:32 +02:00
yuankunzhang ae25053473 refactor: replace chrono with jiff 2025-08-20 11:44:58 +08:00
Daniel Hofstetter 17847a8d9c Merge pull request #212 from yuankunzhang/replace-chrono-with-jiff-5
(jiff series #5) refactor: add conversion from `Day` to `jiff::civil::Weekday`
2025-08-16 16:39:24 +02:00
yuankunzhang 6fce02949d refactor: add conversion from Day to jiff::civil::Weekday 2025-08-16 22:31:12 +08:00
Daniel Hofstetter cc5029c177 Merge pull request #211 from yuankunzhang/replace-chrono-with-jiff-4
(jiff series #4) refactor: optimize offset type sizes and add conversion from `Offset` to `jifff::tz::Offset`
2025-08-16 16:29:12 +02:00
yuankunzhang 1b96dd5707 refactor: optimize offset type sizes and add conversion from Offset to jifff::tz::Offset 2025-08-16 16:10:18 +02:00
Daniel Hofstetter b5439d8cc2 Merge pull request #210 from yuankunzhang/replace-chrono-with-jiff-3
(jiff series #3) refactor: use `(i64, u32)` for fractional seconds in relative time parsing
2025-08-16 16:06:23 +02:00
yuankunzhang d2e6e7819d refactor: use (i64, u32) for fractional seconds in relative time parsing 2025-08-16 21:55:10 +08:00
Daniel Hofstetter a8e26ab539 Merge pull request #209 from yuankunzhang/replace-chrono-with-jiff-2
(jiff series #2) refactor: optimize time type sizes and add conversion from `Time` to `jiff::civil::Time`
2025-08-16 15:20:36 +02:00
yuankunzhang 920cfa5f1b refactor: optimize time type sizes and add convertion from Time to jiff::civil::Time 2025-08-16 15:05:17 +02:00
Daniel Hofstetter a79ce1fc31 Merge pull request #208 from yuankunzhang/replace-chrono-with-jiff-1
(jiff series #1) refactor: add jiff and optimize date type sizes
2025-08-16 15:01:53 +02:00
yuankunzhang c87b6f19eb refactor: add jiff and optimize date type sizes 2025-08-16 20:18:41 +08:00
Daniel Hofstetter 25f9e48e55 Merge pull request #205 from yuankunzhang/refactor-timestamp-parsing
refactor: replace floating-point seconds with precise integer representation
2025-08-13 11:03:37 +02:00
Daniel Hofstetter 14b8a9447c Merge pull request #206 from uutils/dependabot/cargo/slab-0.4.11
chore(deps): bump slab from 0.4.10 to 0.4.11
2025-08-13 08:05:42 +02:00
dependabot[bot] 692f17526d chore(deps): bump slab from 0.4.10 to 0.4.11
Bumps [slab](https://github.com/tokio-rs/slab) from 0.4.10 to 0.4.11.
- [Release notes](https://github.com/tokio-rs/slab/releases)
- [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.11)

---
updated-dependencies:
- dependency-name: slab
  dependency-version: 0.4.11
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-13 04:39:23 +00:00
yuankunzhang 5c686bcfdf refactor: replace floating-point seconds with precise integer representation
Replace f64-based timestamp and second parsing with structured
types using separate second and nanosecond fields to eliminate
floating-point precision issues.
2025-08-12 20:24:38 +08:00
Daniel Hofstetter c86b137e6f Merge pull request #204 from uutils/renovate/actions-checkout-5.x
chore(deps): update actions/checkout action to v5
2025-08-11 17:29:32 +02:00
renovate[bot] f075ba8d8d chore(deps): update actions/checkout action to v5 2025-08-11 15:24:18 +00:00
Daniel Hofstetter 5ca5586523 Merge pull request #203 from yuankunzhang/refactor-timezone-related-code
refactor: extract timezone functionality from time module to timezone module
2025-08-10 16:38:54 +02:00
yuankunzhang 2bc18c52b7 refactor: extract timezone functionality from time module to timezone module.
Move timezone-related code from `time.rs` to `timezone.rs` to
improve separation of concerns. The `Offset` struct and all
timezone parsing functions are now properly separated from time
parsing logic.
2025-08-10 16:30:09 +02:00
Daniel Hofstetter 3eb58ab00a Merge pull request #202 from yuankunzhang/refactor-timestamp-handling
refactor: improve timestamp handling and validation in builder
2025-08-10 16:04:30 +02:00
yuankunzhang 628011ae97 refactor: improve timestamp handling and validation in builder 2025-08-10 20:40:16 +08:00
Daniel Hofstetter 42d09dadf7 Merge pull request #201 from yuankunzhang/minor-improvements
code and docs minor improvements
2025-08-06 16:36:19 +02:00
yuankunzhang 908356f3a3 code and docs minor improvements 2025-08-06 22:15:03 +08:00
Daniel Hofstetter 65965a85d1 Merge pull request #199 from yuankunzhang/implement-pure-number-parsing
feat: implement pure number parsing
2025-08-05 16:02:18 +02:00
yuankunzhang 551985ef91 feat: implement pure number parsing
Implement GNU-compatible pure number interpretation that treats
pure number as year or time based on the current parsing context.
2025-08-04 23:11:41 +08:00
Daniel Hofstetter 8fa8928a6a Merge pull request #198 from yuankunzhang/remove-unsupported-named-time-zones
fix: remove unsupported named time zones
2025-08-04 14:40:00 +02:00
yuankunzhang fa90303836 fix: remove unsupported named time zones 2025-08-04 19:53:13 +08:00
Sylvestre Ledru 46a5ef89c9 Merge pull request #195 from cakebaker/bump_version_to_0_11
Bump version to `0.11.0`
2025-08-01 09:05:34 +02:00
Daniel Hofstetter 226d6a0588 Bump version to 0.11.0 2025-08-01 08:38:55 +02:00
Sylvestre Ledru 28b9f11f2e Merge pull request #179 from cakebaker/remove_unnecessary_option
chore: remove unnecessary `Option` as return value
2025-08-01 08:25:34 +02:00
Sylvestre Ledru b68c325abd Merge pull request #162 from cakebaker/set_timezone_for_tests
tests: set timezone to `UTC0` in integration tests
2025-08-01 08:25:18 +02:00
Daniel Hofstetter 27a626d3d5 Merge pull request #194 from uutils/renovate/rstest-0.x-lockfile
chore(deps): update rust crate rstest to v0.26.1
2025-07-27 16:43:53 +02:00
renovate[bot] 1630fd025e chore(deps): update rust crate rstest to v0.26.1 2025-07-27 14:39:43 +00:00
Daniel Hofstetter cf5770838c Merge pull request #193 from uutils/renovate/rstest-0.x
chore(deps): update rust crate rstest to 0.26
2025-07-27 10:26:19 +02:00
renovate[bot] db9ff29c02 chore(deps): update rust crate rstest to 0.26 2025-07-26 21:25:06 +00:00
Daniel Hofstetter 4cfe8db9be Merge pull request #192 from yuankunzhang/update-readme
Update readme
2025-07-24 16:26:03 +02:00
yuankunzhang 1e7a541528 docs: update readme 2025-07-24 14:21:24 +00:00
Daniel Hofstetter b2bb6dfe91 Merge pull request #191 from yuankunzhang/update-comments
docs: update comments
2025-07-24 16:20:22 +02:00
yuankunzhang d71c6ea41c docs: update comments 2025-07-24 14:09:45 +00:00
Daniel Hofstetter 3b6362e7c8 Merge pull request #190 from yuankunzhang/fix-base-datetime-handling
fix base datetime handling in the builder
2025-07-24 10:21:28 +02:00
yuankunzhang 897a50f4d5 fix base datetime handling in the builder 2025-07-24 10:09:25 +02:00
Daniel Hofstetter 96f477121f Merge pull request #189 from yuankunzhang/support-float-timestamp
fix: support float timestamp values
2025-07-24 10:03:56 +02:00
yuankunzhang c47ce6870a fix: support float timestamp values 2025-07-24 09:34:02 +02:00
Daniel Hofstetter 77e1ab2d00 Merge pull request #188 from yuankunzhang/update-comments
Update comments
2025-07-23 17:35:43 +02:00
yuankunzhang a48ad6aec3 chore: update comments 2025-07-23 17:30:32 +02:00
Daniel Hofstetter 0ddeec70b4 Merge pull request #187 from yuankunzhang/year-module
feat: add a year module
2025-07-23 17:26:43 +02:00
yuankunzhang 2bb8ac8203 feat: add a year module 2025-07-23 13:53:46 +00:00
Daniel Hofstetter 5b52929c91 Merge pull request #186 from yuankunzhang/improve-literal-date-parser
feat: improve the literal date parsers
2025-07-23 14:42:54 +02:00
yuankunzhang 3d6f79d826 feat: improve the literal date parsers 2025-07-23 12:31:33 +00:00
Daniel Hofstetter 324d0bca34 Merge pull request #184 from yuankunzhang/improve-us-date-parser
Improve the `us()` date parser
2025-07-22 15:35:14 +02:00
yuankunzhang 9f75f895e0 feat: improve the us() date parser 2025-07-22 15:30:13 +02:00
Yuankun Zhang 5ba4b18655 Improve the iso1() and iso2() date parsers (#183)
* feat: improve date parsing with GNU compatibility and validation

- Add `TryFrom<(&str, u32, u32)>` implementation for `Date` struct
  with validation.
- Add comprehensive date validation for months, days, taking leap
  year into account.
- Refactor `iso1()` and `iso2()` date parsers to use the new
  validation logic.

* fix typo

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>

* remove unnecessary if condition

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>

---------

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
2025-07-22 14:49:36 +02:00
Daniel Hofstetter 5d869b5264 Merge pull request #182 from yuankunzhang/add-ctx-err
feat: add a `ctx_err` util function
2025-07-20 14:07:21 +02:00
yuankunzhang 793d18dce7 feat: add a ctx_err util function 2025-07-20 03:28:52 +00:00
Daniel Hofstetter 7182760266 Merge pull request #181 from yuankunzhang/support-leading-zeros-in-timezone-colonless
feat: support leading zeros in `timezone_colonless()`
2025-07-19 17:30:07 +02:00
yuankunzhang e7a8a52a21 feat: support leading zeros in timezone_colonless() 2025-07-19 17:18:39 +02:00
Daniel Hofstetter 0674254bc6 Merge pull request #180 from yuankunzhang/fix-time-offset
fix: time offset can only appear after time or timezone
2025-07-19 16:16:17 +02:00
yuankunzhang 8bebfab576 fix: time offset can only appear after time or timezone 2025-07-19 13:57:46 +00:00
Daniel Hofstetter 6feeddfea1 chore: remove unnecessary Option as return value 2025-07-17 10:12:03 +02:00
Daniel Hofstetter 6499842e15 Merge pull request #178 from yuankunzhang/improve-items-mod
chore: re-organize items/mod.rs
2025-07-17 09:44:47 +02:00
yuankunzhang 949e0613d6 chore: re-organize items/mod.rs 2025-07-17 09:54:24 +08:00
Daniel Hofstetter 6e6ffc2dff tests: set timezone to UTC0 in integration tests 2025-07-16 10:13:20 +02:00
Daniel Hofstetter 67364fc6ba Merge pull request #174 from yuankunzhang/exclusive-timestamp
fix: timestamp cannot be combined with other items
2025-07-16 10:09:47 +02:00
Daniel Hofstetter d92f5eeb27 Merge pull request #176 from uutils/renovate/winnow-0.x-lockfile
fix(deps): update rust crate winnow to v0.7.12
2025-07-16 10:07:03 +02:00
renovate[bot] 5c4a60fadf fix(deps): update rust crate winnow to v0.7.12 2025-07-16 08:01:37 +00:00
Daniel Hofstetter 45bda33a6b Merge pull request #175 from uutils/renovate/libfuzzer-sys-0.x-lockfile
fix(deps): update rust crate libfuzzer-sys to v0.4.10
2025-07-04 08:06:44 +02:00
renovate[bot] 64069cdbf2 fix(deps): update rust crate libfuzzer-sys to v0.4.10 2025-07-03 23:25:47 +00:00
yuankunzhang 4d6568b326 fix: timestamp cannot be combined with other items 2025-07-01 13:40:04 +00:00
Daniel Hofstetter f5c1a5e399 Merge pull request #173 from yuankunzhang/date-time-builder
refactor: introduce a builder and simplify the date time composition
2025-07-01 14:52:49 +02:00
yuankunzhang 706ab45937 refactor: introduce a builder and simplify the date time composition 2025-06-30 13:51:25 +00:00
Daniel Hofstetter 4d9afba2d4 Merge pull request #172 from yuankunzhang/minor-refactor
refactor: put the epoch and timezone module into separate files
2025-06-30 15:10:34 +02:00
yuankunzhang 05677a803d refactor: put the epoch and timezone module into separate files 2025-06-30 13:05:10 +00:00
Daniel Hofstetter 98d4931b53 Merge pull request #169 from yuankunzhang/hour-with-meridiem
fix: hour must be greater than 0 when meridiem is specified
2025-06-28 17:03:48 +02:00
yuankunzhang 0b5d3ffdf3 fix: hour must be greater than 0 when meridiem is specified 2025-06-28 16:48:09 +02:00
Daniel Hofstetter 24fa78a927 Merge pull request #168 from yuankunzhang/nanoseconds
feat: support nanoseconds
2025-06-28 16:45:22 +02:00
yuankunzhang c0ada32ce1 feat: support nanoseconds 2025-06-28 16:30:24 +02:00
Daniel Hofstetter b5b1fd2b5e Merge pull request #171 from yuankunzhang/check-overflows
fix: check overflows during date time composition
2025-06-28 16:18:41 +02:00
yuankunzhang 8d78de85a5 fix: check overflows during date time composition 2025-06-27 15:24:00 +00:00
Sylvestre Ledru 33ac3b75cc Merge pull request #170 from cakebaker/clippy_fix_warning
clippy: fix warning from `uninlined_format_args`
2025-06-27 09:54:14 +02:00
Daniel Hofstetter 832942c019 clippy: fix warning from uninlined_format_args 2025-06-27 09:45:11 +02:00
Daniel Hofstetter e031c8ce49 Merge pull request #167 from Maximkaaa/integration_tests
test: Integration tests for time parsing
2025-06-26 16:59:28 +02:00
Maxim 856a13ddb2 test: Integration tests for date parsing 2025-06-25 05:15:41 +00:00
Sylvestre Ledru f1f2c47120 Merge pull request #163 from cakebaker/tests_fix_typos
tests: fix typos
2025-06-24 14:47:21 +02:00
Sylvestre Ledru 2c0fa5a264 Merge pull request #164 from cakebaker/tests_add_license_header
tests: add license header to integration tests
2025-06-24 14:47:08 +02:00
Daniel Hofstetter 937572d557 tests: add license header to integration tests 2025-06-24 14:43:56 +02:00
Daniel Hofstetter a7d9d1ab59 tests: fix typos
aphabetical -> alphabetical, ommititing -> omitting
2025-06-24 13:55:37 +02:00
Sylvestre Ledru 3be37c4d82 Merge pull request #161 from Maximkaaa/integration_tests
Integration tests for date parsing
2025-06-24 12:36:55 +02:00
Daniel Hofstetter 95897465b2 Merge pull request #159 from yuankunzhang/weekday-optional-comma
fix: weekday may be followed by an optional comma
2025-06-21 07:42:03 +02:00
Maxim 7260b0ae73 test: Integration tests for date parsing 2025-06-20 09:16:48 +00:00
Maxim 92daee09de chore: Remove empty source file 2025-06-20 15:31:40 +09:00
yuankunzhang 73f69b1da0 fix: weekday may be followed by an optional comma 2025-06-17 22:08:35 +08:00
Daniel Hofstetter 30cb26d75c Merge pull request #157 from yuankunzhang/simplify-mod-rs
Simplify items/mod.rs by moving the fundamental combinators to their own module
2025-06-15 17:04:49 +02:00
yuankunzhang 6e64f21967 refactor: move fundamental combinators from mod.rs to the primitive module 2025-06-15 21:15:28 +08:00
yuankunzhang 9b67307512 fix: relative date time handling 2025-06-15 21:13:31 +08:00
Sylvestre Ledru 76d8f17350 Merge pull request #155 from yuankunzhang/main
fix: use custom dec_uint in ordinal parsing
2025-06-15 11:05:09 +02:00
Yuankun Zhang c05b8217e0 Merge branch 'main' into main 2025-06-15 12:22:16 +08:00
yuankunzhang 41a53a2610 fix: use custom dec_uint in ordinal parsing 2025-06-15 12:21:07 +08:00
Daniel Hofstetter 03cff2ce1e Merge pull request #150 from yuankunzhang/minor-fix
fix: check whether the parsed time contains timezone
2025-06-14 15:10:44 +02:00
Daniel Hofstetter 12f73c3d57 Merge pull request #153 from uutils/renovate/winnow-0.x-lockfile
fix(deps): update rust crate winnow to v0.7.11
2025-06-10 18:47:50 +02:00
renovate[bot] c694587fdd fix(deps): update rust crate winnow to v0.7.11 2025-06-10 16:41:00 +00:00
yuankunzhang 3fe06c0b94 fix: check whether the parsed time contains timezone 2025-06-04 16:42:32 +08:00
Sylvestre Ledru 1a6a6f688a Merge pull request #143 from yuankunzhang/main
use proper error mode in items::parse()
2025-06-04 09:11:30 +02:00
yuankunzhang 294bd5eeb2 use proper error mode in items::parse() 2025-05-28 23:21:41 +08:00
yuankunzhang 60745300ad use a custom float parser 2025-05-28 23:19:10 +08:00
Sylvestre Ledru 1552608bce Merge pull request #145 from yuankunzhang/fallible-offset-conversion
fallible offset conversion
2025-05-26 18:08:50 +02:00
Sylvestre Ledru ee9beb07d3 prepare release 0.10.0 2025-05-26 16:50:31 +02:00
Daniel Hofstetter 6f073addf5 Merge pull request #146 from drinkcat/fix-tests
Fix/improve tests
2025-05-26 13:59:44 +02:00
Nicolas Boichat a9676626be src/items: Fix timezone tests
The current tests look wrong ("%Z" shouldn't print an offset, but
this accidentally works due to the way the parsing logic operates),
fix that.
2025-05-25 21:34:22 +02:00
Nicolas Boichat da0af495e8 test: Replace %+ format with explicit one
%+ is an extension that jiff doesn't support (if we still want to
convert parse_datetime in the future, and since these are tests,
it's probably better to use an explicit format.
2025-05-25 21:34:22 +02:00
Nicolas Boichat e00555cd96 src/lib: Add end-to-end timezone test
Also make the offset test a little bit smarter.
2025-05-25 21:34:22 +02:00
yuankunzhang ab91b2b6d7 fallible offset conversion 2025-05-20 21:30:35 +08:00
Terts Diepraam cc7c143b21 Merge pull request #141 from yuankunzhang/main
Upgrade winnow to 0.7.10
2025-05-19 15:05:43 +02:00
Daniel Hofstetter 7721ae2cd8 Merge pull request #142 from yanganto/msrv
Specify MSRV 1.71.1
2025-05-14 16:06:27 +02:00
Antonio Yang 557c3a3ae3 Specify MSRV 1.71.1 2025-05-14 00:59:47 +08:00
yuankunzhang bea8ec2641 upgrade winnow to 0.7.10 2025-05-12 19:46:33 +08:00
Terts Diepraam 852e49e224 Merge pull request #138 from yuankunzhang/code-cleanup
Code cleanup
2025-05-09 13:31:02 +02:00
yuankunzhang 2390ef0b25 code cleanup 2025-05-09 18:55:10 +08:00
Terts Diepraam 77156f46d2 Merge pull request #137 from yuankunzhang/replace-the-regex-parser
Replace the regex parser with winnow-based parse
2025-05-09 11:35:48 +02:00
yuankunzhang bc6b70bc9e code fixup to pass all tests 2025-05-07 21:54:42 +08:00
Daniel Hilst 2479c61dd5 Find a compatibility intersection between chrono and GNU 2025-04-30 19:53:34 +08:00
Daniel Hilst 4c3d2221da Restore the old API and add the missing combinators 2025-04-30 19:53:34 +08:00
Terts Diepraam 07d4b80ea5 start parsing date with winnow 2025-04-30 19:53:34 +08:00
Daniel Hofstetter b481a2bdec Merge pull request #136 from uutils/renovate/chrono-0.x-lockfile
fix(deps): update rust crate chrono to v0.4.41
2025-04-29 18:28:41 +02:00
renovate[bot] a387463373 fix(deps): update rust crate chrono to v0.4.41 2025-04-29 15:59:06 +00:00
Daniel Hofstetter b6795d9b2e Merge pull request #134 from dan-hipschman/handle-uppercase
Allow uppercase for words like "today", "next", etc.
2025-04-26 17:56:24 +02:00
Dan Hipschman 3bdafeb2cf Allow uppercase for words like "today", "next", etc. 2025-04-26 08:07:10 -07:00
Daniel Hofstetter b9630d4bd3 Merge pull request #128 from dan-hipschman/parse-relative-weekdays
Parse relative weekdays like "next monday"
2025-04-25 09:24:49 +02:00
Dan Hipschman 65fb9140d2 Parse relative weekdays like "next monday" 2025-04-22 10:00:13 -07:00
Sylvestre Ledru 9b22a18bce Merge pull request #130 from cakebaker/fix_some_clippy_pedantic_issues
clippy: fix some warnings from clippy::pedantic
2025-04-22 10:42:20 +02:00
Daniel Hofstetter 28b788a300 clippy: fix warnings from redundant_else lint 2025-04-22 10:34:39 +02:00
Daniel Hofstetter f6b6b3e4ea clippy: fix warning from manual_let_else lint 2025-04-22 10:31:02 +02:00
Daniel Hofstetter 1a93bb4e3e clippy: fix warning from needless_continue lint 2025-04-22 10:27:13 +02:00
Daniel Hofstetter 9fc966308e clippy: remove unnecessary semicolon 2025-04-22 10:12:20 +02:00
Sylvestre Ledru 7119769488 Merge pull request #121 from cakebaker/rename_const
Rename `TWELVEHOUR` to `TWELVE_HOUR`
2025-04-22 09:56:39 +02:00
Daniel Hofstetter 46a82b70b3 Rename TWELVEHOUR to TWELVE_HOUR 2025-04-22 09:52:19 +02:00
Dan Hipschman e7ed14e8a4 Allow spaces between +/- and number (#129)
* Allow spaces between +/- and number

* Remove space to make fmt job pass

---------

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
2025-04-22 09:44:09 +02:00
Daniel Hofstetter 9184428407 Merge pull request #126 from uutils/renovate/chrono-0.x-lockfile
Update Rust crate chrono to v0.4.40
2025-02-26 13:44:20 +01:00
renovate[bot] 25825df8b7 Update Rust crate chrono to v0.4.40 2025-02-26 11:56:48 +00:00
Sylvestre Ledru 0dbce58014 Merge pull request #120 from cakebaker/readme_fix_sentence
README.md: fix incorrect sentence
2025-02-17 20:31:53 +01:00
Daniel Hofstetter 0c0a054f70 README.md: fix incorrect sentence 2025-02-17 14:51:34 +01:00
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
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
Sylvestre Ledru 70d3e53604 new release 2023-06-06 11:46:45 +02:00
Sylvestre Ledru 1ba787d1c3 Merge pull request #17 from cakebaker/downgrade_libc
Downgrade libc from 0.2.145 to 0.2.144
2023-06-06 08:02:47 +02:00
Daniel Hofstetter 33cefd7db2 Downgrade libc from 0.2.145 to 0.2.144 2023-06-06 07:49:14 +02:00
Sylvestre Ledru d0d913e161 Merge pull request #16 from uutils/renovate/regex-1.x
Update Rust crate regex to 1.8.4
2023-06-05 19:40:31 +02:00
renovate[bot] c784a721ee Update Rust crate regex to 1.8.4 2023-06-05 16:52:25 +00: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
Sylvestre Ledru 03c8e81a10 Merge pull request #15 from Redfire75369/patch-1
Added Syntax Highlighting to Example in ReadME
2023-06-05 14:32:06 +02:00
Redfire 7547f31b76 Added Syntax Highlighting to Example in ReadME 2023-06-05 20:22:57 +08:00
Sylvestre Ledru 1c32733f38 Merge pull request #14 from cakebaker/remove_time_crate
Adapt chrono settings to remove old time crate
2023-06-05 09:53:01 +02:00
Daniel Hofstetter 5278394b7c Adapt chrono settings to remove old time crate 2023-06-05 09:18:54 +02: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
Sylvestre Ledru e5d7fbcece We moved from time to chrono 2023-06-03 16:39:58 +02:00
Sylvestre Ledru 060998e90e refresh the fuzz lock 2023-06-03 16:38:42 +02:00
Sylvestre Ledru a272f55948 update of the doc 2023-06-03 16:38:21 +02:00
Sylvestre Ledru 9c5da7959c new release 2023-06-03 16:34:51 +02:00
Sylvestre Ledru a60f1d89f5 Merge pull request #11 from sylvestre/chrono
Port the crate to use chrono instead of time
2023-06-03 16:33:51 +02:00
Sylvestre Ledru 1d78cc5a3f Simplify the check 2023-06-03 16:31:37 +02:00
Sylvestre Ledru 6ba55a075b Fix the various warnings 2023-06-03 14:38:30 +02:00
Sylvestre Ledru b439960eb1 Port the crate to use chrono instead of time 2023-06-03 13:14:00 +02:00
Sylvestre Ledru f1a15ab2b3 Merge pull request #8 from uutils/renovate/regex-1.x
Update Rust crate regex to v1.8.3
2023-05-29 16:09:02 +02:00
Sylvestre Ledru 0bd4dd716a Merge pull request #7 from uutils/renovate/rand-0.x
Update Rust crate rand to 0.8.5
2023-05-29 15:58:18 +02:00
renovate[bot] 674909c0f9 Update Rust crate regex to v1.8.3 2023-05-29 13:33:54 +00:00
renovate[bot] 57bcf7f433 Update Rust crate rand to 0.8.5 2023-05-29 13:33:48 +00:00
Sylvestre Ledru 2e80358014 Merge pull request #6 from uutils/renovate/configure
Configure Renovate
2023-05-29 15:31:52 +02:00
renovate[bot] 15fb185d95 Add renovate.json 2023-05-29 00:16:49 +00:00
Sylvestre Ledru e64c163f47 adjust the license for Debian 2023-05-27 09:43:52 +02:00
Sylvestre Ledru 86378bc326 version 0.2.1 2023-05-12 13:58:33 +02:00
Sylvestre Ledru 6a52551dea Merge pull request #5 from cakebaker/simplify_test
Simplify tests
2023-05-12 13:54:39 +02:00
Sylvestre Ledru 21e7e01fc8 Merge pull request #4 from sylvestre/reverse
Reverse the two functions
2023-05-12 13:54:31 +02:00
Daniel Hofstetter 1359dc9349 Simplify tests 2023-05-12 09:59:31 +02:00
Sylvestre Ledru daec32d5c4 describe the error 2023-05-11 22:15:29 +02:00
Sylvestre Ledru 7a3d0bafca Reverse the two functions 2023-05-11 22:15:07 +02:00
Sylvestre Ledru 537df73bdd Fix a doc warning 2023-05-11 21:57:28 +02:00
Daniel Hofstetter 3e4c6e3b6c Add .gitignore (#3) 2023-05-11 17:29:08 +02:00
31 changed files with 5329 additions and 692 deletions
+54 -55
View File
@@ -4,6 +4,7 @@ name: Basic CI
env:
CARGO_TERM_COLOR: always
RUST_MIN_SRV: "1.71.1"
jobs:
check:
@@ -13,15 +14,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@v6
- uses: dtolnay/rust-toolchain@stable
- run: cargo check
test:
name: cargo test
@@ -30,31 +25,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@v6
- 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@v6
- 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 +45,23 @@ 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@v6
- uses: dtolnay/rust-toolchain@stable
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
- run: cargo clippy --all-targets -- -D warnings
min_version:
name: Minimum Supported Rust Version
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
command: clippy
args: -- -D warnings
toolchain: ${{ env.RUST_MIN_SRV }}
- run: cargo test
coverage:
name: Code Coverage
@@ -86,7 +74,7 @@ jobs:
- { os: macos-latest , features: macos }
- { os: windows-latest , features: windows }
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Initialize workflow variables
id: vars
shell: bash
@@ -101,28 +89,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,17 +132,34 @@ 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
fail_ci_if_error: false
fuzz:
name: Run the fuzzers
runs-on: ubuntu-latest
env:
RUN_FOR: 60
steps:
- uses: actions/checkout@v6
- 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
+1
View File
@@ -0,0 +1 @@
target/
Generated
+356 -24
View File
@@ -4,32 +4,239 @@ version = 3
[[package]]
name = "aho-corasick"
version = "1.0.1"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "humantime_to_duration"
version = "0.1.4"
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"regex",
"time",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "memchr"
version = "2.5.0"
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "indexmap"
version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "jiff"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-sys",
]
[[package]]
name = "jiff-static"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jiff-tzdb"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
[[package]]
name = "jiff-tzdb-platform"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
dependencies = [
"jiff-tzdb",
]
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "parse_datetime"
version = "0.13.3"
dependencies = [
"jiff",
"num-traits",
"rstest",
"winnow",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"portable-atomic",
]
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.8.1"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
@@ -38,28 +245,153 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.1"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "serde"
version = "1.0.160"
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "time"
version = "0.3.20"
name = "rstest"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
dependencies = [
"serde",
"time-core",
"futures-timer",
"futures-util",
"rstest_macros",
]
[[package]]
name = "time-core"
version = "0.1.0"
name = "rstest_macros"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
dependencies = [
"cfg-if",
"glob",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn",
"unicode-ident",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml_datetime"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.23.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9"
dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
dependencies = [
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"memchr",
]
+11 -6
View File
@@ -1,12 +1,17 @@
[package]
name = "humantime_to_duration"
description = " parsing human-readable relative time strings and converting them to a Duration"
version = "0.2.0"
name = "parse_datetime"
description = "parsing human-readable time strings and converting them to a DateTime"
version = "0.13.3"
edition = "2021"
license = "MIT"
repository = "https://github.com/uutils/humantime_to_duration"
repository = "https://github.com/uutils/parse_datetime"
readme = "README.md"
rust-version = "1.71.1"
[dependencies]
regex = "1.7"
time = "0.3.20"
winnow = "0.7.10"
num-traits = "0.2.19"
jiff = { version = "0.2.15", default-features = false, features = ["tz-system", "tzdb-bundle-platform", "tzdb-zoneinfo"] }
[dev-dependencies]
rstest = "0.26"
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 uutils
Copyright (c) 2023 Sylvestre Ledru and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+46 -29
View File
@@ -1,46 +1,55 @@
# 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 jiff's `Zoned` object.
## 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").
- Allows for chaining time units (e.g., "1 hour 2 minutes" or "2 days 2 hours ago").
- Calculate durations relative to a specified date.
- Relies on Jiff
## Usage
Add this to your `Cargo.toml`:
Add `parse_datetime` to your `Cargo.toml` with:
```toml
[dependencies]
humantime_to_duration = "0.1.1"
```
cargo add parse_datetime
```
Then, import the crate and use the `from_str` and `from_str_at_date` functions:
```
use humantime_to_duration::{from_str, from_str_at_date};
use time::Duration;
Then, import the crate and use the `parse_datetime_at_date` function:
let duration = from_str("+3 days");
assert_eq!(duration.unwrap(), Duration::days(3));
```rs
use jiff::{ToSpan, Zoned};
use parse_datetime::parse_datetime_at_date;
let now = Zoned::now();
let after = parse_datetime_at_date(now.clone(), "+3 days");
let today = OffsetDateTime::now_utc().date();
let yesterday = today - Duration::days(1);
assert_eq!(
from_str_at_date(yesterday, "2 days").unwrap(),
Duration::days(1)
now.checked_add(3.days()).unwrap(),
after.unwrap()
);
```
For DateTime parsing, import the `parse_datetime` function:
```rs
use jiff::{civil::{date, time} ,Zoned};
use parse_datetime::parse_datetime;
let dt = parse_datetime("2021-02-14 06:37:47");
assert_eq!(dt.unwrap(), Zoned::now().with().date(date(2021, 2, 14)).time(time(6, 37, 47, 0)).build().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")
@@ -48,28 +57,36 @@ The `from_str` and `from_str_at_date` functions support the following formats fo
- "yesterday"
- "tomorrow"
- use "ago" for the past
- combined units with "and" or "," (e.g., "2 years and 1 month", "1 day, 2 hours" or "2 weeks 1 second")
- use "next" or "last" with `unit` (e.g., "next week", "last year")
- 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(Zoned)` - If the input string can be parsed as a `Zoned` object
- `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
+197 -102
View File
@@ -1,182 +1,277 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
dependencies = [
"memchr",
]
version = 4
[[package]]
name = "arbitrary"
version = "1.3.0"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "cc"
version = "1.0.79"
version = "1.2.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "fuzz_from_str"
version = "0.1.0"
name = "find-msvc-tools"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]]
name = "fuzz_parse_datetime"
version = "0.2.0"
dependencies = [
"humantime_to_duration",
"libfuzzer-sys",
"rand",
"regex",
"time",
"parse_datetime",
]
[[package]]
name = "getrandom"
version = "0.2.9"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"wasi",
"r-efi",
"wasip2",
]
[[package]]
name = "humantime_to_duration"
version = "0.1.0"
name = "jiff"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35"
dependencies = [
"regex",
"time",
"jiff-static",
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-sys",
]
[[package]]
name = "jiff-static"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jiff-tzdb"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
[[package]]
name = "jiff-tzdb-platform"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
dependencies = [
"jiff-tzdb",
]
[[package]]
name = "jobserver"
version = "0.1.26"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom",
"libc",
]
[[package]]
name = "libc"
version = "0.2.142"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libfuzzer-sys"
version = "0.4.6"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "once_cell"
version = "1.17.1"
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"autocfg",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
name = "parse_datetime"
version = "0.13.2"
dependencies = [
"ppv-lite86",
"rand_core",
"jiff",
"num-traits",
"winnow",
]
[[package]]
name = "rand_core"
version = "0.6.4"
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"getrandom",
"portable-atomic",
]
[[package]]
name = "regex"
version = "1.8.1"
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.1"
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "serde"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
[[package]]
name = "time"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+7 -10
View File
@@ -1,22 +1,19 @@
[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.4"
libfuzzer-sys = "0.4"
regex = "1.8.1"
time = "0.3.20"
libfuzzer-sys = "0.4.7"
[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);
});
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
+321
View File
@@ -0,0 +1,321 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use jiff::{civil, Span, Zoned};
use super::{date, epoch, error, offset, relative, time, weekday, year, Item};
/// The builder is used to construct a DateTime object from various components.
/// The parser creates a `DateTimeBuilder` object with the parsed components,
/// but without the baseline date and time. So you normally need to set the base
/// date and time using the `set_base()` method before calling `build()`, or
/// leave it unset to use the current date and time as the base.
#[derive(Debug, Default)]
pub(crate) struct DateTimeBuilder {
base: Option<Zoned>,
timestamp: Option<epoch::Timestamp>,
date: Option<date::Date>,
time: Option<time::Time>,
weekday: Option<weekday::Weekday>,
offset: Option<offset::Offset>,
timezone: Option<jiff::tz::TimeZone>,
relative: Vec<relative::Relative>,
}
impl DateTimeBuilder {
pub(super) fn new() -> Self {
Self::default()
}
/// Sets the base date and time for the builder. If not set, the current
/// date and time will be used.
pub(super) fn set_base(mut self, base: Zoned) -> Self {
self.base = Some(base);
self
}
/// Sets the timezone rule for the builder.
///
/// By default, the builder uses the time zone rules indicated by the `TZ`
/// environment variable, or the system default rules if `TZ` is not set.
/// This method allows overriding the time zone rules.
fn set_timezone(mut self, tz: jiff::tz::TimeZone) -> Result<Self, &'static str> {
if self.timezone.is_some() {
return Err("timezone rule cannot appear more than once");
}
self.timezone = Some(tz);
Ok(self)
}
/// Sets a timestamp value. Timestamp values are exclusive to other date/time
/// items (date, time, weekday, timezone, relative adjustments).
pub(super) fn set_timestamp(mut self, ts: epoch::Timestamp) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot appear more than once");
} else if self.date.is_some()
|| self.time.is_some()
|| self.weekday.is_some()
|| self.offset.is_some()
|| !self.relative.is_empty()
{
return Err("timestamp cannot be combined with other date/time items");
}
self.timestamp = Some(ts);
Ok(self)
}
fn set_date(mut self, date: date::Date) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot be combined with other date/time items");
} else if self.date.is_some() {
return Err("date cannot appear more than once");
}
self.date = Some(date);
Ok(self)
}
fn set_time(mut self, time: time::Time) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot be combined with other date/time items");
} else if self.time.is_some() {
return Err("time cannot appear more than once");
} else if self.offset.is_some() && time.offset.is_some() {
return Err("time offset and timezone are mutually exclusive");
}
self.time = Some(time);
Ok(self)
}
fn set_weekday(mut self, weekday: weekday::Weekday) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot be combined with other date/time items");
} else if self.weekday.is_some() {
return Err("weekday cannot appear more than once");
}
self.weekday = Some(weekday);
Ok(self)
}
fn set_offset(mut self, timezone: offset::Offset) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot be combined with other date/time items");
} else if self.offset.is_some()
|| self.time.as_ref().and_then(|t| t.offset.as_ref()).is_some()
{
return Err("time offset cannot appear more than once");
}
self.offset = Some(timezone);
Ok(self)
}
fn push_relative(mut self, relative: relative::Relative) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot be combined with other date/time items");
}
self.relative.push(relative);
Ok(self)
}
/// Sets a pure number that can be interpreted as either a year or time
/// depending on the current state of the builder.
///
/// If a date is already set but lacks a year, the number is interpreted as
/// a year. Otherwise, it's interpreted as a time in HHMM, HMM, HH, or H
/// format.
fn set_pure(mut self, pure: String) -> Result<Self, &'static str> {
if self.timestamp.is_some() {
return Err("timestamp cannot be combined with other date/time items");
}
if let Some(date) = self.date.as_mut() {
if date.year.is_none() {
date.year = Some(year::year_from_str(&pure)?);
return Ok(self);
}
}
let (mut hour_str, mut minute_str) = match pure.len() {
1..=2 => (pure.as_str(), "0"),
3..=4 => pure.split_at(pure.len() - 2),
_ => {
return Err("pure number must be 1-4 digits when interpreted as time");
}
};
let hour = time::hour24(&mut hour_str).map_err(|_| "invalid hour in pure number")?;
let minute = time::minute(&mut minute_str).map_err(|_| "invalid minute in pure number")?;
let time = time::Time {
hour,
minute,
..Default::default()
};
self.set_time(time)
}
/// Build a `Zoned` object from the pieces accumulated in this builder.
///
/// Resolution order (mirrors GNU `date` semantics):
///
/// 1. Base instant.
/// - a. If `self.base` is provided, start with it.
/// - b. Else if a `timezone` rule is present, start with "now" in that
/// timezone.
/// - c. Else start with current system local time.
///
/// 2. Absolute timestamp override.
/// - a. If `self.timestamp` is set, it fully determines the result.
///
/// 3. Time of day truncation.
/// - a. If any of date, time, weekday, offset, timezone is set, zero the
/// time of day to 00:00:00 before applying fields.
///
/// 4. Fieldwise resolution (applied to the base instant).
/// - a. Apply date. If year is absent in the parsed date, inherit the year
/// from the base instant.
/// - b. Apply time. If time carries an explicit numeric offset, apply the
/// offset before setting time.
/// - c. Apply weekday (e.g., "next Friday" or "last Monday").
/// - d. Apply relative adjustments (e.g., "+3 days", "-2 months").
/// - e. Apply final fixed offset if present.
pub(super) fn build(self) -> Result<Zoned, error::Error> {
// 1. Choose the base instant.
let base = match (self.base, &self.timezone) {
(Some(b), _) => b,
(None, Some(tz)) => jiff::Timestamp::now().to_zoned(tz.clone()),
(None, None) => Zoned::now(),
};
// 2. Absolute timestamp override everything else.
if let Some(ts) = self.timestamp {
let ts = jiff::Timestamp::try_from(ts)?;
return Ok(ts.to_zoned(base.offset().to_time_zone()));
}
// 3. Determine whether to truncate the time of day.
let need_midnight = self.date.is_some()
|| self.time.is_some()
|| self.weekday.is_some()
|| self.offset.is_some()
|| self.timezone.is_some();
let mut dt = if need_midnight {
base.with().time(civil::time(0, 0, 0, 0)).build()?
} else {
base
};
// 4a. Apply date.
if let Some(date) = self.date {
let d: civil::Date = if date.year.is_some() {
date.try_into()?
} else {
date.with_year(dt.date().year() as u16).try_into()?
};
dt = dt.with().date(d).build()?;
}
// 4b. Apply time.
if let Some(time) = self.time.clone() {
if let Some(offset) = &time.offset {
dt = dt.datetime().to_zoned(offset.try_into()?)?;
}
let t: civil::Time = time.try_into()?;
dt = dt.with().time(t).build()?;
}
// 4c. Apply weekday.
if let Some(weekday::Weekday { mut offset, day }) = self.weekday {
if self.time.is_none() {
dt = dt.with().time(civil::time(0, 0, 0, 0)).build()?;
}
let target = day.into();
// If the current day is not the target day, we need to adjust
// the x value to ensure we find the correct day.
//
// Consider this:
// Assuming today is Monday, next Friday is actually THIS Friday;
// but next Monday is indeed NEXT Monday.
if dt.date().weekday() != target && offset > 0 {
offset -= 1;
}
// Calculate the delta to the target day.
//
// Assuming today is Thursday, here are some examples:
//
// Example 1: last Thursday (x = -1, day = Thursday)
// delta = (3 - 3) % 7 + (-1) * 7 = -7
//
// Example 2: last Monday (x = -1, day = Monday)
// delta = (0 - 3) % 7 + (-1) * 7 = -3
//
// Example 3: next Monday (x = 1, day = Monday)
// delta = (0 - 3) % 7 + (0) * 7 = 4
// (Note that we have adjusted the x value above)
//
// Example 4: next Thursday (x = 1, day = Thursday)
// delta = (3 - 3) % 7 + (1) * 7 = 7
let delta = (target.since(civil::Weekday::Monday) as i32
- dt.date().weekday().since(civil::Weekday::Monday) as i32)
.rem_euclid(7)
+ offset.checked_mul(7).ok_or("multiplication overflow")?;
dt = dt.checked_add(Span::new().try_days(delta)?)?;
}
// 4d. Apply relative adjustments.
for rel in self.relative {
dt = dt.checked_add::<Span>(if let relative::Relative::Months(x) = rel {
// *NOTE* This is done in this way to conform to GNU behavior.
let days = dt.date().last_of_month().day() as i32;
Span::new().try_days(days.checked_mul(x).ok_or("multiplication overflow")?)?
} else {
rel.try_into()?
})?;
}
// 4e. Apply final fixed offset.
if let Some(offset) = self.offset {
let (offset, hour_adjustment) = offset.normalize();
dt = dt.checked_add(Span::new().hours(hour_adjustment))?;
dt = dt.datetime().to_zoned((&offset).try_into()?)?;
}
Ok(dt)
}
}
impl TryFrom<Vec<Item>> for DateTimeBuilder {
type Error = &'static str;
fn try_from(items: Vec<Item>) -> Result<Self, Self::Error> {
let mut builder = DateTimeBuilder::new();
for item in items {
builder = match item {
Item::Timestamp(ts) => builder.set_timestamp(ts)?,
Item::DateTime(dt) => builder.set_date(dt.date)?.set_time(dt.time)?,
Item::Date(d) => builder.set_date(d)?,
Item::Time(t) => builder.set_time(t)?,
Item::Weekday(weekday) => builder.set_weekday(weekday)?,
Item::Offset(offset) => builder.set_offset(offset)?,
Item::Relative(rel) => builder.push_relative(rel)?,
Item::TimeZone(tz) => builder.set_timezone(tz)?,
Item::Pure(pure) => builder.set_pure(pure)?,
}
}
Ok(builder)
}
}
+76
View File
@@ -0,0 +1,76 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//! Parse an ISO 8601 date and time item
//!
//! The GNU docs state:
//!
//! > The ISO 8601 date and time of day extended format consists of an ISO 8601
//! > date, a T character separator, and an ISO 8601 time of day. This format
//! > is also recognized if the T is replaced by a space.
//! >
//! > In this format, the time of day should use 24-hour notation. Fractional
//! > seconds are allowed, with either comma or period preceding the fraction.
//! > ISO 8601 fractional minutes and hours are not supported. Typically, hosts
//! > support nanosecond timestamp resolution; excess precision is silently
//! > discarded.
use winnow::{
combinator::{alt, trace},
seq, ModalResult, Parser,
};
use crate::items::space;
use super::{date, primitive::s, time};
#[derive(PartialEq, Debug, Clone, Default)]
pub(crate) struct DateTime {
pub(crate) date: date::Date,
pub(crate) time: time::Time,
}
pub(crate) fn parse(input: &mut &str) -> ModalResult<DateTime> {
seq!(DateTime {
date: trace("iso_date", alt((date::iso1, date::iso2))),
// Note: the `T` is lowercased by the main parse function
_: alt((s('t').void(), (' ', space).void())),
time: trace("iso_time", time::iso),
})
.parse_next(input)
}
#[cfg(test)]
mod tests {
use super::{parse, DateTime};
use crate::items::{date::Date, time::Time};
#[test]
fn some_date() {
let reference = Some(DateTime {
date: Date {
day: 10,
month: 10,
year: Some(2022),
},
time: Time {
hour: 10,
minute: 10,
second: 55,
nanosecond: 0,
offset: None,
},
});
for mut s in [
"2022-10-10t10:10:55",
"2022-10-10 10:10:55",
"2022-10-10 t 10:10:55",
"2022-10-10 10:10:55",
"2022-10-10 (A comment!) t 10:10:55",
"2022-10-10 (A comment!) 10:10:55",
] {
let old_s = s.to_owned();
assert_eq!(parse(&mut s).ok(), reference, "Failed string: {old_s}")
}
}
}
+640
View File
File diff suppressed because it is too large Load Diff
+137
View File
@@ -0,0 +1,137 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//! Parse a timestamp item.
//!
//! From the GNU docs:
//!
//! > If you precede a number with @, it represents an internal timestamp as
//! > a count of seconds. The number can contain an internal decimal point
//! > (either . or ,); any excess precision not supported by the internal
//! > representation is truncated toward minus infinity. Such a number cannot
//! > be combined with any other date item, as it specifies a complete
//! > timestamp.
//! >
//! > On most hosts, these counts ignore the presence of leap seconds. For
//! > example, on most hosts @1483228799 represents 2016-12-31 23:59:59 UTC,
//! > @1483228800 represents 2017-01-01 00:00:00 UTC, and there is no way to
//! > represent the intervening leap second 2016-12-31 23:59:60 UTC.
use winnow::{
ascii::digit1,
combinator::{opt, preceded},
token::one_of,
ModalResult, Parser,
};
use super::primitive::{dec_uint, plus_or_minus, s};
/// Represents a timestamp with nanosecond accuracy.
///
/// # Invariants
///
/// - `nanosecond` is always in the range of `0..1_000_000_000`.
/// - Negative timestamps are represented by a negative `second` value and a
/// positive `nanosecond` value.
#[derive(Debug, PartialEq)]
pub(super) struct Timestamp {
second: i64,
nanosecond: u32,
}
impl TryFrom<Timestamp> for jiff::Timestamp {
type Error = &'static str;
fn try_from(ts: Timestamp) -> Result<Self, Self::Error> {
jiff::Timestamp::new(
ts.second,
i32::try_from(ts.nanosecond).map_err(|_| "nanosecond in timestamp exceeds i32::MAX")?,
)
.map_err(|_| "timestamp value is out of valid range")
}
}
/// Parse a timestamp in the form of `@1234567890` or `@-1234567890.12345` or
/// `@1234567890,12345`.
pub(super) fn parse(input: &mut &str) -> ModalResult<Timestamp> {
(s("@"), opt(plus_or_minus), s(sec_and_nsec))
.verify_map(|(_, sign, (sec, nsec))| {
let sec = i64::try_from(sec).ok()?;
let (second, nanosecond) = match (sign, nsec) {
(Some('-'), 0) => (-sec, 0),
// Truncate towards minus infinity.
(Some('-'), _) => ((-sec).checked_sub(1)?, 1_000_000_000 - nsec),
_ => (sec, nsec),
};
Some(Timestamp { second, nanosecond })
})
.parse_next(input)
}
/// Parse a second value in the form of `1234567890` or `1234567890.12345` or
/// `1234567890,12345`.
///
/// The first part represents whole seconds. The optional second part represents
/// fractional seconds, parsed as a nanosecond value from up to 9 digits
/// (padded with zeros on the right if fewer digits are present). If the second
/// part is omitted, it defaults to 0 nanoseconds.
pub(super) fn sec_and_nsec(input: &mut &str) -> ModalResult<(u64, u32)> {
(dec_uint, opt(preceded(one_of(['.', ',']), digit1)))
.verify_map(|(sec, opt_nsec_str)| match opt_nsec_str {
Some(nsec_str) if nsec_str.len() >= 9 => Some((sec, nsec_str[..9].parse().ok()?)),
Some(nsec_str) => {
let multiplier = 10_u32.pow(9 - nsec_str.len() as u32);
Some((sec, nsec_str.parse::<u32>().ok()?.checked_mul(multiplier)?))
}
None => Some((sec, 0)),
})
.parse_next(input)
}
#[cfg(test)]
mod tests {
use super::*;
fn ts(second: i64, nanosecond: u32) -> Timestamp {
Timestamp { second, nanosecond }
}
#[test]
fn parse_sec_and_nsec() {
for (input, expected) in [
("1234567890", (1234567890, 0)), // only seconds
("1234567890.12345", (1234567890, 123450000)), // seconds and nanoseconds, '.' as floating point
("1234567890,12345", (1234567890, 123450000)), // seconds and nanoseconds, ',' as floating point
("1234567890.1234567890123", (1234567890, 123456789)), // nanoseconds with more than 9 digits, truncated
] {
let mut s = input;
assert_eq!(sec_and_nsec(&mut s).unwrap(), expected, "{input}");
}
for input in [
".1234567890", // invalid: no leading seconds
"-1234567890", // invalid: negative input not allowed
] {
let mut s = input;
assert!(sec_and_nsec(&mut s).is_err(), "{input}");
}
}
#[test]
fn timestamp() {
for (input, expected) in [
("@1234567890", ts(1234567890, 0)), // positive seconds, no nanoseconds
("@ 1234567890", ts(1234567890, 0)), // space after '@', positive seconds, no nanoseconds
("@-1234567890", ts(-1234567890, 0)), // negative seconds, no nanoseconds
("@ -1234567890", ts(-1234567890, 0)), // space after '@', negative seconds, no nanoseconds
("@ - 1234567890", ts(-1234567890, 0)), // space after '@' and after '-', negative seconds, no nanoseconds
("@1234567890.12345", ts(1234567890, 123450000)), // positive seconds with nanoseconds, '.' as floating point
("@1234567890,12345", ts(1234567890, 123450000)), // positive seconds with nanoseconds, ',' as floating point
("@-1234567890.12345", ts(-1234567891, 876550000)), // negative seconds with nanoseconds, '.' as floating point
("@1234567890.1234567890123", ts(1234567890, 123456789)), // nanoseconds with more than 9 digits, truncated
] {
let mut s = input;
assert_eq!(parse(&mut s).unwrap(), expected, "{input}");
}
}
}
+38
View File
@@ -0,0 +1,38 @@
use std::fmt;
use winnow::error::{ContextError, ErrMode};
#[derive(Debug)]
pub(crate) enum Error {
ParseError(String),
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::ParseError(reason) => {
write!(f, "{reason}")
}
}
}
}
impl From<&'static str> for Error {
fn from(reason: &'static str) -> Self {
Error::ParseError(reason.to_owned())
}
}
impl From<ErrMode<ContextError>> for Error {
fn from(err: ErrMode<ContextError>) -> Self {
Error::ParseError(err.to_string())
}
}
impl From<jiff::Error> for Error {
fn from(err: jiff::Error) -> Self {
Error::ParseError(err.to_string())
}
}
+576
View File
File diff suppressed because it is too large Load Diff
+468
View File
@@ -0,0 +1,468 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//! Parse an offset item.
//!
//! From the GNU docs:
//!
//! > A “time zone item” specifies an international time zone, indicated by a
//! > small set of letters, e.g., UTC or Z for Coordinated Universal Time.
//! > Any included periods are ignored. By following a non-daylight-saving
//! > time zone by the string DST in a separate word (that is, separated by
//! > some white space), the corresponding daylight saving time zone may be
//! > specified. Alternatively, a non-daylight-saving time zone can be
//! > followed by a time zone correction, to add the two values. This is
//! > normally done only for UTC; for example, UTC+05:30 is equivalent to
//! > +05:30.
//! >
//! > Time zone items other than UTC and Z are obsolescent and are not
//! > recommended, because they are ambiguous; for example, EST has a
//! > different meaning in Australia than in the United States, and A has
//! > different meaning as a military time zone than as an obsolete RFC 822
//! > time zone. Instead, it's better to use unambiguous numeric time zone
//! > corrections like -0500, as described in the previous section.
//! >
//! > If neither a time zone item nor a time zone correction is supplied,
//! > timestamps are interpreted using the rules of the default time zone.
use std::fmt::Display;
use winnow::{
combinator::{alt, peek},
error::{ContextError, ErrMode},
stream::{AsChar, Stream},
token::take_while,
ModalResult, Parser,
};
use super::{
primitive::{colon, ctx_err, dec_uint, dec_uint_str, plus_or_minus, s},
relative,
};
/// Represents a time zone offset from UTC.
///
/// This struct is used to represent a time zone offset in hours and minutes,
/// with a boolean indicating whether the offset is negative (i.e., west of
/// UTC).
#[derive(PartialEq, Debug, Clone, Default)]
pub(super) struct Offset {
negative: bool,
hours: u8,
minutes: u8,
}
impl Offset {
/// Merge two timezone offsets.
///
/// Note: when parsing an offset from a string (e.g., "+08:00"), the hours
/// and minutes are validated to ensure they fall within valid bounds. In
/// contrast, merging two offsets does not perform such validation. This
/// behavior is intentional to match GNU date.
fn merge(self, offset: Offset) -> Offset {
fn combine(a: u16, neg_a: bool, b: u16, neg_b: bool) -> (u16, bool) {
if neg_a == neg_b {
(a + b, neg_a)
} else if a > b {
(a - b, neg_a)
} else {
(b - a, neg_b)
}
}
let (total_minutes, negative) = combine(
(self.hours as u16) * 60 + (self.minutes as u16),
self.negative,
(offset.hours as u16) * 60 + (offset.minutes as u16),
offset.negative,
);
let hours = (total_minutes / 60) as u8;
let minutes = (total_minutes % 60) as u8;
Offset {
negative,
hours,
minutes,
}
}
/// Normalize the offset so that the hour field is within the accepted range.
///
/// - If the hour field is less than 24, or exactly 24 with a zero minute,
/// the offset is already normalized, and the function returns the offset
/// itself along with a zero hour adjustment.
/// - Otherwise, the hour field is reduced to 23 while preserving the minute
/// field, and the function returns the normalized offset along with the
/// hour adjustment needed to reach the original offset.
pub(super) fn normalize(self) -> (Offset, i8) {
if self.hours < 24 || (self.hours == 24 && self.minutes == 0) {
return (self, 0);
}
let hour_adjustment = (self.hours as i8 - 23) * if self.negative { 1 } else { -1 };
(
Offset {
negative: self.negative,
hours: 23,
minutes: self.minutes,
},
hour_adjustment,
)
}
}
impl TryFrom<(bool, u8, u8)> for Offset {
type Error = &'static str;
fn try_from((negative, hours, minutes): (bool, u8, u8)) -> Result<Self, Self::Error> {
if hours > 24 {
return Err("timezone hour must be between 0 and 24");
}
if minutes > 60 || (hours == 24 && minutes != 0) {
return Err("timezone minute must be between 0 and 60");
}
Ok(Offset {
negative,
hours,
minutes,
})
}
}
impl TryFrom<&Offset> for jiff::tz::TimeZone {
type Error = &'static str;
fn try_from(
Offset {
negative,
hours,
minutes,
}: &Offset,
) -> Result<Self, Self::Error> {
let secs = (*hours as i32) * 3600 + (*minutes as i32) * 60;
let secs = if *negative { -secs } else { secs };
let offset = jiff::tz::Offset::from_seconds(secs).map_err(|_| "offset is invalid")?;
Ok(jiff::tz::TimeZone::fixed(offset))
}
}
impl Display for Offset {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
fmt,
"{}{:02}:{:02}",
if self.negative { "-" } else { "+" },
self.hours,
self.minutes
)
}
}
pub(super) fn parse(input: &mut &str) -> ModalResult<Offset> {
timezone_name_offset.parse_next(input)
}
/// Parse a timezone starting with `+` or `-`.
pub(super) fn timezone_offset(input: &mut &str) -> ModalResult<Offset> {
// Strings like "+8 years" are ambiguous, they can either be parsed as a
// timezone offset "+8" and a relative time "years", or just a relative time
// "+8 years". GNU date parses them the second way, so we do the same here.
//
// Return early if the input can be parsed as a relative time.
if peek(relative::parse).parse_next(input).is_ok() {
return Err(ErrMode::Backtrack(ContextError::new()));
}
alt((timezone_offset_colon, timezone_offset_colonless)).parse_next(input)
}
/// Parse a timezone by name, with an optional numeric offset appended.
fn timezone_name_offset(input: &mut &str) -> ModalResult<Offset> {
/// I'm assuming there are no timezone abbreviations with more
/// than 6 charactres
const MAX_TZ_SIZE: usize = 6;
let nextword = s(take_while(1..=MAX_TZ_SIZE, AsChar::is_alpha)).parse_next(input)?;
let tz = timezone_name_to_offset(nextword)?;
// Strings like "UTC +8 years" are ambiguous, they can either be parsed as
// "UTC+8" and "years", or "UTC" and "+8 years". GNU date parses them the
// second way, so we do the same here.
//
// Only process if the input cannot be parsed as a relative time.
if peek(relative::parse).parse_next(input).is_err() {
let start = input.checkpoint();
if let Ok(other_tz) = timezone_offset.parse_next(input) {
let new_tz = tz.merge(other_tz);
return Ok(new_tz);
};
input.reset(&start);
}
Ok(tz)
}
/// Parse a timezone offset with a colon separating hours and minutes, e.g.,
/// `+08:00`, `+8:00`, `+8:0`.
fn timezone_offset_colon(input: &mut &str) -> ModalResult<Offset> {
(plus_or_minus, s(dec_uint), s(colon), s(dec_uint))
.parse_next(input)
.and_then(|(sign, hours, _, minutes)| {
(sign == '-', hours, minutes)
.try_into()
.map_err(|e| ErrMode::Cut(ctx_err(e)))
})
}
/// Parse a timezone offset without colon, e.g., `+0800`, `+800`, `+08`, `+8`.
fn timezone_offset_colonless(input: &mut &str) -> ModalResult<Offset> {
(plus_or_minus, s(dec_uint_str))
.verify_map(|(sign, s)| {
// GNU date accepts numeric offset strings with leading zeroes. For
// example, `+000000110` is valid. In such cases, the string is
// truncated to the last four characters. Thus, `+000000110` becomes
// `+0110` (note that one leading zero is kept).
let s = if s.len() > 4 && s.trim_start_matches('0').len() <= 4 {
&s[s.len() - 4..]
} else {
s
};
// Hour and minute values are dependent on the length of the string.
// For example:
//
// - "5" -> 05:00
// - "05" -> 05:00
// - "530" -> 05:30 (the minute is the last two characters here)
// - "0530"-> 05:30
// - "0000530" -> 05:30
let (h_str, m_str) = match s.len() {
1 | 2 => (s, "0"),
3 => s.split_at(1),
4 => s.split_at(2),
_ => return None,
};
let hours = h_str.parse::<u8>().ok()?;
let minutes = m_str.parse::<u8>().ok()?;
Some((sign, hours, minutes))
})
.parse_next(input)
.and_then(|(sign, hours, minutes)| {
(sign == '-', hours, minutes)
.try_into()
.map_err(|e| ErrMode::Cut(ctx_err(e)))
})
}
/// Named timezone list.
///
/// The full list of timezones can be extracted from
/// https://www.timeanddate.com/time/zones/. GNU date only supports a subset of
/// these. We support the same subset as GNU date.
fn timezone_name_to_offset(input: &str) -> ModalResult<Offset> {
let mut offset_str = match input {
"z" => Ok("+0"),
"y" => Ok("-12"),
"x" => Ok("-11"),
"wet" => Ok("+0"),
"west" => Ok("+1"),
"wat" => Ok("+1"),
"w" => Ok("-10"),
"v" => Ok("-9"),
"utc" => Ok("+0"),
"u" => Ok("-8"),
"t" => Ok("-7"),
"sst" => Ok("-11"),
"sgt" => Ok("+8"),
"sast" => Ok("+2"),
"s" => Ok("-6"),
"r" => Ok("-5"),
"q" => Ok("-4"),
"pst" => Ok("-8"),
"pdt" => Ok("-7"),
"p" => Ok("-3"),
"o" => Ok("-2"),
"nzst" => Ok("+12"),
"nzdt" => Ok("+13"),
"nst" => Ok("-3:30"),
"ndt" => Ok("-2:30"),
"n" => Ok("-1"),
"mst" => Ok("-7"),
"msk" => Ok("+3"),
"msd" => Ok("+4"),
"mdt" => Ok("-6"),
"m" => Ok("+12"),
"l" => Ok("+11"),
"k" => Ok("+10"),
"jst" => Ok("+9"),
"ist" => Ok("+5:30"),
"i" => Ok("+9"),
"hst" => Ok("-10"),
"h" => Ok("+8"),
"gst" => Ok("+4"),
"gmt" => Ok("+0"),
"g" => Ok("+7"),
"f" => Ok("+6"),
"est" => Ok("-5"),
"eet" => Ok("+2"),
"eest" => Ok("+3"),
"edt" => Ok("-4"),
"eat" => Ok("+3"),
"e" => Ok("+5"),
"d" => Ok("+4"),
"cst" => Ok("-6"),
"clt" => Ok("-4"),
"clst" => Ok("-3"),
"cet" => Ok("+1"),
"cest" => Ok("+2"),
"cdt" => Ok("-5"),
"cat" => Ok("+2"),
"c" => Ok("+3"),
"bst" => Ok("+6"),
"brt" => Ok("-3"),
"brst" => Ok("-2"),
"b" => Ok("+2"),
"ast" => Ok("-3"),
"art" => Ok("-3"),
"akst" => Ok("-9"),
"akdt" => Ok("-8"),
"adt" => Ok("+4"),
"a" => Ok("+1"),
_ => Err(ErrMode::Backtrack(ContextError::new())),
}?;
timezone_offset(&mut offset_str)
}
#[cfg(test)]
mod tests {
use super::*;
fn off(negative: bool, hours: u8, minutes: u8) -> Offset {
Offset {
negative,
hours,
minutes,
}
}
#[test]
fn timezone_offset_with_colon() {
for (input, expected) in [
("+00:00", off(false, 0, 0)), // UTC
("-00:00", off(true, 0, 0)), // UTC
("+01:00", off(false, 1, 0)), // positive offset
("-06:00", off(true, 6, 0)), // negative offset
("+05:30", off(false, 5, 30)), // positive offset with non-zero minutes
("-03:30", off(true, 3, 30)), // negative offset with non-zero minutes
("- 06:00", off(true, 6, 0)), // space after sign
("- 06 : 00", off(true, 6, 0)), // space around colon
("+5:3", off(false, 5, 3)), // single-digit hours and single-digit minutes
("+5:03", off(false, 5, 3)), // single-digit hours
("+05:3", off(false, 5, 3)), // single-digit minutes
("+00005:00030", off(false, 5, 30)), // leading zeroes in hours and minutes
("+00:00abc", off(false, 0, 0)), // space separator can be ignored if immediately followed by alphas (GNU date behavior)
] {
let mut s = input;
assert_eq!(timezone_offset(&mut s).unwrap(), expected, "{input}");
}
for input in [
"+25:00", // invalid: hours > 24
"-23:61", // invalid: minutes > 60
"+24:01", // invalid: minutes > 0 when hours == 24
] {
let mut s = input;
assert!(timezone_offset(&mut s).is_err(), "{input}");
}
}
#[test]
fn timezone_offset_without_colon() {
for (input, expected) in [
("+0000", off(false, 0, 0)), // UTC
("-0000", off(true, 0, 0)), // UTC
("+0100", off(false, 1, 0)), // positive offset
("-0600", off(true, 6, 0)), // negative offset
("+0530", off(false, 5, 30)), // positive offset with non-zero minutes
("-0330", off(true, 3, 30)), // negative offset with non-zero minutes
("- 0330", off(true, 3, 30)), // space after sign
("+530", off(false, 5, 30)), // single-digit hours
("+05", off(false, 5, 0)), // double-digit hours and no minutes
("+5", off(false, 5, 0)), // single-digit hours and no minutes
("+00000530", off(false, 5, 30)), // leading zeroes
("+0000abc", off(false, 0, 0)), // space separator can be ignored if immediately followed by alphas (GNU date behavior)
] {
let mut s = input;
assert_eq!(timezone_offset(&mut s).unwrap(), expected, "{input}");
}
for input in [
"+2500", // invalid: hours > 24
"-2361", // invalid: minutes > 60
"+2401", // invalid: minutes > 0 when hours == 24
"+23 days", // invalid: ambiguous with relative time parsing
] {
let mut s = input;
assert!(timezone_offset(&mut s).is_err(), "{input}");
}
}
#[test]
fn timezone_name_without_offset() {
for (input, expected) in [
("utc", off(false, 0, 0)), // UTC
("gmt", off(false, 0, 0)), // UTC
("z", off(false, 0, 0)), // UTC
("west", off(false, 1, 0)), // positive offset
("cst", off(true, 6, 0)), // negative offset
("ist", off(false, 5, 30)), // positive offset with non-zero minutes
("nst", off(true, 3, 30)), // negative offset with non-zero minutes
("z123", off(false, 0, 0)), // space separator can be ignored if immediately followed by digits (GNU date behavior)
] {
let mut s = input;
assert_eq!(timezone_name_offset(&mut s).unwrap(), expected, "{input}");
}
for input in [
"abc", // invalid: non-existent timezone
"utcabc", // invalid: non-existent timezone
] {
let mut s = input;
assert!(timezone_name_offset(&mut s).is_err(), "{input}");
}
}
#[test]
fn timezone_name_with_offset() {
for (input, expected) in [
("utc+5:30", off(false, 5, 30)), // UTC with possitive offset
("utc-5:30", off(true, 5, 30)), // UTC with negative offset
("utc +5:30", off(false, 5, 30)), // space after timezone name
("utc + 5 : 30", off(false, 5, 30)), // spaces
("a+5:30", off(false, 6, 30)), // merge two positive offsets (a=+1)
("a-5:30", off(true, 4, 30)), // merge positive and negative offsets (a=+1)
("n-5:30", off(true, 6, 30)), // merge two negative offsets (n=-1)
("n+5:30", off(false, 4, 30)), // merge negative and positive offsets (n=-1)
("m+24", off(false, 36, 0)), // maximum possible positive offset (m=+12)
("y-24", off(true, 36, 0)), // maximum possible negative offset (y=-12)
] {
let mut s = input;
assert_eq!(timezone_name_offset(&mut s).unwrap(), expected, "{input}");
}
for input in [
"abc+08:00", // invalid: non-existent timezone
"utc+25", // invalid: invalid offset
"utc+23 days", // invalid: ambiguous with relative time parsing
] {
let mut s = input;
assert!(
timezone_name_offset(&mut s).is_err() || !s.is_empty(),
"{input}"
);
}
}
}
+47
View File
@@ -0,0 +1,47 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use winnow::{
ascii::alpha1,
combinator::{alt, opt},
ModalResult, Parser,
};
use super::primitive::{dec_uint, s};
pub(super) fn ordinal(input: &mut &str) -> ModalResult<i32> {
alt((text_ordinal, number_ordinal)).parse_next(input)
}
fn number_ordinal(input: &mut &str) -> ModalResult<i32> {
let sign = opt(alt(('+'.value(1), '-'.value(-1)))).map(|s| s.unwrap_or(1));
(s(sign), s(dec_uint))
.verify_map(|(s, u): (i32, u32)| {
let i: i32 = u.try_into().ok()?;
Some(s * i)
})
.parse_next(input)
}
fn text_ordinal(input: &mut &str) -> ModalResult<i32> {
s(alpha1)
.verify_map(|s: &str| {
Some(match s {
"last" => -1,
"this" => 0,
"next" | "first" => 1,
"third" => 3,
"fourth" => 4,
"fifth" => 5,
"sixth" => 6,
"seventh" => 7,
"eight" => 8,
"ninth" => 9,
"tenth" => 10,
"eleventh" => 11,
"twelfth" => 12,
_ => return None,
})
})
.parse_next(input)
}
+144
View File
@@ -0,0 +1,144 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//! Primitive combinators.
use std::str::FromStr;
use winnow::{
ascii::{digit1, multispace0, Uint},
combinator::{alt, delimited, not, opt, peek, preceded, repeat, separated},
error::{ContextError, ParserError, StrContext, StrContextValue},
stream::AsChar,
token::{none_of, one_of, take_while},
Parser,
};
/// Allow spaces and comments before a parser
///
/// Every token parser should be wrapped in this to allow spaces and comments.
/// It is only preceding, because that allows us to check mandatory whitespace
/// after running the parser.
pub(super) fn s<'a, O, E>(p: impl Parser<&'a str, O, E>) -> impl Parser<&'a str, O, E>
where
E: ParserError<&'a str>,
{
preceded(space, p)
}
/// Parse the space in-between tokens
///
/// You probably want to use the [`s`] combinator instead.
pub(super) fn space<'a, E>(input: &mut &'a str) -> winnow::Result<(), E>
where
E: ParserError<&'a str>,
{
separated(0.., multispace0, alt((comment, ignored_hyphen_or_plus))).parse_next(input)
}
/// A hyphen or plus is ignored when it is not followed by a digit
///
/// This includes being followed by a comment! Compare these inputs:
/// ```txt
/// - 12 weeks
/// - (comment) 12 weeks
/// ```
/// The last comment should be ignored.
///
/// The plus is undocumented, but it seems to be ignored.
fn ignored_hyphen_or_plus<'a, E>(input: &mut &'a str) -> winnow::Result<(), E>
where
E: ParserError<&'a str>,
{
(
alt(('-', '+')),
multispace0,
peek(not(take_while(1, AsChar::is_dec_digit))),
)
.void()
.parse_next(input)
}
/// Parse a comment
///
/// A comment is given between parentheses, which must be balanced. Any other
/// tokens can be within the comment.
fn comment<'a, E>(input: &mut &'a str) -> winnow::Result<(), E>
where
E: ParserError<&'a str>,
{
delimited(
'(',
repeat(0.., alt((none_of(['(', ')']).void(), comment))),
')',
)
.parse_next(input)
}
/// Parse a signed decimal integer.
///
/// Rationale for not using `winnow::ascii::dec_int`: When upgrading winnow from
/// 0.5 to 0.7, we discovered that `winnow::ascii::dec_int` now accepts only the
/// following two forms:
///
/// - 0
/// - [+-]?[1-9][0-9]*
///
/// Inputs like [+-]?0[0-9]* (e.g., `+012`) are therefore rejected. We provide a
/// custom implementation to support such zero-prefixed integers.
#[allow(unused)]
pub(super) fn dec_int<'a, E>(input: &mut &'a str) -> winnow::Result<i32, E>
where
E: ParserError<&'a str>,
{
(opt(one_of(['+', '-'])), digit1)
.void()
.take()
.verify_map(|s: &str| s.parse().ok())
.parse_next(input)
}
/// Parse an unsigned decimal integer.
///
/// See the rationale for `dec_int` for why we don't use
/// `winnow::ascii::dec_uint`.
pub(super) fn dec_uint<'a, O, E>(input: &mut &'a str) -> winnow::Result<O, E>
where
O: Uint + FromStr,
E: ParserError<&'a str>,
{
dec_uint_str
.verify_map(|s: &str| s.parse().ok())
.parse_next(input)
}
/// Parse an unsigned decimal integer as a string slice.
pub(super) fn dec_uint_str<'a, E>(input: &mut &'a str) -> winnow::Result<&'a str, E>
where
E: ParserError<&'a str>,
{
digit1.void().take().parse_next(input)
}
/// Parse a colon preceded by whitespace.
pub(super) fn colon<'a, E>(input: &mut &'a str) -> winnow::Result<(), E>
where
E: ParserError<&'a str>,
{
s(':').void().parse_next(input)
}
/// Parse a plus or minus character optionally preceeded by whitespace.
pub(super) fn plus_or_minus<'a, E>(input: &mut &'a str) -> winnow::Result<char, E>
where
E: ParserError<&'a str>,
{
s(alt(('+', '-'))).parse_next(input)
}
/// Create a context error with a reason.
pub(super) fn ctx_err(reason: &'static str) -> ContextError {
let mut err = ContextError::new();
err.push(StrContext::Expected(StrContextValue::Description(reason)));
err
}

Some files were not shown because too many files have changed in this diff Show More