diff --git a/src/items/mod.rs b/src/items/mod.rs index 9a9794a..f43f661 100644 --- a/src/items/mod.rs +++ b/src/items/mod.rs @@ -145,9 +145,9 @@ where /// following two forms: /// /// - 0 -/// - [+-][1-9][0-9]* +/// - [+-]?[1-9][0-9]* /// -/// Inputs like [+-]0[0-9]* (e.g., `+012`) are therefore rejected. We provide a +/// Inputs like [+-]?0[0-9]* (e.g., `+012`) are therefore rejected. We provide a /// custom implementation to support such zero-prefixed integers. fn dec_int<'a, E>(input: &mut &'a str) -> winnow::Result where @@ -175,6 +175,23 @@ where .parse_next(input) } +/// Parse a float number. +/// +/// Rationale for not using `winnow::ascii::float`: the `float` parser provided +/// by winnow accepts E-notation numbers (e.g., `1.23e4`), whereas GNU date +/// rejects such numbers. To remain compatible with GNU date, we provide a +/// custom implementation that only accepts inputs like [+-]?[0-9]+(\.[0-9]+)?. +fn float<'a, E>(input: &mut &'a str) -> winnow::Result +where + E: ParserError<&'a str>, +{ + (opt(one_of(['+', '-'])), digit1, opt(preceded('.', digit1))) + .void() + .take() + .verify_map(|s: &str| s.parse().ok()) + .parse_next(input) +} + // Parse an item pub fn parse_one(input: &mut &str) -> ModalResult { trace( diff --git a/src/items/relative.rs b/src/items/relative.rs index 3a605a7..701a197 100644 --- a/src/items/relative.rs +++ b/src/items/relative.rs @@ -32,12 +32,12 @@ //! > ‘this thursday’. use winnow::{ - ascii::{alpha1, float}, + ascii::alpha1, combinator::{alt, opt}, ModalResult, Parser, }; -use super::{ordinal::ordinal, s}; +use super::{float, ordinal::ordinal, s}; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Relative { diff --git a/src/lib.rs b/src/lib.rs index 9d25616..3dc5a1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,7 +165,13 @@ mod tests { #[test] fn invalid_formats() { - let invalid_dts = vec!["NotADate", "202104", "202104-12T22:37:47"]; + let invalid_dts = vec![ + "NotADate", + "202104", + "202104-12T22:37:47", + "a774e26sec", // 774e26 is not a valid seconds value (we don't accept E-notation) + "12.", // Invalid floating point number + ]; for dt in invalid_dts { assert_eq!(parse_datetime(dt), Err(ParseDateTimeError::InvalidInput)); }