diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index f30eb913fa..90817dc45c 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -806,6 +806,18 @@ fn test_parse_datetime_utc() { "2012-12-12 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-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", @@ -850,38 +862,21 @@ fn test_parse_datetime_utc() { "+1441497364", // valid datetime, wrong format "+1441497364Z", // valid datetime, wrong format "2014/02/03 04:05:06Z", // valid datetime, wrong format - "2001-02-03T04:05:0600:00", // valid datetime, timezone too close - "2015-15-15T15:15:15Z", // invalid datetime - "2012-12-12T12:12:12x", // invalid timezone - "2012-123-12T12:12:12Z", // invalid month - "2012-12-77T12:12:12Z", // invalid day - "2012-12-12T26:12:12Z", // invalid hour - "2012-12-12T12:61:12Z", // invalid minute - "2012-12-12T12:12:62Z", // invalid second - "2012-12-12 T12:12:12Z", // space after date - "2012-12-12T12:12:12ZZ", // trailing literal 'Z' - "+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", // leading space - "2001-02-03T04:05:06-00 00", // invalid timezone spacing - "2001-02-03T04:05:06-01: 00", // invalid timezone spacing - "2001-02-03T04:05:06-01 :00", // invalid timezone spacing - "2001-02-03T04:05:06-01 : 00", // invalid timezone spacing - "2001-02-03T04:05:06-01 : 00", // invalid timezone spacing - "2001-02-03T04:05:06-01 : :00", // invalid timezone spacing + "2001-02-03T04:05:0600:00", // valid datetime, timezone too close + "2015-15-15T15:15:15Z", // invalid datetime + "2012-12-12T12:12:12x", // invalid timezone + "2012-123-12T12:12:12Z", // invalid month + "2012-12-77T12:12:12Z", // invalid day + "2012-12-12T26:12:12Z", // invalid hour + "2012-12-12T12:61:12Z", // invalid minute + "2012-12-12T12:12:62Z", // invalid second + "2012-12-12 T12:12:12Z", // space after date + "2012-12-12T12:12:12ZZ", // trailing literal 'Z' + "+802701-12-12T12:12:12Z", // invalid year (out of bounds) + "+ 2012-12-12T12:12:12Z", // invalid space before year " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 Z", // valid datetime, wrong format ]; - for &s in invalid.iter() { + for &s in &invalid { eprintln!("test_parse_datetime_utc invalid {:?}", s); assert!(s.parse::>().is_err()); } @@ -973,7 +968,7 @@ fn test_datetime_parse_from_str() { assert_eq!(parse("Aug 09 2013 23:54:35 -09:00 ", "%b %d %Y %H:%M:%S %z "), Ok(dt)); // trailing newline after timezone assert!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z ").is_err()); + assert_eq!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z "), Ok(dt)); // trailing colon assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %z").is_err()); // trailing colon with space @@ -1067,8 +1062,8 @@ fn test_datetime_parse_from_str() { assert!(parse("Aug 09 2013 23:54:35 -09: ", "%b %d %Y %H:%M:%S %#z ").is_err()); assert_eq!(parse("Aug 09 2013 23:54:35+-09", "%b %d %Y %H:%M:%S+%#z"), Ok(dt)); assert_eq!(parse("Aug 09 2013 23:54:35--09", "%b %d %Y %H:%M:%S-%#z"), Ok(dt)); - assert!(parse("Aug 09 2013 -09:00 23:54:35", "%b %d %Y %#z%H:%M:%S").is_err()); - assert!(parse("Aug 09 2013 -0900 23:54:35", "%b %d %Y %#z%H:%M:%S").is_err()); + assert_eq!(parse("Aug 09 2013 -09:00 23:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); + assert_eq!(parse("Aug 09 2013 -0900 23:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); assert_eq!(parse("Aug 09 2013 -090023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); assert_eq!(parse("Aug 09 2013 -09:0023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); // timezone with partial minutes adjacent hours diff --git a/src/format/parse.rs b/src/format/parse.rs index 3116c2e26f..b07c74345b 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -241,7 +241,7 @@ pub(crate) fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseRes /// /// - Padding-agnostic (for numeric items). /// The [`Pad`](./enum.Pad.html) field is completely ignored, -/// so one can prepend any number of zeroes before numbers. +/// so one can prepend any number of whitespace then any number of zeroes before numbers. /// /// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()> @@ -326,37 +326,13 @@ where s = &s[prefix.len()..]; } - Item::Space(item_space) => { - for expect in item_space.chars() { - let actual = match s.chars().next() { - Some(c) => c, - None => { - return Err((s, TOO_SHORT)); - } - }; - if expect != actual { - return Err((s, INVALID)); - } - // advance `s` forward 1 char - s = scan::s_next(s); - } + Item::Space(_) => { + s = s.trim_start(); } #[cfg(feature = "alloc")] - Item::OwnedSpace(ref item_space) => { - for expect in item_space.chars() { - let actual = match s.chars().next() { - Some(c) => c, - None => { - return Err((s, TOO_SHORT)); - } - }; - if expect != actual { - return Err((s, INVALID)); - } - // advance `s` forward 1 char - s = scan::s_next(s); - } + Item::OwnedSpace(_) => { + s = s.trim_start(); } Item::Numeric(ref spec, ref _pad) => { @@ -389,6 +365,7 @@ where Internal(ref int) => match int._dummy {}, }; + s = s.trim_start(); let v = if signed { if s.starts_with('-') { let v = try_consume!(scan::number(&s[1..], 1, usize::MAX)); @@ -481,9 +458,8 @@ where | &TimezoneOffsetDoubleColon | &TimezoneOffsetTripleColon | &TimezoneOffset => { - s = scan::trim1(s); let offset = try_consume!(scan::timezone_offset( - s, + s.trim_start(), scan::consume_colon_maybe, false, false, @@ -493,9 +469,8 @@ where } &TimezoneOffsetColonZ | &TimezoneOffsetZ => { - s = scan::trim1(s); let offset = try_consume!(scan::timezone_offset( - s, + s.trim_start(), scan::consume_colon_maybe, true, false, @@ -506,9 +481,8 @@ where &Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive, }) => { - s = scan::trim1(s); let offset = try_consume!(scan::timezone_offset( - s, + s.trim_start(), scan::consume_colon_maybe, true, true, @@ -544,12 +518,13 @@ where /// Accepts a relaxed form of RFC3339. /// A space or a 'T' are accepted as the separator between the date and time -/// parts. +/// parts. Additional spaces are allowed between each component. /// /// ``` /// # use chrono::{DateTime, offset::FixedOffset}; -/// "2000-01-02T03:04:05Z".parse::>()?; -/// "2000-01-02 03:04:05Z".parse::>()?; +/// "2012-12-12T12:12:12Z".parse::>()?; +/// "2012-12-12 12:12:12Z".parse::>()?; +/// "2012- 12-12T12: 12:12Z".parse::>()?; /// # Ok::<(), chrono::ParseError>(()) /// ``` impl str::FromStr for DateTime { @@ -652,31 +627,31 @@ mod tests { parses(" ", &[Space(" ")]); parses(" ", &[Space(" ")]); parses(" ", &[Space(" ")]); - check(" ", &[Space("")], Err(TOO_LONG)); - check(" ", &[Space(" ")], Err(TOO_LONG)); - check(" ", &[Space(" ")], Err(TOO_LONG)); - check(" ", &[Space(" ")], Err(TOO_LONG)); - check("", &[Space(" ")], Err(TOO_SHORT)); - check(" ", &[Space(" ")], Err(TOO_SHORT)); - check(" ", &[Space(" ")], Err(TOO_SHORT)); - check(" ", &[Space(" "), Space(" ")], Err(TOO_SHORT)); - check(" ", &[Space(" "), Space(" ")], Err(TOO_SHORT)); + parses(" ", &[Space("")]); + parses(" ", &[Space(" ")]); + parses(" ", &[Space(" ")]); + parses(" ", &[Space(" ")]); + parses("", &[Space(" ")]); + parses(" ", &[Space(" ")]); + parses(" ", &[Space(" ")]); + parses(" ", &[Space(" "), Space(" ")]); + parses(" ", &[Space(" "), Space(" ")]); parses(" ", &[Space(" "), Space(" ")]); parses(" ", &[Space(" "), Space(" ")]); parses(" ", &[Space(" "), Space(" ")]); parses(" ", &[Space(" "), Space(" "), Space(" ")]); - check("\t", &[Space("")], Err(TOO_LONG)); - check(" \n\r \n", &[Space("")], Err(TOO_LONG)); + parses("\t", &[Space("")]); + parses(" \n\r \n", &[Space("")]); parses("\t", &[Space("\t")]); - check("\t", &[Space(" ")], Err(INVALID)); - check(" ", &[Space("\t")], Err(INVALID)); + parses("\t", &[Space(" ")]); + parses(" ", &[Space("\t")]); parses("\t\r", &[Space("\t\r")]); parses("\t\r ", &[Space("\t\r ")]); parses("\t \r", &[Space("\t \r")]); parses(" \t\r", &[Space(" \t\r")]); parses(" \n\r \n", &[Space(" \n\r \n")]); - check(" \t\n", &[Space(" \t")], Err(TOO_LONG)); - check(" \n\t", &[Space(" \t\n")], Err(INVALID)); + parses(" \t\n", &[Space(" \t")]); + parses(" \n\t", &[Space(" \t\n")]); parses("\u{2002}", &[Space("\u{2002}")]); // most unicode whitespace characters parses( @@ -692,11 +667,11 @@ mod tests { ] ); check("a", &[Space("")], Err(TOO_LONG)); - check("a", &[Space(" ")], Err(INVALID)); - // a Space containing a literal can match a literal, but this should not be done - parses("a", &[Space("a")]); + check("a", &[Space(" ")], Err(TOO_LONG)); + // a Space containing a literal does not match a literal + check("a", &[Space("a")], Err(TOO_LONG)); check("abc", &[Space("")], Err(TOO_LONG)); - check("abc", &[Space(" ")], Err(INVALID)); + check("abc", &[Space(" ")], Err(TOO_LONG)); check(" abc", &[Space("")], Err(TOO_LONG)); check(" abc", &[Space(" ")], Err(TOO_LONG)); @@ -743,7 +718,7 @@ mod tests { // check("x y", &[Literal("x"), Literal("y")], Err(INVALID)); parses("xy", &[Literal("x"), Space(""), Literal("y")]); - check("x y", &[Literal("x"), Space(""), Literal("y")], Err(INVALID)); + parses("x y", &[Literal("x"), Space(""), Literal("y")]); parses("x y", &[Literal("x"), Space(" "), Literal("y")]); // whitespaces + literals @@ -776,7 +751,7 @@ mod tests { check("2015", &[num(Year)], parsed!(year: 2015)); check("0000", &[num(Year)], parsed!(year: 0)); check("9999", &[num(Year)], parsed!(year: 9999)); - check(" \t987", &[num(Year)], Err(INVALID)); + check(" \t987", &[num(Year)], parsed!(year: 987)); check(" \t987", &[Space(" \t"), num(Year)], parsed!(year: 987)); check(" \t987🤠", &[Space(" \t"), num(Year), Literal("🤠")], parsed!(year: 987)); check("987🤠", &[num(Year), Literal("🤠")], parsed!(year: 987)); @@ -788,9 +763,9 @@ mod tests { check("12345", &[nums(Year), Literal("5")], parsed!(year: 1234)); check("12345", &[num0(Year), Literal("5")], parsed!(year: 1234)); check("12341234", &[num(Year), num(Year)], parsed!(year: 1234)); - check("1234 1234", &[num(Year), num(Year)], Err(INVALID)); + check("1234 1234", &[num(Year), num(Year)], parsed!(year: 1234)); check("1234 1234", &[num(Year), Space(" "), num(Year)], parsed!(year: 1234)); - check("1234 1235", &[num(Year), num(Year)], Err(INVALID)); + check("1234 1235", &[num(Year), num(Year)], Err(IMPOSSIBLE)); check("1234 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID)); check("1234x1234", &[num(Year), Literal("x"), num(Year)], parsed!(year: 1234)); check("1234 x 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID)); @@ -815,10 +790,10 @@ mod tests { check("-42195", &[num(Year)], parsed!(year: -42195)); check("−42195", &[num(Year)], Err(INVALID)); // MINUS SIGN (U+2212) check("+42195", &[num(Year)], parsed!(year: 42195)); - check(" -42195", &[num(Year)], Err(INVALID)); - check(" +42195", &[num(Year)], Err(INVALID)); - check(" -42195", &[num(Year)], Err(INVALID)); - check(" +42195", &[num(Year)], Err(INVALID)); + check(" -42195", &[num(Year)], parsed!(year: -42195)); + check(" +42195", &[num(Year)], parsed!(year: 42195)); + check(" -42195", &[num(Year)], parsed!(year: -42195)); + check(" +42195", &[num(Year)], parsed!(year: 42195)); check("-42195 ", &[num(Year)], Err(TOO_LONG)); check("+42195 ", &[num(Year)], Err(TOO_LONG)); check(" - 42", &[num(Year)], Err(INVALID)); @@ -835,7 +810,7 @@ mod tests { check("345", &[num(Ordinal)], parsed!(ordinal: 345)); check("+345", &[num(Ordinal)], Err(INVALID)); check("-345", &[num(Ordinal)], Err(INVALID)); - check(" 345", &[num(Ordinal)], Err(INVALID)); + check(" 345", &[num(Ordinal)], parsed!(ordinal: 345)); check("−345", &[num(Ordinal)], Err(INVALID)); // MINUS SIGN (U+2212) check("345 ", &[num(Ordinal)], Err(TOO_LONG)); check(" 345", &[Space(" "), num(Ordinal)], parsed!(ordinal: 345)); @@ -849,28 +824,27 @@ mod tests { check(" +345", &[Space(" "), num(Ordinal)], Err(INVALID)); check(" -345", &[Space(" "), num(Ordinal)], Err(INVALID)); - const S: Item = Space(" "); // various numeric fields - check("1234 5678", &[num(Year), S, num(IsoYear)], parsed!(year: 1234, isoyear: 5678)); - check("1234 5678", &[num(Year), S, num(IsoYear)], parsed!(year: 1234, isoyear: 5678)); + check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678)); + check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678)); check( "12 34 56 78", - &[num(YearDiv100), S, num(YearMod100), S, num(IsoYearDiv100), S, num(IsoYearMod100)], + &[num(YearDiv100), num(YearMod100), num(IsoYearDiv100), num(IsoYearMod100)], parsed!(year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78), ); check( - "1 2 3 45", - &[num(Month), S, num(Day), S, num(WeekFromSun), S, num(NumDaysFromSun), num(IsoWeek)], + "1 2 3 4 5", + &[num(Month), num(Day), num(WeekFromSun), num(NumDaysFromSun), num(IsoWeek)], parsed!(month: 1, day: 2, week_from_sun: 3, weekday: Weekday::Thu, isoweek: 5), ); check( "6 7 89 01", - &[num(WeekFromMon), S, num(WeekdayFromMon), S, num(Ordinal), S, num(Hour12)], + &[num(WeekFromMon), num(WeekdayFromMon), num(Ordinal), num(Hour12)], parsed!(week_from_mon: 6, weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1), ); check( "23 45 6 78901234 567890123", - &[num(Hour), S, num(Minute), S, num(Second), S, num(Nanosecond), S, num(Timestamp)], + &[num(Hour), num(Minute), num(Second), num(Nanosecond), num(Timestamp)], parsed!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234, timestamp: 567_890_123), ); } diff --git a/src/format/scan.rs b/src/format/scan.rs index 6eafc2efbc..e0948d5e74 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -192,15 +192,6 @@ pub(super) fn s_next(s: &str) -> &str { } } -/// If the first `char` is whitespace then consume it and return `s`. -/// Else return `s`. -pub(super) fn trim1(s: &str) -> &str { - match s.chars().next() { - Some(c) if c.is_whitespace() => s_next(s), - Some(_) | None => s, - } -} - /// Consumes one colon char `:` if it is at the front of `s`. /// Always returns `Ok(s)`. pub(crate) fn consume_colon_maybe(mut s: &str) -> ParseResult<&str> { @@ -387,7 +378,7 @@ enum CommentState { mod tests { use super::{ comment_2822, consume_colon_maybe, nanosecond, nanosecond_fixed, s_next, - short_or_long_month0, short_or_long_weekday, space, timezone_offset_2822, trim1, + short_or_long_month0, short_or_long_weekday, space, timezone_offset_2822, }; use crate::format::{INVALID, TOO_SHORT}; use crate::Weekday; @@ -488,20 +479,6 @@ mod tests { assert_eq!(s_next("a😾c"), "😾c"); } - #[test] - fn test_trim1() { - assert_eq!(trim1(""), ""); - assert_eq!(trim1(" "), ""); - assert_eq!(trim1("\t"), ""); - assert_eq!(trim1("\t\t"), "\t"); - assert_eq!(trim1(" "), " "); - assert_eq!(trim1("a"), "a"); - assert_eq!(trim1("a "), "a "); - assert_eq!(trim1("ab"), "ab"); - assert_eq!(trim1("😼"), "😼"); - assert_eq!(trim1("😼b"), "😼b"); - } - #[test] fn test_consume_colon_maybe() { assert_eq!(consume_colon_maybe(""), Ok("")); diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 91f105236a..cdad1f3a46 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -302,9 +302,9 @@ fn test_datetime_from_str() { // some invalid cases // since `ParseErrorKind` is private, all we can do is to check if there was an error let invalid = [ - "", // empty - "x", // invalid / missing data - "15", // missing data + "", // empty + "x", // invalid / missing data + "15", // missing data "15:8:9", // looks like a time (invalid date) "15-8-9", // looks like a date (invalid) "Fri, 09 Aug 2013 23:54:35 GMT", // valid date, wrong format @@ -319,27 +319,13 @@ fn test_datetime_from_str() { "2012-12-12T12:12:12 +00:00", // unexpected timezone / trailing literal "2012-12-12T12:12:12 GMT", // unexpected timezone / trailing literal "2012-123-12T12:12:12", // invalid month - "2012 -12-12T12:12:12", // space after year - "2012 -12-12T12:12:12", // multi space after year - "2012- 12-12T12:12:12", // space before month - "2012- 12-12T12:12:12", // multi space before month - "2012-12-12 T12:12:12", // space after day - "2012-12-12T 12:12:12", // space after date-time divider - "2012-12-12T12 :12:12", // space after hour - "2012-12-12T12 :12:12", // multi space after hour - "2012-12-12T12: 12:12", // space before minute - "2012-12-12T12: 12:12", // multi space before minute - "2012-12-12T12 : 12:12", // space around hour-minute divider - "2012-12-12T12:12:12 ", // trailing space - " 2012-12-12T12:12:12", // leading space "2012-12-12t12:12:12", // bad divider 't' "2012-12-12 12:12:12", // missing divider 'T' "2012-12-12T12:12:12Z", // trailing char 'Z' "+ 82701-123-12T12:12:12", // strange year, invalid month "+802701-123-12T12:12:12", // out-of-bound year, invalid month - " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", // many spaces ]; - for &s in invalid.iter() { + for &s in &invalid { eprintln!("test_datetime_from_str invalid {:?}", s); assert!(s.parse::().is_err()); } @@ -356,12 +342,8 @@ fn test_datetime_parse_from_str() { NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), Ok(ymdhms(2014, 5, 7, 12, 34, 56)) ); // ignore offset - assert!( - // intermixed whitespace - NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S").is_err() - ); assert_eq!( - NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u %H%M%S"), + NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"), Ok(ymdhms(2015, 2, 2, 0, 0, 0)) ); assert_eq!( @@ -407,29 +389,18 @@ fn test_datetime_parse_from_str_with_spaces() { assert_eq!(parse_from_str(" Aug 09 2013 23:54:35 ", " %b %d %Y %H:%M:%S "), Ok(dt)); assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "), Ok(dt),); + assert_eq!(parse_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "), Ok(dt)); assert_eq!(parse_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt)); assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt)); assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt)); assert_eq!(parse_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt)); assert_eq!(parse_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt)); + assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n"), Ok(dt)); + assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt)); + assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S "), Ok(dt)); + assert_eq!(parse_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); + assert_eq!(parse_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!(parse_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S").is_err()); - // trailing whitespace in format - assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err()); - // extra mid-string whitespace in format - assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err()); - // mismatched leading whitespace - assert!(parse_from_str("\tAug 09 2013 23:54:35", "\n%b %d %Y %H:%M:%S").is_err()); - // mismatched trailing whitespace - assert!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n").is_err()); - // mismatched mid-string whitespace - assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S").is_err()); - // trailing whitespace in format - assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err()); - // trailing whitespace (newline) in format - assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err()); // leading space in data assert!(parse_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err()); // trailing space in data