diff --git a/tests/parsing.rs b/tests/parsing.rs index 0dc6887c9..f3ca87713 100644 --- a/tests/parsing.rs +++ b/tests/parsing.rs @@ -114,32 +114,31 @@ fn rfc_2822() -> time::Result<()> { Ok(()) } +#[test] +fn issue_661() -> time::Result<()> { + assert_eq!( + OffsetDateTime::parse("02 Jan 2021 03:04:05 +0607", &Rfc2822)?, + datetime!(2021-01-02 03:04:05 +06:07), + ); + assert_eq!( + Date::parse("02 Jan 2021 03:04:05 +0607", &Rfc2822)?, + date!(2021 - 01 - 02) + ); + + Ok(()) +} + #[allow(clippy::cognitive_complexity)] // all test the same thing #[test] fn rfc_2822_err() { // In the first test, the "weekday" component is invalid, we're actually testing the whitespace // parser. The error is because the parser attempts and fails to parse the whitespace, but it's // optional so it backtracks and attempts to parse the weekday (while still having leading - // whitespace), thus failing. + // whitespace). The weekday is also optional, so it backtracks and attempts to parse the day. + // This component is required, so it fails at this point. assert!(matches!( OffsetDateTime::parse(" \r\nM", &Rfc2822), - invalid_component!("weekday") - )); - assert!(matches!( - OffsetDateTime::parse("Mon,(\u{0}", &Rfc2822), - invalid_literal!() - )); - assert!(matches!( - OffsetDateTime::parse("Mon,(", &Rfc2822), - invalid_literal!() - )); - assert!(matches!( - OffsetDateTime::parse("Mon,(\\\u{ff}", &Rfc2822), - invalid_literal!() - )); - assert!(matches!( - OffsetDateTime::parse("Mon,((\\\u{0})(\\\u{b} )(\\\u{d})", &Rfc2822), - invalid_literal!() + invalid_component!("day") )); assert!(matches!( diff --git a/time/src/parsing/parsable.rs b/time/src/parsing/parsable.rs index bed178283..1b7043899 100644 --- a/time/src/parsing/parsable.rs +++ b/time/src/parsing/parsable.rs @@ -165,8 +165,8 @@ impl sealed::Sealed for Rfc2822 { let colon = ascii_char::; let comma = ascii_char::; - let input = opt(fws)(input).into_inner(); - let input = first_match( + let input = opt(cfws)(input).into_inner(); + let weekday = first_match( [ (b"Mon".as_slice(), Weekday::Monday), (b"Tue".as_slice(), Weekday::Tuesday), @@ -177,11 +177,16 @@ impl sealed::Sealed for Rfc2822 { (b"Sun".as_slice(), Weekday::Sunday), ], false, - )(input) - .and_then(|item| item.consume_value(|value| parsed.set_weekday(value))) - .ok_or(InvalidComponent("weekday"))?; - let input = comma(input).ok_or(InvalidLiteral)?.into_inner(); - let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + )(input); + let input = if let Some(item) = weekday { + let input = item + .consume_value(|value| parsed.set_weekday(value)) + .ok_or(InvalidComponent("weekday"))?; + let input = comma(input).ok_or(InvalidLiteral)?.into_inner(); + opt(cfws)(input).into_inner() + } else { + input + }; let input = n_to_m_digits::<1, 2, _>(input) .and_then(|item| item.consume_value(|value| parsed.set_day(value))) .ok_or(InvalidComponent("day"))?; @@ -307,6 +312,8 @@ impl sealed::Sealed for Rfc2822 { }) .ok_or(InvalidComponent("offset minute"))?; + let input = opt(cfws)(input).into_inner(); + Ok(input) } @@ -320,10 +327,10 @@ impl sealed::Sealed for Rfc2822 { let colon = ascii_char::; let comma = ascii_char::; - let input = opt(fws)(input).into_inner(); + let input = opt(cfws)(input).into_inner(); // This parses the weekday, but we don't actually use the value anywhere. Because of this, // just return `()` to avoid unnecessary generated code. - let ParsedItem(input, ()) = first_match( + let weekday = first_match( [ (b"Mon".as_slice(), ()), (b"Tue".as_slice(), ()), @@ -334,10 +341,14 @@ impl sealed::Sealed for Rfc2822 { (b"Sun".as_slice(), ()), ], false, - )(input) - .ok_or(InvalidComponent("weekday"))?; - let input = comma(input).ok_or(InvalidLiteral)?.into_inner(); - let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + )(input); + let input = if let Some(item) = weekday { + let input = item.into_inner(); + let input = comma(input).ok_or(InvalidLiteral)?.into_inner(); + opt(cfws)(input).into_inner() + } else { + input + }; let ParsedItem(input, day) = n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?; let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); @@ -442,6 +453,8 @@ impl sealed::Sealed for Rfc2822 { (input, offset_hour, offset_minute.cast_signed()) }; + let input = opt(cfws)(input).into_inner(); + if !input.is_empty() { return Err(error::Parse::ParseFromDescription( error::ParseFromDescription::UnexpectedTrailingCharacters,