diff --git a/tests/parsing.rs b/tests/parsing.rs index 192a96498..518661d3b 100644 --- a/tests/parsing.rs +++ b/tests/parsing.rs @@ -323,6 +323,16 @@ fn rfc_3339() -> time::Result<()> { offset!(-00:01), ); + // Any separator is allowed by RFC 3339, not just `T`. + assert_eq!( + OffsetDateTime::parse("2021-01-02 03:04:05Z", &Rfc3339)?, + datetime!(2021-01-02 03:04:05 UTC), + ); + assert_eq!( + OffsetDateTime::parse("2021-01-02$03:04:05Z", &Rfc3339)?, + datetime!(2021-01-02 03:04:05 UTC), + ); + Ok(()) } @@ -354,8 +364,8 @@ fn rfc_3339_err() { invalid_component!("day") )); assert!(matches!( - PrimitiveDateTime::parse("2021-01-01x", &Rfc3339), - invalid_literal!() + PrimitiveDateTime::parse("2021-01-01", &Rfc3339), + invalid_component!("separator") )); assert!(matches!( PrimitiveDateTime::parse("2021-01-01T0", &Rfc3339), @@ -448,8 +458,8 @@ fn rfc_3339_err() { invalid_component!("day") )); assert!(matches!( - OffsetDateTime::parse("2021-01-01x", &Rfc3339), - invalid_literal!() + OffsetDateTime::parse("2021-01-01", &Rfc3339), + invalid_component!("separator") )); assert!(matches!( OffsetDateTime::parse("2021-01-01T0", &Rfc3339), diff --git a/time/src/parsing/parsable.rs b/time/src/parsing/parsable.rs index bad81e652..8386d132f 100644 --- a/time/src/parsing/parsable.rs +++ b/time/src/parsing/parsable.rs @@ -520,9 +520,18 @@ impl sealed::Sealed for Rfc3339 { let input = exactly_n_digits::<2, _>(input) .and_then(|item| item.consume_value(|value| parsed.set_day(value))) .ok_or(InvalidComponent("day"))?; - let input = ascii_char_ignore_case::(input) - .ok_or(InvalidLiteral)? - .into_inner(); + + // RFC3339 allows any separator, not just `T`, not just `space`. + // cf. Section 5.6: Internet Date/Time Format: + // NOTE: ISO 8601 defines date and time separated by "T". + // Applications using this syntax may choose, for the sake of + // readability, to specify a full-date and full-time separated by + // (say) a space character. + // Specifically, rusqlite uses space separators. + let input = input + .get(1..) + .ok_or_else(|| InvalidComponent("separator"))?; + let input = exactly_n_digits::<2, _>(input) .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value))) .ok_or(InvalidComponent("hour"))?; @@ -618,9 +627,18 @@ impl sealed::Sealed for Rfc3339 { let input = dash(input).ok_or(InvalidLiteral)?.into_inner(); let ParsedItem(input, day) = exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("day"))?; - let input = ascii_char_ignore_case::(input) - .ok_or(InvalidLiteral)? - .into_inner(); + + // RFC3339 allows any separator, not just `T`, not just `space`. + // cf. Section 5.6: Internet Date/Time Format: + // NOTE: ISO 8601 defines date and time separated by "T". + // Applications using this syntax may choose, for the sake of + // readability, to specify a full-date and full-time separated by + // (say) a space character. + // Specifically, rusqlite uses space separators. + let input = input + .get(1..) + .ok_or_else(|| InvalidComponent("separator"))?; + let ParsedItem(input, hour) = exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?; let input = colon(input).ok_or(InvalidLiteral)?.into_inner();