Skip to content

Commit

Permalink
Exact whitespace and timezone colon parsing
Browse files Browse the repository at this point in the history
Be exact about allowed whitespace around and between data and
formats for all parsing.
Except RFC 2822 format which explicitly allows arbitrary whitespace.

For timezone colons dividing hours and seconds,
limit variations to `":"`, `" "`, `" :"`, `": "`, or `" : "`.
Previously chrono allowed unlimited whitespace and colons to
divide a timezone, e.g. the unusual string
`"09:  : :  :::    :00"` could be a valid timezone (it shouldn't be).

Issue #660
  • Loading branch information
jtmoon79 committed Sep 10, 2022
1 parent fd946e8 commit 742fb95
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 193 deletions.
65 changes: 36 additions & 29 deletions src/datetime/tests.rs
Expand Up @@ -259,18 +259,6 @@ fn test_parse_datetime_utc() {
let valid = [
"2001-02-03T04:05:06Z",
"2012-12-12T12:12:12Z",
"2012 -12-12T12:12:12Z",
"2012 -12-12T12:12:12Z",
"2012- 12-12T12:12:12Z",
"2012- 12-12T12:12:12Z",
"2012-12-12T 12:12:12Z",
"2012-12-12T12 :12:12Z",
"2012-12-12T12 :12:12Z",
"2012-12-12T12: 12:12Z",
"2012-12-12T12: 12:12Z",
"2012-12-12T12 : 12:12Z",
"2012-12-12T12:12:12Z ",
" 2012-12-12T12:12:12Z",
"2015-02-18T23:16:09.153Z",
"2015-2-18T23:16:09.153Z",
"+2015-2-18T23:16:09.153Z",
Expand Down Expand Up @@ -327,6 +315,18 @@ fn test_parse_datetime_utc() {
"2012-12-12t12:12:12Z", // wrong divider 't'
"+802701-12-12T12:12:12Z", // invalid year (out of bounds)
"+ 2012-12-12T12:12:12Z", // invalid space before year
"2012 -12-12T12:12:12Z", // space after year
"2012 -12-12T12:12:12Z", // multi space after year
"2012- 12-12T12:12:12Z", // space after year divider
"2012- 12-12T12:12:12Z", // multi space after year divider
"2012-12-12T 12:12:12Z", // space after date-time divider
"2012-12-12T12 :12:12Z", // space after hour
"2012-12-12T12 :12:12Z", // multi space after hour
"2012-12-12T12: 12:12Z", // space before minute
"2012-12-12T12: 12:12Z", // multi space before minute
"2012-12-12T12 : 12:12Z", // space space before and after hour-minute divider
"2012-12-12T12:12:12Z ", // trailing space
" 2012-12-12T12:12:12Z", // leading space
" +82701 - 05 - 6 T 15 : 9 : 60.898989898989 Z", // valid datetime, wrong format
];
for &s in &invalid {
Expand Down Expand Up @@ -395,12 +395,23 @@ fn test_utc_datetime_from_str_with_spaces() {
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S "), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n"), Ok(dt),);
// with varying spaces - should fail
// leading whitespace in format
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S").is_err());
// trailing whitespace in format
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err());
// extra mid-string whitespace in format
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
// mismatched leading whitespace
assert!(Utc.datetime_from_str("\tAug 09 2013 23:54:35", "\n%b %d %Y %H:%M:%S").is_err());
// mismatched trailing whitespace
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n").is_err());
// mismatched mid-string whitespace
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S").is_err());
// trailing whitespace in format
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err());
// trailing whitespace (newline) in format
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
// leading space in data
assert!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
// trailing space in data
Expand Down Expand Up @@ -447,9 +458,8 @@ fn test_datetime_parse_from_str() {
assert!(
DateTime::parse_from_str("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z").is_err()
);
assert_eq!(
DateTime::parse_from_str("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z "),
Ok(dt),
assert!(
DateTime::parse_from_str("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z ").is_err()
);
// trailing colon
assert!(
Expand Down Expand Up @@ -620,9 +630,8 @@ fn test_datetime_parse_from_str() {
DateTime::parse_from_str("Aug 09 2013 23:54:35 -09:", "%b %d %Y %H:%M:%S %#z"),
Ok(dt),
);
assert_eq!(
DateTime::parse_from_str("Aug 09 2013 23:54:35 -09: ", "%b %d %Y %H:%M:%S %#z "),
Ok(dt),
assert!(
DateTime::parse_from_str("Aug 09 2013 23:54:35 -09: ", "%b %d %Y %H:%M:%S %#z ").is_err(),
);
assert_eq!(
DateTime::parse_from_str("Aug 09 2013 23:54:35+-09", "%b %d %Y %H:%M:%S+%#z"),
Expand All @@ -632,13 +641,11 @@ fn test_datetime_parse_from_str() {
DateTime::parse_from_str("Aug 09 2013 23:54:35--09", "%b %d %Y %H:%M:%S-%#z"),
Ok(dt),
);
assert_eq!(
DateTime::parse_from_str("Aug 09 2013 -09:00 23:54:35", "%b %d %Y %#z%H:%M:%S"),
Ok(dt),
assert!(
DateTime::parse_from_str("Aug 09 2013 -09:00 23:54:35", "%b %d %Y %#z%H:%M:%S").is_err(),
);
assert_eq!(
DateTime::parse_from_str("Aug 09 2013 -0900 23:54:35", "%b %d %Y %#z%H:%M:%S"),
Ok(dt),
assert!(
DateTime::parse_from_str("Aug 09 2013 -0900 23:54:35", "%b %d %Y %#z%H:%M:%S").is_err(),
);
assert_eq!(
DateTime::parse_from_str("Aug 09 2013 -090023:54:35", "%b %d %Y %#z%H:%M:%S"),
Expand Down
12 changes: 6 additions & 6 deletions src/format/mod.rs
Expand Up @@ -218,27 +218,27 @@ pub enum Fixed {
///
/// It does not support parsing, its use in the parser is an immediate failure.
TimezoneName,
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`).
/// Offset from the local time to UTC (`+09:00` or `-0400` or `+00:00`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
/// In the parser, the colon may be omitted,
/// The offset is limited from `-24:00` to `+24:00`,
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetColon,
/// Offset from the local time to UTC with seconds (`+09:00:00` or `-04:00:00` or `+00:00:00`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
/// In the parser, the colon may be omitted,
/// The offset is limited from `-24:00:00` to `+24:00:00`,
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetDoubleColon,
/// Offset from the local time to UTC without minutes (`+09` or `-04` or `+00`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
/// In the parser, the colon may be omitted,
/// The offset is limited from `-24` to `+24`,
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetTripleColon,
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
/// Offset from the local time to UTC (`+09:00` or `-0400` or `Z`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
/// In the parser, the colon may be omitted,
/// and `Z` can be either in upper case or in lower case.
/// The offset is limited from `-24:00` to `+24:00`,
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
Expand Down

0 comments on commit 742fb95

Please sign in to comment.