From 5e8261c539499be979a0707013b65719734dda34 Mon Sep 17 00:00:00 2001 From: jtmoon79 <815261+jtmoon79@users.noreply.github.com> Date: Thu, 8 Sep 2022 16:25:18 -0700 Subject: [PATCH] Add various tests for current parsing Add more varying testing for most parsing functions. Tests emphasize whitespace, literals, timezones, and timezone delimiters (colons and whitespace). Add tests for multiple-byte characters and combining characters in and around data and parsing formats. These tests are added to aid humans verifying the next commit that changes parsing behavior. Issue #660 --- src/datetime/mod.rs | 5 + src/datetime/tests.rs | 898 +++++++++++++++++++++++++++++++++++- src/format/strftime.rs | 107 +++++ src/naive/date.rs | 33 +- src/naive/datetime/tests.rs | 45 +- src/naive/time/tests.rs | 70 ++- 6 files changed, 1128 insertions(+), 30 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 8686340e16..24f67298cf 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -700,6 +700,9 @@ impl DateTime { /// RFC 2822 is the internet message standard that specifies the representation of times in HTTP /// and email headers. /// + /// The RFC 2822 standard allows arbitrary intermixed whitespace. + /// See [RFC 2822 Appendix A.5] + /// /// ``` /// # use chrono::{DateTime, FixedOffset, TimeZone}; /// assert_eq!( @@ -707,6 +710,8 @@ impl DateTime { /// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap() /// ); /// ``` + /// + /// [RFC 2822 Appendix A.5]: https://www.rfc-editor.org/rfc/rfc2822#appendix-A.5 pub fn parse_from_rfc2822(s: &str) -> ParseResult> { const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; let mut parsed = Parsed::new(); diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 2d273f605d..b8338267f2 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -357,8 +357,10 @@ fn test_datetime_with_timezone() { #[test] #[cfg(any(feature = "alloc", feature = "std"))] -fn test_datetime_rfc2822_and_rfc3339() { +fn test_datetime_rfc2822() { let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap(); + + // timezone 0 assert_eq!( Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(), "Wed, 18 Feb 2015 23:16:09 +0000" @@ -367,6 +369,7 @@ fn test_datetime_rfc2822_and_rfc3339() { Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(), "2015-02-18T23:16:09+00:00" ); + // timezone +05 assert_eq!( edt.from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) @@ -389,6 +392,7 @@ fn test_datetime_rfc2822_and_rfc3339() { .to_rfc3339(), "2015-02-18T23:16:09.150+05:00" ); + // seconds 60 assert_eq!( edt.from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) @@ -421,7 +425,131 @@ fn test_datetime_rfc2822_and_rfc3339() { Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) ); assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), + edt.ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + .to_rfc2822(), + "Wed, 18 Feb 2015 23:59:60 +0500" + ); + assert_eq!( + DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), + Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap()) + ); + + // many varying whitespace intermixed + assert_eq!( + DateTime::::parse_from_rfc2822( + "\t\t\tWed,\n\t\t18 \r\n\t\tFeb \u{3000} 2015\r\n\t\t\t23:59:60 \t+0500" + ), + Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap()) + ); + // example from RFC 2822 Appendix A.5. + assert_eq!( + DateTime::::parse_from_rfc2822( + "Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330 (Newfoundland Time)" + ), + Ok(FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60) + .unwrap() + .ymd_opt(1969, 2, 13) + .unwrap() + .and_hms_opt(23, 32, 0) + .unwrap() + ) + ); + // example from RFC 2822 Appendix A.5. without trailing " (Newfoundland Time)" + assert_eq!( + DateTime::::parse_from_rfc2822( + "Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330" + ), + Ok(FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60) + .unwrap() + .ymd_opt(1969, 2, 13) + .unwrap() + .and_hms_opt(23, 32, 0) + .unwrap()) + ); + + // bad year + assert!(DateTime::::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); + // wrong format + assert!( + DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +00:00").is_err() + ); + // full name day of week + assert!(DateTime::::parse_from_rfc2822("Wednesday, 18 Feb 2015 23:16:09 +0000") + .is_err()); + // full name day of week + assert!(DateTime::::parse_from_rfc2822("Wednesday 18 Feb 2015 23:16:09 +0000") + .is_err()); + // wrong day of week separator '.' + assert!(DateTime::::parse_from_rfc2822("Wed. 18 Feb 2015 23:16:09 +0000").is_err()); + // *trailing* space causes failure + assert!( + DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000 ").is_err() + ); +} + +#[test] +#[cfg(any(feature = "alloc", feature = "std"))] +fn test_datetime_rfc3339() { + let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap(); + assert_eq!( + Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap().to_rfc3339(), + "2015-02-18T23:16:09+00:00" + ); + assert_eq!( + edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap().to_rfc3339(), + "2015-02-18T23:16:09.150+05:00" + ); + assert_eq!( + edt.ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + .to_rfc3339(), + "2015-02-18T23:59:60.234567+05:00" + ); + assert_eq!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:59.123+05:00"), + Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 123_000).unwrap()) + ); + assert_eq!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:59.123456+05:00"), + Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 123_456).unwrap()) + ); + assert_eq!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:59.123456789+05:00"), + Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_nano_opt(23, 59, 59, 123_456_789).unwrap()) + ); + assert_eq!( + DateTime::::parse_from_rfc3339("2015-02-18T23:16:09Z"), + Ok(FixedOffset::east_opt(0) + .unwrap() + .ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_opt(23, 16, 9) + .unwrap()) + ); + + assert_eq!( + edt.ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + .to_rfc3339(), + "2015-02-18T23:59:60.234567+05:00" + ); + assert_eq!( + edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap().to_rfc3339(), + "2015-02-18T23:16:09.150+05:00" + ); + assert_eq!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 1_234_567).unwrap()) + ); + assert_eq!( + DateTime::::parse_from_rfc3339("2015-02-18T23:16:09Z"), Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) ); assert_eq!( @@ -447,6 +575,44 @@ fn test_datetime_rfc2822_and_rfc3339() { ) .unwrap()) ); + assert_eq!( + Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap().to_rfc3339(), + "2015-02-18T23:16:09+00:00" + ); + + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567 +05:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:059:60.234567+05:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00PST").is_err() + ); + assert!(DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+PST").is_err()); + assert!(DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567PST").is_err()); + assert!(DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+0500").is_err()); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18 23:59:60.234567+05:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567:+05:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00 ").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339(" 2015-02-18T23:59:60.234567+05:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015- 02-18T23:59:60.234567+05:00").is_err() + ); + assert!( + DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567A+05:00").is_err() + ); } #[test] @@ -590,7 +756,94 @@ fn test_datetime_from_str() { } #[test] -fn test_datetime_parse_from_str() { +fn test_parse_datetime_utc() { + // valid cases + let valid = [ + "2001-02-03T04:05:06Z", + "2001-02-03T04:05:06+0000", + "2001-02-03T04:05:06-00:00", + "2001-02-03T04:05:06-01:00", + "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", + "-77-02-18T23:16:09Z", + "+82701-05-6T15:9:60.898989898989Z", + ]; + for &s in &valid { + eprintln!("test_parse_datetime_utc valid {:?}", s); + let d = match s.parse::>() { + Ok(d) => d, + Err(e) => panic!("parsing `{}` has failed: {}", s, e), + }; + let s_ = format!("{:?}", d); + // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same + let d_ = match s_.parse::>() { + Ok(d) => d, + Err(e) => { + panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + } + }; + assert!( + d == d_, + "`{}` is parsed into `{:?}`, but reparsed result \ + `{:?}` does not match", + s, + d, + d_ + ); + } + + // some invalid cases + // since `ParseErrorKind` is private, all we can do is to check if there was an error + let invalid = [ + "", // empty + "Z", // missing data + "15Z", // missing data + "15:8:9Z", // missing date + "15-8-9Z", // missing time or date + "Fri, 09 Aug 2013 23:54:35 GMT", // valid datetime, wrong format + "Sat Jun 30 23:59:60 2012", // valid datetime, wrong format + "1441497364.649", // valid datetime, wrong format + "+1441497364.649", // valid datetime, wrong format + "+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:12Z", // wrong divider 't' + "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 { + eprintln!("test_parse_datetime_utc invalid {:?}", s); + assert!(s.parse::>().is_err()); + } +} + +#[test] +fn test_utc_datetime_from_str() { let ymdhms = |y, m, d, h, n, s, off| { FixedOffset::east_opt(off).unwrap().with_ymd_and_hms(y, m, d, h, n, s).unwrap() }; @@ -605,6 +858,645 @@ fn test_datetime_parse_from_str() { Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap()) ); + + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::>(), + Ok(FixedOffset::east_opt(0) + .unwrap() + .ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::>(), + Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15 UTC".parse::>(), + Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15UTC".parse::>(), + Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) + ); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::>(), + Ok(FixedOffset::east_opt(0) + .unwrap() + .ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap()) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::>(), + Ok(FixedOffset::west_opt(10 * 3600) + .unwrap() + .ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(13, 16, 9, 150) + .unwrap()) + ); + assert!("2015-2-18T23:16:9.15".parse::>().is_err()); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::>(), + Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::>(), + Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) + ); + assert!("2015-2-18T23:16:9.15".parse::>().is_err()); + + // no test for `DateTime`, we cannot verify that much. +} + +#[test] +fn test_utc_datetime_from_str_with_spaces() { + let dt = Utc.ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap(); + // with varying spaces - should succeed + 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 "), 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("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "), + Ok(dt), + ); + assert_eq!(Utc.datetime_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), 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\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 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 + assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err()); + // trailing tab in data + assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err()); + // mismatched newlines + assert!(Utc.datetime_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err()); + // trailing literal in data + assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err()); +} + +#[test] +fn test_datetime_parse_from_str() { + let dt = FixedOffset::east_opt(-9 * 60 * 60) + .unwrap() + .ymd_opt(2013, 8, 9) + .unwrap() + .and_hms_opt(23, 54, 35) + .unwrap(); + + // timezone variations + + // + // %Z + // + // wrong timezone format + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %Z" + ) + .is_err()); + // bad timezone data? + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 PST", + "%b %d %Y %H:%M:%S %Z" + ) + .is_err()); + // bad timezone data + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 XXXXX", + "%b %d %Y %H:%M:%S %Z" + ) + .is_err()); + + // + // %z + // + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09 00", + "%b %d %Y %H:%M:%S %z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00", + "%b %d %Y %H:%M:%S %z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09 : 00", + "%b %d %Y %H:%M:%S %z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 --0900", + "%b %d %Y %H:%M:%S -%z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 +-0900", + "%b %d %Y %H:%M:%S +%z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00 ", + "%b %d %Y %H:%M:%S %z " + ), + Ok(dt), + ); + // trailing newline after timezone + 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), + ); + // trailing colon + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:", + "%b %d %Y %H:%M:%S %z" + ) + .is_err()); + // trailing colon with space + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00: ", + "%b %d %Y %H:%M:%S %z " + ) + .is_err()); + // trailing colon, mismatch space + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:", + "%b %d %Y %H:%M:%S %z " + ) + .is_err()); + // wrong timezone data + 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::00", + "%b %d %Y %H:%M:%S %z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900::", + "%b %d %Y %H:%M:%S %z::" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00", + "%b %d %Y %H:%M:%S %z:00" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00 ", + "%b %d %Y %H:%M:%S %z:00 " + ), + Ok(dt), + ); + + // + // %:z + // + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00", + "%b %d %Y %H:%M:%S %:z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %:z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09 00", + "%b %d %Y %H:%M:%S %:z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09 : 00", + "%b %d %Y %H:%M:%S %:z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09 : 00:", + "%b %d %Y %H:%M:%S %:z:" + ), + Ok(dt), + ); + // wrong timezone data + 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::00", + "%b %d %Y %H:%M:%S %:z" + ), + Ok(dt), + ); + // timezone data hs too many colons + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:", + "%b %d %Y %H:%M:%S %:z" + ) + .is_err()); + // timezone data hs too many colons + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00::", + "%b %d %Y %H:%M:%S %:z" + ) + .is_err()); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00::", + "%b %d %Y %H:%M:%S %:z::" + ), + Ok(dt), + ); + + // + // %:::z + // + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %::z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00", + "%b %d %Y %H:%M:%S %::z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09 : 00", + "%b %d %Y %H:%M:%S %::z" + ), + Ok(dt), + ); + // mismatching colon expectations + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00", + "%b %d %Y %H:%M:%S %::z" + ) + .is_err()); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09::00", + "%b %d %Y %H:%M:%S %::z" + ), + Ok(dt), + ); + // wrong timezone data + 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 -09001234", + "%b %d %Y %H:%M:%S %::z1234" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:001234", + "%b %d %Y %H:%M:%S %::z1234" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900 ", + "%b %d %Y %H:%M:%S %::z " + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900\t\n", + "%b %d %Y %H:%M:%S %::z\t\n" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900:", + "%b %d %Y %H:%M:%S %::z:" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 :-0900:0", + "%b %d %Y %H:%M:%S :%::z:0" + ), + Ok(dt), + ); + // mismatching colons and spaces + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 :-0900: ", + "%b %d %Y %H:%M:%S :%::z::" + ) + .is_err()); + // mismatching colons expectations + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00", + "%b %d %Y %H:%M:%S %::z" + ) + .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_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 :-0900:0 23:54:35", + "%b %d %Y :%::z:0 %H:%M:%S" + ), + Ok(dt), + ); + // mismatching colons expectations mid-string + assert!(DateTime::::parse_from_str( + "Aug 09 2013 :-0900: 23:54:35", + "%b %d %Y :%::z %H:%M:%S" + ) + .is_err()); + // mismatching colons expectations, before end + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00 ", + "%b %d %Y %H:%M:%S %::z " + ) + .is_err()); + + // + // %:::z + // + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00", + "%b %d %Y %H:%M:%S %:::z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %:::z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900 ", + "%b %d %Y %H:%M:%S %:::z " + ), + Ok(dt), + ); + // wrong timezone data + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09", + "%b %d %Y %H:%M:%S %:::z" + ) + .is_err()); + + // + // %::::z + // + // too many colons + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %::::z" + ) + .is_err()); + // too many colons + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00", + "%b %d %Y %H:%M:%S %::::z" + ) + .is_err()); + // too many colons + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:", + "%b %d %Y %H:%M:%S %::::z" + ) + .is_err()); + // too many colons + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00", + "%b %d %Y %H:%M:%S %::::z" + ) + .is_err()); + + // + // %#z + // + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00", + "%b %d %Y %H:%M:%S %#z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%b %d %Y %H:%M:%S %#z" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00 ", + "%b %d %Y %H:%M:%S %#z " + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900 ", + "%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_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -0900", + "%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_eq!( + 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_eq!( + 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_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 -0900 23:54:35", + "%b %d %Y %#z%H:%M:%S" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 -090023:54:35", + "%b %d %Y %#z%H:%M:%S" + ), + Ok(dt), + ); + assert_eq!( + DateTime::::parse_from_str( + "Aug 09 2013 -09:0023:54:35", + "%b %d %Y %#z%H:%M:%S" + ), + Ok(dt), + ); + // timezone with partial minutes adjacent hours + assert_ne!( + DateTime::::parse_from_str("Aug 09 2013 -09023:54:35", "%b %d %Y %#z%H:%M:%S"), + Ok(dt), + ); + // bad timezone data + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -09:00:00", + "%b %d %Y %H:%M:%S %#z" + ) + .is_err()); + // bad timezone data (partial minutes) + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -090", + "%b %d %Y %H:%M:%S %#z" + ) + .is_err()); + // bad timezone data (partial minutes) with trailing space + assert!(DateTime::::parse_from_str( + "Aug 09 2013 23:54:35 -090 ", + "%b %d %Y %H:%M:%S %#z " + ) + .is_err()); + // bad timezone data (partial minutes) mid-string + assert!(DateTime::::parse_from_str( + "Aug 09 2013 -090 23:54:35", + "%b %d %Y %#z %H:%M:%S" + ) + .is_err()); + // bad timezone data + assert!(DateTime::::parse_from_str( + "Aug 09 2013 -09:00:00 23:54:35", + "%b %d %Y %#z %H:%M:%S" + ) + .is_err()); + // timezone data ambiguous with hours + 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("0", "%s").unwrap(), NaiveDateTime::from_timestamp_opt(0, 0).unwrap().and_utc().fixed_offset() diff --git a/src/format/strftime.rs b/src/format/strftime.rs index 5ca7507043..50d4ac7cc3 100644 --- a/src/format/strftime.rs +++ b/src/format/strftime.rs @@ -508,12 +508,32 @@ mod tests { fn test_strftime_items() { fn parse_and_collect(s: &str) -> Vec> { // map any error into `[Item::Error]`. useful for easy testing. + eprintln!("test_strftime_items: parse_and_collect({:?})", s); let items = StrftimeItems::new(s); let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) }); items.collect::>>().unwrap_or_else(|| vec![Item::Error]) } assert_eq!(parse_and_collect(""), []); + assert_eq!(parse_and_collect(" "), [Space(" ")]); + assert_eq!(parse_and_collect(" "), [Space(" ")]); + // ne! + assert_ne!(parse_and_collect(" "), [Space(" "), Space(" ")]); + // eq! + assert_eq!(parse_and_collect(" "), [Space(" ")]); + assert_eq!(parse_and_collect("a"), [Literal("a")]); + assert_eq!(parse_and_collect("ab"), [Literal("ab")]); + assert_eq!(parse_and_collect("😽"), [Literal("😽")]); + assert_eq!(parse_and_collect("a😽"), [Literal("a😽")]); + assert_eq!(parse_and_collect("😽a"), [Literal("😽a")]); + assert_eq!(parse_and_collect(" 😽"), [Space(" "), Literal("😽")]); + assert_eq!(parse_and_collect("😽 "), [Literal("😽"), Space(" ")]); + // ne! + assert_ne!(parse_and_collect("😽😽"), [Literal("😽")]); + assert_ne!(parse_and_collect("😽"), [Literal("😽😽")]); + assert_ne!(parse_and_collect("😽😽"), [Literal("😽😽"), Literal("😽")]); + // eq! + assert_eq!(parse_and_collect("😽😽"), [Literal("😽😽")]); assert_eq!(parse_and_collect(" \t\n\r "), [Space(" \t\n\r ")]); assert_eq!(parse_and_collect("hello?"), [Literal("hello?")]); assert_eq!( @@ -530,12 +550,88 @@ mod tests { parse_and_collect("%Y-%m-%d"), [num0(Year), Literal("-"), num0(Month), Literal("-"), num0(Day)] ); + assert_eq!(parse_and_collect("😽 "), [Literal("😽"), Space(" ")]); + assert_eq!(parse_and_collect("😽😽"), [Literal("😽😽")]); + assert_eq!(parse_and_collect("😽😽😽"), [Literal("😽😽😽")]); + assert_eq!(parse_and_collect("😽😽 😽"), [Literal("😽😽"), Space(" "), Literal("😽")]); + assert_eq!(parse_and_collect("😽😽a 😽"), [Literal("😽😽a"), Space(" "), Literal("😽")]); + assert_eq!(parse_and_collect("😽😽a b😽"), [Literal("😽😽a"), Space(" "), Literal("b😽")]); + assert_eq!( + parse_and_collect("😽😽a b😽c"), + [Literal("😽😽a"), Space(" "), Literal("b😽c")] + ); + assert_eq!(parse_and_collect("😽😽 "), [Literal("😽😽"), Space(" ")]); + assert_eq!(parse_and_collect("😽😽 😽"), [Literal("😽😽"), Space(" "), Literal("😽")]); + assert_eq!(parse_and_collect(" 😽"), [Space(" "), Literal("😽")]); + assert_eq!(parse_and_collect(" 😽 "), [Space(" "), Literal("😽"), Space(" ")]); + assert_eq!( + parse_and_collect(" 😽 😽"), + [Space(" "), Literal("😽"), Space(" "), Literal("😽")] + ); + assert_eq!( + parse_and_collect(" 😽 😽 "), + [Space(" "), Literal("😽"), Space(" "), Literal("😽"), Space(" ")] + ); + assert_eq!( + parse_and_collect(" 😽 😽 "), + [Space(" "), Literal("😽"), Space(" "), Literal("😽"), Space(" ")] + ); + assert_eq!( + parse_and_collect(" 😽 😽😽 "), + [Space(" "), Literal("😽"), Space(" "), Literal("😽😽"), Space(" ")] + ); + assert_eq!(parse_and_collect(" 😽😽"), [Space(" "), Literal("😽😽")]); + assert_eq!(parse_and_collect(" 😽😽 "), [Space(" "), Literal("😽😽"), Space(" ")]); + assert_eq!( + parse_and_collect(" 😽😽 "), + [Space(" "), Literal("😽😽"), Space(" ")] + ); + assert_eq!( + parse_and_collect(" 😽😽 "), + [Space(" "), Literal("😽😽"), Space(" ")] + ); + assert_eq!(parse_and_collect(" 😽😽 "), [Space(" "), Literal("😽😽"), Space(" ")]); + assert_eq!( + parse_and_collect(" 😽 😽😽 "), + [Space(" "), Literal("😽"), Space(" "), Literal("😽😽"), Space(" ")] + ); + assert_eq!( + parse_and_collect(" 😽 πŸ˜½γ―γ„πŸ˜½ ハンバーガー"), + [ + Space(" "), + Literal("😽"), + Space(" "), + Literal("πŸ˜½γ―γ„πŸ˜½"), + Space(" "), + Literal("ハンバーガー") + ] + ); + assert_eq!( + parse_and_collect("%%😽%%😽"), + [Literal("%"), Literal("😽"), Literal("%"), Literal("😽")] + ); + assert_eq!(parse_and_collect("%Y--%m"), [num0(Year), Literal("--"), num0(Month)]); assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]")); + assert_eq!(parse_and_collect("100%%😽"), [Literal("100"), Literal("%"), Literal("😽")]); + assert_eq!( + parse_and_collect("100%%😽%%a"), + [Literal("100"), Literal("%"), Literal("😽"), Literal("%"), Literal("a")] + ); + assert_eq!(parse_and_collect("😽100%%"), [Literal("😽100"), Literal("%")]); assert_eq!(parse_and_collect("%m %d"), [num0(Month), Space(" "), num0(Day)]); assert_eq!(parse_and_collect("%"), [Item::Error]); assert_eq!(parse_and_collect("%%"), [Literal("%")]); assert_eq!(parse_and_collect("%%%"), [Item::Error]); + assert_eq!(parse_and_collect("%a"), [fixed(Fixed::ShortWeekdayName)]); + assert_eq!(parse_and_collect("%aa"), [fixed(Fixed::ShortWeekdayName), Literal("a")]); + assert_eq!(parse_and_collect("%%a%"), [Item::Error]); + assert_eq!(parse_and_collect("%😽"), [Item::Error]); + assert_eq!(parse_and_collect("%😽😽"), [Item::Error]); assert_eq!(parse_and_collect("%%%%"), [Literal("%"), Literal("%")]); + assert_eq!( + parse_and_collect("%%%%ハンバーガー"), + [Literal("%"), Literal("%"), Literal("ハンバーガー")] + ); assert_eq!(parse_and_collect("foo%?"), [Item::Error]); assert_eq!(parse_and_collect("bar%42"), [Item::Error]); assert_eq!(parse_and_collect("quux% +"), [Item::Error]); @@ -555,6 +651,10 @@ mod tests { assert_eq!(parse_and_collect("%0e"), [num0(Day)]); assert_eq!(parse_and_collect("%_e"), [nums(Day)]); assert_eq!(parse_and_collect("%z"), [fixed(Fixed::TimezoneOffset)]); + assert_eq!(parse_and_collect("%:z"), [fixed(Fixed::TimezoneOffsetColon)]); + assert_eq!(parse_and_collect("%Z"), [fixed(Fixed::TimezoneName)]); + assert_eq!(parse_and_collect("%ZZZZ"), [fixed(Fixed::TimezoneName), Literal("ZZZ")]); + assert_eq!(parse_and_collect("%Z😽"), [fixed(Fixed::TimezoneName), Literal("😽")]); assert_eq!( parse_and_collect("%#z"), [internal_fixed(InternalInternal::TimezoneOffsetPermissive)] @@ -664,6 +764,13 @@ mod tests { assert_eq!(dt.format("%t").to_string(), "\t"); assert_eq!(dt.format("%n").to_string(), "\n"); assert_eq!(dt.format("%%").to_string(), "%"); + + // complex format specifiers + assert_eq!(dt.format(" %Y%d%m%%%%%t%H%M%S\t").to_string(), " 20010807%%\t003460\t"); + assert_eq!( + dt.format(" %Y%d%m%%%%%t%H:%P:%M%S%:::z\t").to_string(), + " 20010807%%\t00:am:3460+09\t" + ); } #[test] diff --git a/src/naive/date.rs b/src/naive/date.rs index 3acbc1fe03..1f7838847b 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -2967,16 +2967,20 @@ mod tests { "360-02-29", "0360-02-29", "2015-2 -18", + "2015-02-18", "+70-2-18", "+70000-2-18", "+00007-2-18", ]; for &s in &valid { + eprintln!("test_date_from_str valid {:?}", s); let d = match s.parse::() { Ok(d) => d, Err(e) => panic!("parsing `{}` has failed: {}", s, e), }; + eprintln!("d {:?} (NaiveDate)", d); let s_ = format!("{:?}", d); + eprintln!("s_ {:?}", s_); // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same let d_ = match s_.parse::() { Ok(d) => d, @@ -2984,6 +2988,7 @@ mod tests { panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) } }; + eprintln!("d_ {:?} (NaiveDate)", d_); assert!( d == d_, "`{}` is parsed into `{:?}`, but reparsed result \ @@ -2996,13 +3001,27 @@ mod tests { // some invalid cases // since `ParseErrorKind` is private, all we can do is to check if there was an error - assert!("".parse::().is_err()); - assert!("x".parse::().is_err()); - assert!("2014".parse::().is_err()); - assert!("2014-01".parse::().is_err()); - assert!("2014-01-00".parse::().is_err()); - assert!("2014-13-57".parse::().is_err()); - assert!("9999999-9-9".parse::().is_err()); // out-of-bounds + let invalid = [ + "", // empty + "x", // invalid + "Fri, 09 Aug 2013 GMT", // valid date, wrong format + "Sat Jun 30 2012", // valid date, wrong format + "1441497364.649", // valid datetime, wrong format + "+1441497364.649", // valid datetime, wrong format + "+1441497364", // valid datetime, wrong format + "2014/02/03", // valid date, wrong format + "2014", // datetime missing data + "2014-01", // datetime missing data + "2014-01-00", // invalid day + "2014-11-32", // invalid day + "2014-13-01", // invalid month + "2014-13-57", // invalid month, day + "9999999-9-9", // invalid year (out of bounds) + ]; + for &s in &invalid { + eprintln!("test_date_from_str invalid {:?}", s); + assert!(s.parse::().is_err()); + } } #[test] diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 4656147d64..b16f68b9ec 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -199,11 +199,16 @@ fn test_datetime_timestamp() { fn test_datetime_from_str() { // valid cases let valid = [ - "2015-2-18T23:16:9.15", + "2001-02-03T04:05:06", + "2012-12-12T12:12:12", + "2015-02-18T23:16:09.153", + "2015-2-18T23:16:09.153", "-77-02-18T23:16:09", + "+82701-05-6T15:9:60.898989898989", " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", ]; for &s in &valid { + eprintln!("test_parse_naivedatetime valid {:?}", s); let d = match s.parse::() { Ok(d) => d, Err(e) => panic!("parsing `{}` has failed: {}", s, e), @@ -228,16 +233,34 @@ fn test_datetime_from_str() { // some invalid cases // since `ParseErrorKind` is private, all we can do is to check if there was an error - assert!("".parse::().is_err()); - assert!("x".parse::().is_err()); - assert!("15".parse::().is_err()); - assert!("15:8:9".parse::().is_err()); - assert!("15-8-9".parse::().is_err()); - assert!("2015-15-15T15:15:15".parse::().is_err()); - assert!("2012-12-12T12:12:12x".parse::().is_err()); - assert!("2012-123-12T12:12:12".parse::().is_err()); - assert!("+ 82701-123-12T12:12:12".parse::().is_err()); - assert!("+802701-123-12T12:12:12".parse::().is_err()); // out-of-bound + let invalid = [ + "", // 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 + "Sat Jun 30 23:59:60 2012", // valid date, wrong format + "1441497364.649", // valid date, wrong format + "+1441497364.649", // valid date, wrong format + "+1441497364", // valid date, wrong format + "2014/02/03 04:05:06", // valid date, wrong format + "2015-15-15T15:15:15", // invalid date + "2012-12-12T12:12:12x", // bad timezone / trailing literal + "2012-12-12T12:12:12+00:00", // unexpected timezone / trailing literal + "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", // 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 + ]; + for &s in &invalid { + eprintln!("test_datetime_from_str invalid {:?}", s); + assert!(s.parse::().is_err()); + } } #[test] diff --git a/src/naive/time/tests.rs b/src/naive/time/tests.rs index 72491fbd2c..3eb83ba167 100644 --- a/src/naive/time/tests.rs +++ b/src/naive/time/tests.rs @@ -235,9 +235,37 @@ fn test_date_from_str() { " 4 : 3 : 2.1 ", " 09:08:07 ", " 9:8:07 ", + "01:02:03", + "4:3:2.1", + "9:8:7", + "09:8:7", + "9:08:7", + "9:8:07", + "09:08:7", + "09:8:07", + "09:08:7", + "9:08:07", + "09:08:07", + "9:8:07.123", + "9:08:7.123", + "09:8:7.123", + "09:08:7.123", + "9:08:07.123", + "09:8:07.123", + "09:08:07.123", + "09:08:07.123", + "09:08:07.1234", + "09:08:07.12345", + "09:08:07.123456", + "09:08:07.1234567", + "09:08:07.12345678", + "09:08:07.123456789", + "09:08:07.1234567891", + "09:08:07.12345678912", "23:59:60.373929310237", ]; for &s in &valid { + eprintln!("test_time_parse_from_str valid {:?}", s); let d = match s.parse::() { Ok(d) => d, Err(e) => panic!("parsing `{}` has failed: {}", s, e), @@ -262,15 +290,30 @@ fn test_date_from_str() { // some invalid cases // since `ParseErrorKind` is private, all we can do is to check if there was an error - assert!("".parse::().is_err()); - assert!("x".parse::().is_err()); - assert!("15".parse::().is_err()); - assert!("15:8".parse::().is_err()); - assert!("15:8:x".parse::().is_err()); - assert!("15:8:9x".parse::().is_err()); - assert!("23:59:61".parse::().is_err()); - assert!("12:34:56.x".parse::().is_err()); - assert!("12:34:56. 0".parse::().is_err()); + let invalid = [ + "", // empty + "x", // invalid + "15", // missing data + "15:8", // missing data + "15:8:x", // missing data, invalid data + "15:8:9x", // missing data, invalid data + "23:59:61", // invalid second (out of bounds) + "23:54:35 GMT", // invalid (timezone non-sensical for NaiveTime) + "23:54:35 +0000", // invalid (timezone non-sensical for NaiveTime) + "1441497364.649", // valid datetime, not a NaiveTime + "+1441497364.649", // valid datetime, not a NaiveTime + "+1441497364", // valid datetime, not a NaiveTime + "001:02:03", // invalid hour + "01:002:03", // invalid minute + "01:02:003", // invalid second + "12:34:56.x", // invalid fraction + "12:34:56. 0", // invalid fraction format + "09:08:00000000007", // invalid second / invalid fraction format + ]; + for &s in &invalid { + eprintln!("test_time_parse_from_str invalid {:?}", s); + assert!(s.parse::().is_err()); + } } #[test] @@ -281,6 +324,15 @@ fn test_time_parse_from_str() { Ok(hms(12, 34, 56)) ); // ignore date and offset assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0))); + assert_eq!(NaiveTime::parse_from_str("12:59 \n\t PM", "%H:%M \n\t %P"), Ok(hms(12, 59, 0))); + assert_eq!(NaiveTime::parse_from_str("\t\t12:59\tPM\t", "\t\t%H:%M\t%P\t"), Ok(hms(12, 59, 0))); + assert_eq!( + NaiveTime::parse_from_str("\t\t1259\t\tPM\t", "\t\t%H%M\t\t%P\t"), + Ok(hms(12, 59, 0)) + ); + assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M\t%P").is_ok()); + assert!(NaiveTime::parse_from_str("\t\t12:59 PM\t", "\t\t%H:%M\t%P\t").is_ok()); + assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M %P").is_ok()); assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err()); }