Skip to content

Commit

Permalink
fix: RFC3339 allows spaces (or anything) as separators
Browse files Browse the repository at this point in the history
  • Loading branch information
fasterthanlime committed Aug 24, 2024
1 parent 866c3a0 commit d60fded
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 10 deletions.
18 changes: 14 additions & 4 deletions tests/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down
30 changes: 24 additions & 6 deletions time/src/parsing/parsable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<b'T'>(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"))?;
Expand Down Expand Up @@ -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::<b'T'>(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();
Expand Down

0 comments on commit d60fded

Please sign in to comment.