From becc22219d12f7fd27b2811618e9d432baf4acc5 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 30 May 2023 18:09:48 +0200 Subject: [PATCH] Revert backport of #807 --- src/datetime/mod.rs | 5 - src/datetime/tests.rs | 616 ++---------------------------- src/format/mod.rs | 12 +- src/format/parse.rs | 732 +++++------------------------------- src/format/scan.rs | 100 +---- src/format/strftime.rs | 82 ---- src/naive/date.rs | 51 +-- src/naive/datetime/tests.rs | 66 +--- src/naive/time/mod.rs | 3 + src/naive/time/tests.rs | 85 +---- 10 files changed, 182 insertions(+), 1570 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 2e9203a307..65a39aa8da 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -560,9 +560,6 @@ 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!( @@ -570,8 +567,6 @@ 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 87f72f9c4c..c6be142d08 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -240,94 +240,6 @@ fn test_datetime_sub_months() { ); } -// local helper function to easily create a DateTime -fn ymdhms( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, -) -> DateTime { - fixedoffset.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap() -} - -// local helper function to easily create a DateTime -fn ymdhms_milli( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - milli: i64, -) -> DateTime { - fixedoffset - .with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .checked_add_signed(Duration::milliseconds(milli)) - .unwrap() -} - -// local helper function to easily create a DateTime -fn ymdhms_micro( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - micro: i64, -) -> DateTime { - fixedoffset - .with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .checked_add_signed(Duration::microseconds(micro)) - .unwrap() -} - -// local helper function to easily create a DateTime -fn ymdhms_nano( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - nano: i64, -) -> DateTime { - fixedoffset - .with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .checked_add_signed(Duration::nanoseconds(nano)) - .unwrap() -} - -// local helper function to easily create a DateTime -fn ymdhms_utc(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime { - Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap() -} - -// local helper function to easily create a DateTime -fn ymdhms_milli_utc( - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - milli: i64, -) -> DateTime { - Utc.with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .checked_add_signed(Duration::milliseconds(milli)) - .unwrap() -} - #[test] fn test_datetime_offset() { let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); @@ -432,15 +344,16 @@ fn test_datetime_with_timezone() { } #[test] -fn test_datetime_rfc2822() { +fn test_datetime_rfc2822_and_rfc3339() { 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" ); - // timezone +05 + assert_eq!( + Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(), + "2015-02-18T23:16:09+00:00" + ); assert_eq!( edt.from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) @@ -452,157 +365,55 @@ fn test_datetime_rfc2822() { .to_rfc2822(), "Wed, 18 Feb 2015 23:16:09 +0500" ); - // seconds 60 assert_eq!( edt.from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) .unwrap() - .and_hms_micro_opt(23, 59, 59, 1_234_567) + .and_hms_milli_opt(23, 16, 9, 150) .unwrap() ) .unwrap() - .to_rfc2822(), - "Wed, 18 Feb 2015 23:59:60 +0500" - ); - - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), - Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) - ); - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), - Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) - ); - assert_eq!( - ymdhms_milli(&edt, 2015, 2, 18, 23, 59, 58, 1_234_567).to_rfc2822(), - "Thu, 19 Feb 2015 00:20:32 +0500" - ); - assert_eq!( - DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:58 +0500"), - Ok(ymdhms(&edt, 2015, 2, 18, 23, 59, 58)) - ); - assert_ne!( - DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:58 +0500"), - Ok(ymdhms_milli(&edt, 2015, 2, 18, 23, 59, 58, 500)) - ); - - // 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:58 \t+0500" - ), - Ok(ymdhms(&edt, 2015, 2, 18, 23, 59, 58)) - ); - // 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( - ymdhms( - &FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60).unwrap(), - 1969, 2, 13, 23, 32, 0, - ) - ) + .to_rfc3339(), + "2015-02-18T23:16:09.150+05:00" ); - // 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( - ymdhms(&FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60).unwrap(), 1969, 2, 13, 23, 32, 0,) + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() ) + .unwrap() + .to_rfc2822(), + "Wed, 18 Feb 2015 23:59:60 +0500" ); - - // 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] -fn test_datetime_rfc3339() { - let edt5 = FixedOffset::east_opt(5 * 60 * 60).unwrap(); - let edt0 = FixedOffset::east_opt(0).unwrap(); - - // timezone 0 - assert_eq!( - 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!( - edt5.from_local_datetime( + edt.from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) + .and_hms_micro_opt(23, 59, 59, 1_234_567) .unwrap() ) .unwrap() .to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" - ); - - assert_eq!(ymdhms_utc(2015, 2, 18, 23, 16, 9).to_rfc3339(), "2015-02-18T23:16:09+00:00"); - assert_eq!( - ymdhms_milli(&edt5, 2015, 2, 18, 23, 16, 9, 150).to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" - ); - assert_eq!( - ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(), - "2015-02-19T00:00:00.234567+05:00" - ); - assert_eq!( - DateTime::::parse_from_rfc3339("2015-02-18T23:59:59.123+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 123_000)) - ); - assert_eq!( - DateTime::::parse_from_rfc3339("2015-02-18T23:59:59.123456+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 123_456)) - ); - assert_eq!( - DateTime::::parse_from_rfc3339("2015-02-18T23:59:59.123456789+05:00"), - Ok(ymdhms_nano(&edt5, 2015, 2, 18, 23, 59, 59, 123_456_789)) - ); - assert_eq!( - DateTime::::parse_from_rfc3339("2015-02-18T23:16:09Z"), - Ok(ymdhms(&edt0, 2015, 2, 18, 23, 16, 9)) + "2015-02-18T23:59:60.234567+05:00" ); assert_eq!( - ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(), - "2015-02-19T00:00:00.234567+05:00" - ); - assert_eq!( - ymdhms_milli(&edt5, 2015, 2, 18, 23, 16, 9, 150).to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), + 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-18T00:00:00.234567+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 0, 0, 0, 234_567)) + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), + 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"), - Ok(ymdhms(&edt0, 2015, 2, 18, 23, 16, 9)) + 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!( - DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), - Ok(edt5 + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), + Ok(edt .from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) .unwrap() @@ -613,8 +424,8 @@ fn test_datetime_rfc3339() { ); assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); assert_eq!( - DateTime::::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), - Ok(edt5 + DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + Ok(edt .from_local_datetime( &NaiveDate::from_ymd_opt(2015, 2, 18) .unwrap() @@ -623,41 +434,6 @@ fn test_datetime_rfc3339() { ) .unwrap()) ); - assert_eq!(ymdhms_utc(2015, 2, 18, 23, 16, 9).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] @@ -799,106 +575,13 @@ fn test_datetime_from_str() { } #[test] -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", - "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 - "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 - "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 - " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 Z", // valid datetime, wrong format - ]; - for &s in invalid.iter() { - eprintln!("test_parse_datetime_utc invalid {:?}", s); - assert!(s.parse::>().is_err()); - } -} - -#[test] -fn test_utc_datetime_from_str() { - let edt = FixedOffset::east_opt(570 * 60).unwrap(); - let edt0 = FixedOffset::east_opt(0).unwrap(); - let wdt = FixedOffset::west_opt(10 * 3600).unwrap(); +fn test_datetime_parse_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() + }; assert_eq!( - DateTime::::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(ymdhms(&edt, 2014, 5, 7, 12, 34, 56)) + DateTime::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, 570 * 60)) ); // ignore offset assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") @@ -907,235 +590,6 @@ fn test_utc_datetime_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(ymdhms_milli(&edt0, 2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)), - ); - assert_eq!( - "2015-02-18T23:16:9.15 UTC".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15UTC".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli(&edt0, 2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(ymdhms_milli(&wdt, 2015, 2, 18, 13, 16, 9, 150)) - ); - assert!("2015-2-18T23:16:9.15".parse::>().is_err()); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - 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 = ymdhms_utc(2013, 8, 9, 23, 54, 35); - // 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),); - // 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 - 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 = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35); - let parse = DateTime::::parse_from_str; - - // timezone variations - - // - // %Z - // - // wrong timezone format - assert!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %Z").is_err()); - // bad timezone data? - assert!(parse("Aug 09 2013 23:54:35 PST", "%b %d %Y %H:%M:%S %Z").is_err()); - // bad timezone data - assert!(parse("Aug 09 2013 23:54:35 XXXXX", "%b %d %Y %H:%M:%S %Z").is_err()); - - // - // %z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 --0900", "%b %d %Y %H:%M:%S -%z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 +-0900", "%b %d %Y %H:%M:%S +%z"), Ok(dt)); - 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()); - // 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 - assert!(parse("Aug 09 2013 23:54:35 -09:00: ", "%b %d %Y %H:%M:%S %z ").is_err()); - // trailing colon, mismatch space - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %z ").is_err()); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900::", "%b %d %Y %H:%M:%S %z::"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %z:00"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00:00 ", "%b %d %Y %H:%M:%S %z:00 "), Ok(dt)); - - // - // %:z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - assert!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %:z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %:z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09 : 00:", "%b %d %Y %H:%M:%S %:z:").is_err()); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %:z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %:z").is_err()); - // timezone data hs too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %:z").is_err()); - // timezone data hs too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00::", "%b %d %Y %H:%M:%S %:z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00::", "%b %d %Y %H:%M:%S %:z::"), Ok(dt)); - - // - // %::z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %::z"), Ok(dt)); - assert!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %::z").is_err()); - // mismatching colon expectations - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %::z").is_err()); - assert!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %:z").is_err()); - // wrong timezone data - 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 -09001234", "%b %d %Y %H:%M:%S %::z1234"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:001234", "%b %d %Y %H:%M:%S %::z1234"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%b %d %Y %H:%M:%S %::z "), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900\t\n", "%b %d %Y %H:%M:%S %::z\t\n"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900:", "%b %d %Y %H:%M:%S %::z:"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 :-0900:0", "%b %d %Y %H:%M:%S :%::z:0"), Ok(dt)); - // mismatching colons and spaces - assert!(parse("Aug 09 2013 23:54:35 :-0900: ", "%b %d %Y %H:%M:%S :%::z::").is_err()); - // mismatching colons expectations - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::z").is_err()); - 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 :-0900:0 23:54:35", "%b %d %Y :%::z:0 %H:%M:%S"), Ok(dt)); - // mismatching colons expectations mid-string - assert!(parse("Aug 09 2013 :-0900: 23:54:35", "%b %d %Y :%::z %H:%M:%S").is_err()); - // mismatching colons expectations, before end - assert!(parse("Aug 09 2013 23:54:35 -09:00:00 ", "%b %d %Y %H:%M:%S %::z ").is_err()); - - // - // %:::z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %:::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %:::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%b %d %Y %H:%M:%S %:::z "), Ok(dt)); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %:::z").is_err()); - - // - // %::::z - // - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %::::z").is_err()); - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %::::z").is_err()); - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %::::z").is_err()); - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::::z").is_err()); - - // - // %#z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00 ", "%b %d %Y %H:%M:%S %#z "), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%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_eq!(parse("Aug 09 2013 23:54:35 -0900", "%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 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 -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 - assert_ne!(parse("Aug 09 2013 -09023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); - // bad timezone data - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %#z").is_err()); - // bad timezone data (partial minutes) - assert!(parse("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!(parse("Aug 09 2013 23:54:35 -090 ", "%b %d %Y %H:%M:%S %#z ").is_err()); - // bad timezone data (partial minutes) mid-string - assert!(parse("Aug 09 2013 -090 23:54:35", "%b %d %Y %#z %H:%M:%S").is_err()); - // bad timezone data - assert!(parse("Aug 09 2013 -09:00:00 23:54:35", "%b %d %Y %#z %H:%M:%S").is_err()); - // timezone data ambiguous with hours - assert!(parse("Aug 09 2013 -09:00:23:54:35", "%b %d %Y %#z%H:%M:%S").is_err()); } #[test] diff --git a/src/format/mod.rs b/src/format/mod.rs index 75c0194fd5..93c9f86d09 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -207,27 +207,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 `-0400` or `+00:00`). + /// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`). /// - /// In the parser, the colon may be omitted, + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. /// 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 may be omitted, + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. /// 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 may be omitted, + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. /// 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 `-0400` or `Z`). + /// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`). /// - /// In the parser, the colon may be omitted, + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace, /// 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. diff --git a/src/format/parse.rs b/src/format/parse.rs index 4a0ceb0f54..ca8193e15d 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -42,10 +42,6 @@ fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<() }) } -/// Parse an RFC 2822 format datetime -/// e.g. `Fri, 21 Nov 1997 09:55:06 -0600` -/// -/// This function allows arbitrary intermixed whitespace per RFC 2822 appendix A.5 fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> { macro_rules! try_consume { ($e:expr) => {{ @@ -241,7 +237,7 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st /// /// - 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 +322,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_left(); } #[cfg(any(feature = "alloc", feature = "std", test))] - 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_left(); } Item::Numeric(ref spec, ref _pad) => { @@ -389,6 +361,7 @@ where Internal(ref int) => match int._dummy {}, }; + s = s.trim_left(); let v = if signed { if s.starts_with('-') { let v = try_consume!(scan::number(&s[1..], 1, usize::MAX)); @@ -481,26 +454,26 @@ where | &TimezoneOffsetDoubleColon | &TimezoneOffsetTripleColon | &TimezoneOffset => { - s = scan::trim1(s); - let offset = - try_consume!(scan::timezone_offset(s, scan::consume_colon_maybe)); + let offset = try_consume!(scan::timezone_offset( + s.trim_left(), + scan::colon_or_space + )); parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?; } &TimezoneOffsetColonZ | &TimezoneOffsetZ => { - s = scan::trim1(s); - let offset = - try_consume!(scan::timezone_offset_zulu(s, scan::consume_colon_maybe)); + let offset = try_consume!(scan::timezone_offset_zulu( + s.trim_left(), + scan::colon_or_space + )); parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?; } - &Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive, }) => { - s = scan::trim1(s); let offset = try_consume!(scan::timezone_offset_permissive( - s, - scan::consume_colon_maybe + s.trim_left(), + scan::colon_or_space )); parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?; } @@ -525,14 +498,15 @@ where } /// Accepts a relaxed form of RFC3339. -/// A space or a 'T' are accepted as the separator between the date and time -/// parts. +/// A space or a 'T' are acepted as the separator between the date and time +/// parts. Additional spaces are allowed between each component. /// +/// All of these examples are equivalent: /// ``` /// # use chrono::{DateTime, offset::FixedOffset}; -/// "2000-01-02T03:04:05Z".parse::>()?; -/// "2000-01-02 03:04:05Z".parse::>()?; -/// # Ok::<(), chrono::ParseError>(()) +/// "2012-12-12T12:12:12Z".parse::>(); +/// "2012-12-12 12:12:12Z".parse::>(); +/// "2012- 12-12T12: 12:12Z".parse::>(); /// ``` impl str::FromStr for DateTime { type Err = ParseError; @@ -540,19 +514,25 @@ impl str::FromStr for DateTime { fn from_str(s: &str) -> ParseResult> { const DATE_ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Year, Pad::Zero), + Item::Space(""), Item::Literal("-"), Item::Numeric(Numeric::Month, Pad::Zero), + Item::Space(""), Item::Literal("-"), Item::Numeric(Numeric::Day, Pad::Zero), ]; const TIME_ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Hour, Pad::Zero), + Item::Space(""), Item::Literal(":"), Item::Numeric(Numeric::Minute, Pad::Zero), + Item::Space(""), Item::Literal(":"), Item::Numeric(Numeric::Second, Pad::Zero), Item::Fixed(Fixed::Nanosecond), + Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ), + Item::Space(""), ]; let mut parsed = Parsed::new(); @@ -574,6 +554,7 @@ impl str::FromStr for DateTime { #[cfg(test)] #[test] fn test_parse() { + use super::IMPOSSIBLE; use super::*; // workaround for Rust issue #22255 @@ -585,11 +566,9 @@ fn test_parse() { macro_rules! check { ($fmt:expr, $items:expr; $err:tt) => ( - eprintln!("test_parse: format {:?}", $fmt); assert_eq!(parse_all($fmt, &$items), Err($err)) ); ($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] { - eprintln!("test_parse: format {:?}", $fmt); let mut expected = Parsed::new(); $(expected.$k = Some($v);)* assert_eq!(parse_all($fmt, &$items), Ok(expected)) @@ -600,126 +579,35 @@ fn test_parse() { check!("", []; ); check!(" ", []; TOO_LONG); check!("a", []; TOO_LONG); - check!("abc", []; TOO_LONG); - check!("🀠", []; TOO_LONG); // whitespaces check!("", [sp!("")]; ); - check!(" ", [sp!(" ")]; ); - check!(" ", [sp!(" ")]; ); - check!(" ", [sp!(" ")]; ); - check!(" ", [sp!("")]; TOO_LONG); - check!(" ", [sp!(" ")]; TOO_LONG); - check!(" ", [sp!(" ")]; TOO_LONG); - check!(" ", [sp!(" ")]; TOO_LONG); - check!("", [sp!(" ")]; TOO_SHORT); - check!(" ", [sp!(" ")]; TOO_SHORT); - check!(" ", [sp!(" ")]; TOO_SHORT); - check!(" ", [sp!(" "), sp!(" ")]; TOO_SHORT); - check!(" ", [sp!(" "), sp!(" ")]; TOO_SHORT); - check!(" ", [sp!(" "), sp!(" ")]; ); - check!(" ", [sp!(" "), sp!(" ")]; ); - check!(" ", [sp!(" "), sp!(" ")]; ); - check!(" ", [sp!(" "), sp!(" "), sp!(" ")]; ); - check!("\t", [sp!("")]; TOO_LONG); - check!(" \n\r \n", [sp!("")]; TOO_LONG); - check!("\t", [sp!("\t")]; ); - check!("\t", [sp!(" ")]; INVALID); - check!(" ", [sp!("\t")]; INVALID); - check!("\t\r", [sp!("\t\r")]; ); - check!("\t\r ", [sp!("\t\r ")]; ); - check!("\t \r", [sp!("\t \r")]; ); - check!(" \t\r", [sp!(" \t\r")]; ); - check!(" \n\r \n", [sp!(" \n\r \n")]; ); - check!(" \t\n", [sp!(" \t")]; TOO_LONG); - check!(" \n\t", [sp!(" \t\n")]; INVALID); - check!("\u{2002}", [sp!("\u{2002}")]; ); - // most unicode whitespace characters - check!( - "\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}", - [sp!("\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}")]; - ); - // most unicode whitespace characters - check!( - "\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}", - [ - sp!("\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}"), - sp!("\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}") - ]; - ); + check!(" ", [sp!("")]; ); + check!("\t", [sp!("")]; ); + check!(" \n\r \n", [sp!("")]; ); check!("a", [sp!("")]; TOO_LONG); - check!("a", [sp!(" ")]; INVALID); - // a Space containing a literal can match a literal, but this should not be done - check!("a", [sp!("a")]; ); - check!("abc", [sp!("")]; TOO_LONG); - check!("abc", [sp!(" ")]; INVALID); - check!(" abc", [sp!("")]; TOO_LONG); - check!(" abc", [sp!(" ")]; TOO_LONG); - - // `\u{0363}` is combining diacritic mark "COMBINING LATIN SMALL LETTER A" // literal - check!("", [lit!("")]; ); check!("", [lit!("a")]; TOO_SHORT); check!(" ", [lit!("a")]; INVALID); check!("a", [lit!("a")]; ); - // a Literal may contain whitespace and match whitespace, but this should not be done - check!(" ", [lit!(" ")]; ); check!("aa", [lit!("a")]; TOO_LONG); - check!("🀠", [lit!("a")]; INVALID); check!("A", [lit!("a")]; INVALID); - check!("a", [lit!("z")]; INVALID); - check!("a", [lit!("🀠")]; TOO_SHORT); - check!("a", [lit!("\u{0363}a")]; TOO_SHORT); - check!("\u{0363}a", [lit!("a")]; INVALID); - check!("\u{0363}a", [lit!("\u{0363}a")]; ); - check!("a", [lit!("ab")]; TOO_SHORT); - check!("xy", [lit!("xy")]; ); - check!("xy", [lit!("x"), lit!("y")]; ); - check!("1", [lit!("1")]; ); - check!("1234", [lit!("1234")]; ); - check!("+1234", [lit!("+1234")]; ); - check!("PST", [lit!("PST")]; ); - check!("🀠", [lit!("🀠")]; ); - check!("🀠a", [lit!("🀠"), lit!("a")]; ); - check!("🀠a🀠", [lit!("🀠"), lit!("a🀠")]; ); - check!("a🀠b", [lit!("a"), lit!("🀠"), lit!("b")]; ); - // literals can be together check!("xy", [lit!("xy")]; ); - check!("xyz", [lit!("xyz")]; ); - // or literals can be apart check!("xy", [lit!("x"), lit!("y")]; ); - check!("xyz", [lit!("x"), lit!("yz")]; ); - check!("xyz", [lit!("xy"), lit!("z")]; ); - check!("xyz", [lit!("x"), lit!("y"), lit!("z")]; ); - // check!("x y", [lit!("x"), lit!("y")]; INVALID); check!("xy", [lit!("x"), sp!(""), lit!("y")]; ); - check!("x y", [lit!("x"), sp!(""), lit!("y")]; INVALID); - check!("x y", [lit!("x"), sp!(" "), lit!("y")]; ); - - // whitespaces + literals - check!("a\n", [lit!("a"), sp!("\n")]; ); - check!("\tab\n", [sp!("\t"), lit!("ab"), sp!("\n")]; ); - check!("ab\tcd\ne", [lit!("ab"), sp!("\t"), lit!("cd"), sp!("\n"), lit!("e")]; ); - check!("+1ab\tcd\r\n+,.", [lit!("+1ab"), sp!("\t"), lit!("cd"), sp!("\r\n"), lit!("+,.")]; ); - // whitespace and literals can be intermixed - check!("a\tb", [lit!("a\tb")]; ); - check!("a\tb", [lit!("a"), sp!("\t"), lit!("b")]; ); + check!("x y", [lit!("x"), sp!(""), lit!("y")]; ); // numeric check!("1987", [num!(Year)]; year: 1987); check!("1987 ", [num!(Year)]; TOO_LONG); check!("0x12", [num!(Year)]; TOO_LONG); // `0` is parsed check!("x123", [num!(Year)]; INVALID); - check!("o123", [num!(Year)]; INVALID); check!("2015", [num!(Year)]; year: 2015); check!("0000", [num!(Year)]; year: 0); check!("9999", [num!(Year)]; year: 9999); - check!(" \t987", [num!(Year)]; INVALID); - check!(" \t987", [sp!(" \t"), num!(Year)]; year: 987); - check!(" \t987🀠", [sp!(" \t"), num!(Year), lit!("🀠")]; year: 987); - check!("987🀠", [num!(Year), lit!("🀠")]; year: 987); + check!(" \t987", [num!(Year)]; year: 987); check!("5", [num!(Year)]; year: 5); check!("5\0", [num!(Year)]; TOO_LONG); check!("\x005", [num!(Year)]; INVALID); @@ -728,16 +616,12 @@ fn test_parse() { check!("12345", [nums!(Year), lit!("5")]; year: 1234); check!("12345", [num0!(Year), lit!("5")]; year: 1234); check!("12341234", [num!(Year), num!(Year)]; year: 1234); - check!("1234 1234", [num!(Year), num!(Year)]; INVALID); - check!("1234 1234", [num!(Year), sp!(" "), num!(Year)]; year: 1234); - check!("1234 1235", [num!(Year), num!(Year)]; INVALID); + check!("1234 1234", [num!(Year), num!(Year)]; year: 1234); + check!("1234 1235", [num!(Year), num!(Year)]; IMPOSSIBLE); check!("1234 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); check!("1234x1234", [num!(Year), lit!("x"), num!(Year)]; year: 1234); - check!("1234 x 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); check!("1234xx1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); - check!("1234xx1234", [num!(Year), lit!("xx"), num!(Year)]; year: 1234); - check!("1234 x 1234", [num!(Year), sp!(" "), lit!("x"), sp!(" "), num!(Year)]; year: 1234); - check!("1234 x 1235", [num!(Year), sp!(" "), lit!("x"), sp!(" "), lit!("1235")]; year: 1234); + check!("1234 x 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); // signed numeric check!("-42", [num!(Year)]; year: -42); @@ -746,18 +630,10 @@ fn test_parse() { check!("+0042", [num!(Year)]; year: 42); check!("-42195", [num!(Year)]; year: -42195); check!("+42195", [num!(Year)]; year: 42195); - check!(" -42195", [num!(Year)]; INVALID); - check!(" +42195", [num!(Year)]; INVALID); - check!(" -42195", [num!(Year)]; INVALID); - check!(" +42195", [num!(Year)]; INVALID); - check!("-42195 ", [num!(Year)]; TOO_LONG); - check!("+42195 ", [num!(Year)]; TOO_LONG); + check!(" -42195", [num!(Year)]; year: -42195); + check!(" +42195", [num!(Year)]; year: 42195); check!(" - 42", [num!(Year)]; INVALID); check!(" + 42", [num!(Year)]; INVALID); - check!(" -42195", [sp!(" "), num!(Year)]; year: -42195); - check!(" +42195", [sp!(" "), num!(Year)]; year: 42195); - check!(" - 42", [sp!(" "), num!(Year)]; INVALID); - check!(" + 42", [sp!(" "), num!(Year)]; INVALID); check!("-", [num!(Year)]; TOO_SHORT); check!("+", [num!(Year)]; TOO_SHORT); @@ -765,41 +641,26 @@ fn test_parse() { check!("345", [num!(Ordinal)]; ordinal: 345); check!("+345", [num!(Ordinal)]; INVALID); check!("-345", [num!(Ordinal)]; INVALID); - check!(" 345", [num!(Ordinal)]; INVALID); - check!("345 ", [num!(Ordinal)]; TOO_LONG); - check!(" 345", [sp!(" "), num!(Ordinal)]; ordinal: 345); - check!("345 ", [num!(Ordinal), sp!(" ")]; ordinal: 345); - check!("345🀠 ", [num!(Ordinal), lit!("🀠"), sp!(" ")]; ordinal: 345); - check!("345🀠", [num!(Ordinal)]; TOO_LONG); - check!("\u{0363}345", [num!(Ordinal)]; INVALID); + check!(" 345", [num!(Ordinal)]; ordinal: 345); check!(" +345", [num!(Ordinal)]; INVALID); check!(" -345", [num!(Ordinal)]; INVALID); - check!("\t345", [sp!("\t"), num!(Ordinal)]; ordinal: 345); - check!(" +345", [sp!(" "), num!(Ordinal)]; INVALID); - check!(" -345", [sp!(" "), num!(Ordinal)]; INVALID); // various numeric fields - check!("1234 5678", [num!(Year), num!(IsoYear)]; INVALID); check!("1234 5678", - [num!(Year), sp!(" "), num!(IsoYear)]; + [num!(Year), num!(IsoYear)]; year: 1234, isoyear: 5678); check!("12 34 56 78", [num!(YearDiv100), num!(YearMod100), num!(IsoYearDiv100), num!(IsoYearMod100)]; - INVALID); - check!("12 34🀠56 78", - [num!(YearDiv100), sp!(" "), num!(YearMod100), - lit!("🀠"), num!(IsoYearDiv100), sp!(" "), num!(IsoYearMod100)]; year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78); check!("1 2 3 4 5 6", - [num!(Month), sp!(" "), num!(Day), sp!(" "), num!(WeekFromSun), sp!(" "), - num!(WeekFromMon), sp!(" "), num!(IsoWeek), sp!(" "), num!(NumDaysFromSun)]; + [num!(Month), num!(Day), num!(WeekFromSun), num!(WeekFromMon), num!(IsoWeek), + num!(NumDaysFromSun)]; month: 1, day: 2, week_from_sun: 3, week_from_mon: 4, isoweek: 5, weekday: Weekday::Sat); check!("7 89 01", - [num!(WeekdayFromMon), sp!(" "), num!(Ordinal), sp!(" "), num!(Hour12)]; + [num!(WeekdayFromMon), num!(Ordinal), num!(Hour12)]; weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1); check!("23 45 6 78901234 567890123", - [num!(Hour), sp!(" "), num!(Minute), sp!(" "), num!(Second), sp!(" "), - num!(Nanosecond), sp!(" "), num!(Timestamp)]; + [num!(Hour), num!(Minute), num!(Second), num!(Nanosecond), num!(Timestamp)]; hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234, timestamp: 567_890_123); @@ -808,7 +669,6 @@ fn test_parse() { check!("Apr", [fix!(ShortMonthName)]; month: 4); check!("APR", [fix!(ShortMonthName)]; month: 4); check!("ApR", [fix!(ShortMonthName)]; month: 4); - check!("\u{0363}APR", [fix!(ShortMonthName)]; INVALID); check!("April", [fix!(ShortMonthName)]; TOO_LONG); // `Apr` is parsed check!("A", [fix!(ShortMonthName)]; TOO_SHORT); check!("Sol", [fix!(ShortMonthName)]; INVALID); @@ -846,15 +706,7 @@ fn test_parse() { check!("AM", [fix!(UpperAmPm)]; hour_div_12: 0); check!("PM", [fix!(UpperAmPm)]; hour_div_12: 1); check!("Am", [fix!(LowerAmPm)]; hour_div_12: 0); - check!(" Am", [sp!(" "), fix!(LowerAmPm)]; hour_div_12: 0); - check!("Am🀠", [fix!(LowerAmPm), lit!("🀠")]; hour_div_12: 0); - check!("🀠Am", [lit!("🀠"), fix!(LowerAmPm)]; hour_div_12: 0); - check!("\u{0363}am", [fix!(LowerAmPm)]; INVALID); - check!("\u{0360}am", [fix!(LowerAmPm)]; INVALID); check!(" Am", [fix!(LowerAmPm)]; INVALID); - check!("Am ", [fix!(LowerAmPm)]; TOO_LONG); - check!("a.m.", [fix!(LowerAmPm)]; INVALID); - check!("A.M.", [fix!(LowerAmPm)]; INVALID); check!("ame", [fix!(LowerAmPm)]; TOO_LONG); // `am` is parsed check!("a", [fix!(LowerAmPm)]; TOO_SHORT); check!("p", [fix!(LowerAmPm)]; TOO_SHORT); @@ -872,21 +724,11 @@ fn test_parse() { check!(".42", [fix!(Nanosecond)]; nanosecond: 420_000_000); check!(".421", [fix!(Nanosecond)]; nanosecond: 421_000_000); check!(".42195", [fix!(Nanosecond)]; nanosecond: 421_950_000); - check!(".421951", [fix!(Nanosecond)]; nanosecond: 421_951_000); - check!(".4219512", [fix!(Nanosecond)]; nanosecond: 421_951_200); - check!(".42195123", [fix!(Nanosecond)]; nanosecond: 421_951_230); check!(".421950803", [fix!(Nanosecond)]; nanosecond: 421_950_803); - check!(".4219508035", [fix!(Nanosecond)]; nanosecond: 421_950_803); - check!(".42195080354", [fix!(Nanosecond)]; nanosecond: 421_950_803); check!(".421950803547", [fix!(Nanosecond)]; nanosecond: 421_950_803); - check!(".000000003", [fix!(Nanosecond)]; nanosecond: 3); - check!(".0000000031", [fix!(Nanosecond)]; nanosecond: 3); - check!(".0000000035", [fix!(Nanosecond)]; nanosecond: 3); check!(".000000003547", [fix!(Nanosecond)]; nanosecond: 3); - check!(".0000000009", [fix!(Nanosecond)]; nanosecond: 0); check!(".000000000547", [fix!(Nanosecond)]; nanosecond: 0); - check!(".0000000009999999999999999999999999", [fix!(Nanosecond)]; nanosecond: 0); - check!(".4🀠", [fix!(Nanosecond), lit!("🀠")]; nanosecond: 400_000_000); + check!(".", [fix!(Nanosecond)]; TOO_SHORT); check!(".4x", [fix!(Nanosecond)]; TOO_LONG); check!(". 4", [fix!(Nanosecond)]; INVALID); check!(" .4", [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming @@ -898,12 +740,8 @@ fn test_parse() { check!("4", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); check!("42", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); check!("421", [internal_fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000); - check!("4210", [internal_fix!(Nanosecond3NoDot)]; TOO_LONG); check!("42143", [internal_fix!(Nanosecond3NoDot), num!(Second)]; nanosecond: 421_000_000, second: 43); - check!("421🀠", [internal_fix!(Nanosecond3NoDot), lit!("🀠")]; nanosecond: 421_000_000); - check!("🀠421", [lit!("🀠"), internal_fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000); check!("42195", [internal_fix!(Nanosecond3NoDot)]; TOO_LONG); - check!("123456789", [internal_fix!(Nanosecond3NoDot)]; TOO_LONG); check!("4x", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); check!(" 4", [internal_fix!(Nanosecond3NoDot)]; INVALID); check!(".421", [internal_fix!(Nanosecond3NoDot)]; INVALID); @@ -911,13 +749,10 @@ fn test_parse() { check!("", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); check!(".", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); check!("0", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); - check!("1234", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); - check!("12345", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("42195", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); check!("421950", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000); check!("000003", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 3000); check!("000000", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 0); - check!("1234567", [internal_fix!(Nanosecond6NoDot)]; TOO_LONG); - check!("123456789", [internal_fix!(Nanosecond6NoDot)]; TOO_LONG); check!("4x", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); check!(" 4", [internal_fix!(Nanosecond6NoDot)]; INVALID); check!(".42100", [internal_fix!(Nanosecond6NoDot)]; INVALID); @@ -925,375 +760,58 @@ fn test_parse() { check!("", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT); check!(".", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT); check!("42195", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT); - check!("12345678", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT); check!("421950803", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803); check!("000000003", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 3); check!("42195080354", [internal_fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9 - check!("1234567890", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG); + check!("421950803547", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG); check!("000000000", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 0); check!("00000000x", [internal_fix!(Nanosecond9NoDot)]; INVALID); check!(" 4", [internal_fix!(Nanosecond9NoDot)]; INVALID); check!(".42100000", [internal_fix!(Nanosecond9NoDot)]; INVALID); // fixed: timezone offsets - - // TimezoneOffset - check!("1", [fix!(TimezoneOffset)]; INVALID); - check!("12", [fix!(TimezoneOffset)]; INVALID); - check!("123", [fix!(TimezoneOffset)]; INVALID); - check!("1234", [fix!(TimezoneOffset)]; INVALID); - check!("12345", [fix!(TimezoneOffset)]; INVALID); - check!("123456", [fix!(TimezoneOffset)]; INVALID); - check!("1234567", [fix!(TimezoneOffset)]; INVALID); - check!("+1", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+12", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+123", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+1234", [fix!(TimezoneOffset)]; offset: 45_240); - check!("+12345", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+123456", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+1234567", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12345678", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12:", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+12:3", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+12:34", [fix!(TimezoneOffset)]; offset: 45_240); - check!("-12:34", [fix!(TimezoneOffset)]; offset: -45_240); - check!("+12:34:", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12:34:5", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12:34:56", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12:34:56:", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12 34", [fix!(TimezoneOffset)]; INVALID); - check!("+12 34", [fix!(TimezoneOffset)]; INVALID); - check!("12:34", [fix!(TimezoneOffset)]; INVALID); - check!("12:34:56", [fix!(TimezoneOffset)]; INVALID); - check!("+12::34", [fix!(TimezoneOffset)]; INVALID); - check!("+12: :34", [fix!(TimezoneOffset)]; INVALID); - check!("+12:::34", [fix!(TimezoneOffset)]; INVALID); - check!("+12::::34", [fix!(TimezoneOffset)]; INVALID); - check!("+12::34", [fix!(TimezoneOffset)]; INVALID); - check!("+12:34:56", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12:3456", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+1234:56", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+1234:567", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+00:00", [fix!(TimezoneOffset)]; offset: 0); - check!("-00:00", [fix!(TimezoneOffset)]; offset: 0); - check!("+00:01", [fix!(TimezoneOffset)]; offset: 60); - check!("-00:01", [fix!(TimezoneOffset)]; offset: -60); - check!("+00:30", [fix!(TimezoneOffset)]; offset: 1_800); - check!("-00:30", [fix!(TimezoneOffset)]; offset: -1_800); - check!("+24:00", [fix!(TimezoneOffset)]; offset: 86_400); - check!("-24:00", [fix!(TimezoneOffset)]; offset: -86_400); - check!("+99:59", [fix!(TimezoneOffset)]; offset: 359_940); - check!("-99:59", [fix!(TimezoneOffset)]; offset: -359_940); - check!("+00:60", [fix!(TimezoneOffset)]; OUT_OF_RANGE); - check!("+00:99", [fix!(TimezoneOffset)]; OUT_OF_RANGE); - check!("#12:34", [fix!(TimezoneOffset)]; INVALID); - check!("+12:34 ", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12 34 ", [fix!(TimezoneOffset)]; INVALID); - check!(" +12:34", [fix!(TimezoneOffset)]; offset: 45_240); - check!(" -12:34", [fix!(TimezoneOffset)]; offset: -45_240); - check!(" +12:34", [fix!(TimezoneOffset)]; INVALID); - check!(" -12:34", [fix!(TimezoneOffset)]; INVALID); - check!("\t -12:34", [fix!(TimezoneOffset)]; INVALID); - check!("-12: 34", [fix!(TimezoneOffset)]; INVALID); - check!("-12 :34", [fix!(TimezoneOffset)]; INVALID); - check!("-12 : 34", [fix!(TimezoneOffset)]; INVALID); - check!("-12 : 34", [fix!(TimezoneOffset)]; INVALID); - check!("-12 : 34", [fix!(TimezoneOffset)]; INVALID); - check!("-12: 34", [fix!(TimezoneOffset)]; INVALID); - check!("-12 :34", [fix!(TimezoneOffset)]; INVALID); - check!("-12 : 34", [fix!(TimezoneOffset)]; INVALID); - check!("12:34 ", [fix!(TimezoneOffset)]; INVALID); - check!(" 12:34", [fix!(TimezoneOffset)]; INVALID); - check!("", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+12345", [fix!(TimezoneOffset), num!(Day)]; offset: 45_240, day: 5); - check!("+12:345", [fix!(TimezoneOffset), num!(Day)]; offset: 45_240, day: 5); - check!("+12:34:", [fix!(TimezoneOffset), lit!(":")]; offset: 45_240); - check!("Z12:34", [fix!(TimezoneOffset)]; INVALID); - check!("X12:34", [fix!(TimezoneOffset)]; INVALID); - check!("Z+12:34", [fix!(TimezoneOffset)]; INVALID); - check!("X+12:34", [fix!(TimezoneOffset)]; INVALID); - check!("🀠+12:34", [fix!(TimezoneOffset)]; INVALID); - check!("+12:34🀠", [fix!(TimezoneOffset)]; TOO_LONG); - check!("+12:🀠34", [fix!(TimezoneOffset)]; INVALID); - check!("+12:34🀠", [fix!(TimezoneOffset), lit!("🀠")]; offset: 45_240); - check!("🀠+12:34", [lit!("🀠"), fix!(TimezoneOffset)]; offset: 45_240); - check!("Z", [fix!(TimezoneOffset)]; INVALID); - check!("A", [fix!(TimezoneOffset)]; INVALID); - check!("PST", [fix!(TimezoneOffset)]; INVALID); - check!("#Z", [fix!(TimezoneOffset)]; INVALID); - check!(":Z", [fix!(TimezoneOffset)]; INVALID); - check!("+Z", [fix!(TimezoneOffset)]; TOO_SHORT); - check!("+:Z", [fix!(TimezoneOffset)]; INVALID); - check!("+Z:", [fix!(TimezoneOffset)]; INVALID); - check!("z", [fix!(TimezoneOffset)]; INVALID); - check!(" :Z", [fix!(TimezoneOffset)]; INVALID); - check!(" Z", [fix!(TimezoneOffset)]; INVALID); - check!(" z", [fix!(TimezoneOffset)]; INVALID); - - // TimezoneOffsetColon - check!("1", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12", [fix!(TimezoneOffsetColon)]; INVALID); - check!("123", [fix!(TimezoneOffsetColon)]; INVALID); - check!("1234", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12345", [fix!(TimezoneOffsetColon)]; INVALID); - check!("123456", [fix!(TimezoneOffsetColon)]; INVALID); - check!("1234567", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12345678", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+1", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+12", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+123", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+1234", [fix!(TimezoneOffsetColon)]; offset: 45_240); - check!("-1234", [fix!(TimezoneOffsetColon)]; offset: -45_240); - check!("+12345", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+123456", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+1234567", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12345678", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("1:", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:3", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:34:", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:34:5", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:34:56", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+1:", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12:", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+12:3", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+12:34", [fix!(TimezoneOffsetColon)]; offset: 45_240); - check!("-12:34", [fix!(TimezoneOffsetColon)]; offset: -45_240); - check!("+12:34:", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12:34:5", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12:34:56", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12:34:56:", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12:34:56:7", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12:34:56:78", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12:3456", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+1234:56", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!("+12 34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12: 34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12 :34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12::34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12: :34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12:::34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12::::34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12::34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("#1234", [fix!(TimezoneOffsetColon)]; INVALID); - check!("#12:34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12:34 ", [fix!(TimezoneOffsetColon)]; TOO_LONG); - check!(" +12:34", [fix!(TimezoneOffsetColon)]; offset: 45_240); - check!("\t+12:34", [fix!(TimezoneOffsetColon)]; offset: 45_240); - check!("\t\t+12:34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("12:34 ", [fix!(TimezoneOffsetColon)]; INVALID); - check!(" 12:34", [fix!(TimezoneOffsetColon)]; INVALID); - check!("", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!(":", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+12345", [fix!(TimezoneOffsetColon), num!(Day)]; offset: 45_240, day: 5); - check!("+12:345", [fix!(TimezoneOffsetColon), num!(Day)]; offset: 45_240, day: 5); - check!("+12:34:", [fix!(TimezoneOffsetColon), lit!(":")]; offset: 45_240); - check!("Z", [fix!(TimezoneOffsetColon)]; INVALID); - check!("A", [fix!(TimezoneOffsetColon)]; INVALID); - check!("PST", [fix!(TimezoneOffsetColon)]; INVALID); - check!("#Z", [fix!(TimezoneOffsetColon)]; INVALID); - check!(":Z", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+Z", [fix!(TimezoneOffsetColon)]; TOO_SHORT); - check!("+:Z", [fix!(TimezoneOffsetColon)]; INVALID); - check!("+Z:", [fix!(TimezoneOffsetColon)]; INVALID); - check!("z", [fix!(TimezoneOffsetColon)]; INVALID); - check!(" :Z", [fix!(TimezoneOffsetColon)]; INVALID); - check!(" Z", [fix!(TimezoneOffsetColon)]; INVALID); - check!(" z", [fix!(TimezoneOffsetColon)]; INVALID); - // testing `TimezoneOffsetColon` also tests same path as `TimezoneOffsetDoubleColon` - // and `TimezoneOffsetTripleColon` for function `parse_internal`. - // No need for separate tests for `TimezoneOffsetDoubleColon` and - // `TimezoneOffsetTripleColon`. - - // TimezoneOffsetZ - check!("1", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12", [fix!(TimezoneOffsetZ)]; INVALID); - check!("123", [fix!(TimezoneOffsetZ)]; INVALID); - check!("1234", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12345", [fix!(TimezoneOffsetZ)]; INVALID); - check!("123456", [fix!(TimezoneOffsetZ)]; INVALID); - check!("1234567", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12345678", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+1", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+12", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+123", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+1234", [fix!(TimezoneOffsetZ)]; offset: 45_240); - check!("-1234", [fix!(TimezoneOffsetZ)]; offset: -45_240); - check!("+12345", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+123456", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+1234567", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12345678", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("1:", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:3", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:34:", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:34:5", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:34:56", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+1:", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12:", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+12:3", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+12:34", [fix!(TimezoneOffsetZ)]; offset: 45_240); - check!("-12:34", [fix!(TimezoneOffsetZ)]; offset: -45_240); - check!("+12:34:", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12:34:5", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12:34:56", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12:34:56:", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12:34:56:7", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12:34:56:78", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12::34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12:3456", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+1234:56", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12: 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12 :34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12 : 34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("12:34 ", [fix!(TimezoneOffsetZ)]; INVALID); - check!(" 12:34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+12:34 ", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("+12 34 ", [fix!(TimezoneOffsetZ)]; INVALID); - check!(" +12:34", [fix!(TimezoneOffsetZ)]; offset: 45_240); - check!("+12345", [fix!(TimezoneOffsetZ), num!(Day)]; offset: 45_240, day: 5); - check!("+12:345", [fix!(TimezoneOffsetZ), num!(Day)]; offset: 45_240, day: 5); - check!("+12:34:", [fix!(TimezoneOffsetZ), lit!(":")]; offset: 45_240); - check!("Z12:34", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("X12:34", [fix!(TimezoneOffsetZ)]; INVALID); - check!("Z", [fix!(TimezoneOffsetZ)]; offset: 0); - check!("z", [fix!(TimezoneOffsetZ)]; offset: 0); - check!(" Z", [fix!(TimezoneOffsetZ)]; offset: 0); - check!(" z", [fix!(TimezoneOffsetZ)]; offset: 0); - check!("\u{0363}Z", [fix!(TimezoneOffsetZ)]; INVALID); - check!("Z ", [fix!(TimezoneOffsetZ)]; TOO_LONG); - check!("A", [fix!(TimezoneOffsetZ)]; INVALID); - check!("PST", [fix!(TimezoneOffsetZ)]; INVALID); - check!("#Z", [fix!(TimezoneOffsetZ)]; INVALID); - check!(":Z", [fix!(TimezoneOffsetZ)]; INVALID); - check!(":z", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+Z", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("-Z", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+A", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+πŸ™ƒ", [fix!(TimezoneOffsetZ)]; INVALID); - check!("+Z:", [fix!(TimezoneOffsetZ)]; INVALID); - check!(" :Z", [fix!(TimezoneOffsetZ)]; INVALID); - check!(" +Z", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!(" -Z", [fix!(TimezoneOffsetZ)]; TOO_SHORT); - check!("+:Z", [fix!(TimezoneOffsetZ)]; INVALID); - check!("Y", [fix!(TimezoneOffsetZ)]; INVALID); - check!("Zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0); - check!("zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0); - check!("+1234ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 45_240); - check!("+12:34ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 45_240); - // Testing `TimezoneOffsetZ` also tests same path as `TimezoneOffsetColonZ` - // in function `parse_internal`. - // No need for separate tests for `TimezoneOffsetColonZ`. - - // TimezoneOffsetPermissive - check!("1", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("123", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("1234", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12345", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("123456", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("1234567", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12345678", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+1", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("+12", [internal_fix!(TimezoneOffsetPermissive)]; offset: 43_200); - check!("+123", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("+1234", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240); - check!("-1234", [internal_fix!(TimezoneOffsetPermissive)]; offset: -45_240); - check!("+12345", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+123456", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+1234567", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12345678", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("1:", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:3", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:34:", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:34:5", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:34:56", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+1:", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:", [internal_fix!(TimezoneOffsetPermissive)]; offset: 43_200); - check!("+12:3", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("+12:34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240); - check!("-12:34", [internal_fix!(TimezoneOffsetPermissive)]; offset: -45_240); - check!("+12:34:", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12:34:5", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12:34:56", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12:34:56:", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12:34:56:7", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12:34:56:78", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 :34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12: 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 : 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 :34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12: 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 : 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12::34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 ::34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12: :34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:: 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12 ::34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12: :34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:: 34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:::34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12::::34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("12:34 ", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!(" 12:34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:34 ", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!(" +12:34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240); - check!("+12345", [internal_fix!(TimezoneOffsetPermissive), num!(Day)]; offset: 45_240, day: 5); - check!("+12:345", [internal_fix!(TimezoneOffsetPermissive), num!(Day)]; offset: 45_240, day: 5); - check!("+12:34:", [internal_fix!(TimezoneOffsetPermissive), lit!(":")]; offset: 45_240); - check!("🀠+12:34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:34🀠", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("+12:🀠34", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+12:34🀠", [internal_fix!(TimezoneOffsetPermissive), lit!("🀠")]; offset: 45_240); - check!("🀠+12:34", [lit!("🀠"), internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240); - check!("Z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); - check!("A", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("PST", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); - check!(" Z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); - check!(" z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); - check!("Z ", [internal_fix!(TimezoneOffsetPermissive)]; TOO_LONG); - check!("#Z", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!(":Z", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!(":z", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+Z", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("-Z", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("+A", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("+PST", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+πŸ™ƒ", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("+Z:", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!(" :Z", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!(" +Z", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!(" -Z", [internal_fix!(TimezoneOffsetPermissive)]; TOO_SHORT); - check!("+:Z", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - check!("Y", [internal_fix!(TimezoneOffsetPermissive)]; INVALID); - - // TimezoneName - check!("CEST", [fix!(TimezoneName)]; ); - check!("cest", [fix!(TimezoneName)]; ); // lowercase - check!("XXXXXXXX", [fix!(TimezoneName)]; ); // not a real timezone name - check!("!!!!", [fix!(TimezoneName)]; ); // not a real timezone name! - check!("CEST 5", [fix!(TimezoneName), lit!(" "), num!(Day)]; day: 5); - check!("CEST ", [fix!(TimezoneName)]; TOO_LONG); - check!(" CEST", [fix!(TimezoneName)]; TOO_LONG); - check!("CE ST", [fix!(TimezoneName)]; TOO_LONG); + check!("+00:00", [fix!(TimezoneOffset)]; offset: 0); + check!("-00:00", [fix!(TimezoneOffset)]; offset: 0); + check!("+00:01", [fix!(TimezoneOffset)]; offset: 60); + check!("-00:01", [fix!(TimezoneOffset)]; offset: -60); + check!("+00:30", [fix!(TimezoneOffset)]; offset: 30 * 60); + check!("-00:30", [fix!(TimezoneOffset)]; offset: -30 * 60); + check!("+04:56", [fix!(TimezoneOffset)]; offset: 296 * 60); + check!("-04:56", [fix!(TimezoneOffset)]; offset: -296 * 60); + check!("+24:00", [fix!(TimezoneOffset)]; offset: 24 * 60 * 60); + check!("-24:00", [fix!(TimezoneOffset)]; offset: -24 * 60 * 60); + check!("+99:59", [fix!(TimezoneOffset)]; offset: (100 * 60 - 1) * 60); + check!("-99:59", [fix!(TimezoneOffset)]; offset: -(100 * 60 - 1) * 60); + check!("+00:59", [fix!(TimezoneOffset)]; offset: 59 * 60); + check!("+00:60", [fix!(TimezoneOffset)]; OUT_OF_RANGE); + check!("+00:99", [fix!(TimezoneOffset)]; OUT_OF_RANGE); + check!("#12:34", [fix!(TimezoneOffset)]; INVALID); + check!("12:34", [fix!(TimezoneOffset)]; INVALID); + check!("+12:34 ", [fix!(TimezoneOffset)]; TOO_LONG); + check!(" +12:34", [fix!(TimezoneOffset)]; offset: 754 * 60); + check!("\t -12:34", [fix!(TimezoneOffset)]; offset: -754 * 60); + check!("", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+1", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+12", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+123", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+1234", [fix!(TimezoneOffset)]; offset: 754 * 60); + check!("+12345", [fix!(TimezoneOffset)]; TOO_LONG); + check!("+12345", [fix!(TimezoneOffset), num!(Day)]; offset: 754 * 60, day: 5); + check!("Z", [fix!(TimezoneOffset)]; INVALID); + check!("z", [fix!(TimezoneOffset)]; INVALID); + check!("Z", [fix!(TimezoneOffsetZ)]; offset: 0); + check!("z", [fix!(TimezoneOffsetZ)]; offset: 0); + check!("Y", [fix!(TimezoneOffsetZ)]; INVALID); + check!("Zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0); + check!("zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0); + check!("+1234ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60); + check!("+12:34ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60); + check!("Z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); + check!("z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); + check!("+12:00", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60); + check!("+12", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60); + check!("CEST 5", [fix!(TimezoneName), lit!(" "), num!(Day)]; day: 5); // some practical examples check!("2015-02-04T14:37:05+09:00", @@ -1306,31 +824,10 @@ fn test_parse() { num!(Hour), num!(Minute), num!(Second), internal_fix!(Nanosecond3NoDot)]; year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37, second: 5, nanosecond: 567000000); - check!("20150204143705.567", - [num!(Year), num!(Month), num!(Day), - num!(Hour), num!(Minute), num!(Second), fix!(Nanosecond)]; - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, - minute: 37, second: 5, nanosecond: 567000000); - check!("20150204143705.567891", - [num!(Year), num!(Month), num!(Day), - num!(Hour), num!(Minute), num!(Second), fix!(Nanosecond)]; - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, - minute: 37, second: 5, nanosecond: 567891000); - check!("20150204143705.567891023", - [num!(Year), num!(Month), num!(Day), - num!(Hour), num!(Minute), num!(Second), fix!(Nanosecond)]; - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, - minute: 37, second: 5, nanosecond: 567891023); - check!("Mon, 10 Jun 2013 09:32:37 GMT", + check!("Mon, 10 Jun 2013 09:32:37 GMT", [fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "), fix!(ShortMonthName), sp!(" "), num!(Year), sp!(" "), num!(Hour), lit!(":"), - num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")]; - year: 2013, month: 6, day: 10, weekday: Weekday::Mon, - hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37); - check!("🀠Mon, 10 Jun🀠2013 09:32:37 GMT🀠", - [lit!("🀠"), fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "), - fix!(ShortMonthName), lit!("🀠"), num!(Year), sp!(" "), num!(Hour), lit!(":"), - num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT"), lit!("🀠")]; + num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")]; year: 2013, month: 6, day: 10, weekday: Weekday::Mon, hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37); check!("Sun Aug 02 13:39:15 CEST 2020", @@ -1351,22 +848,6 @@ fn test_parse() { check!("12345678901234.56789", [num!(Timestamp), fix!(Nanosecond)]; nanosecond: 567_890_000, timestamp: 12_345_678_901_234); - - // docstring examples from `impl str::FromStr` - check!("2000-01-02T03:04:05Z", - [num!(Year), lit!("-"), num!(Month), lit!("-"), num!(Day), lit!("T"), - num!(Hour), lit!(":"), num!(Minute), lit!(":"), num!(Second), - internal_fix!(TimezoneOffsetPermissive)]; - year: 2000, month: 1, day: 2, - hour_div_12: 0, hour_mod_12: 3, minute: 4, second: 5, - offset: 0); - check!("2000-01-02 03:04:05Z", - [num!(Year), lit!("-"), num!(Month), lit!("-"), num!(Day), sp!(" "), - num!(Hour), lit!(":"), num!(Minute), lit!(":"), num!(Second), - internal_fix!(TimezoneOffsetPermissive)]; - year: 2000, month: 1, day: 2, - hour_div_12: 0, hour_mod_12: 3, minute: 4, second: 5, - offset: 0); } #[cfg(test)] @@ -1383,8 +864,6 @@ fn test_rfc2822() { ("Fri, 2 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // folding whitespace ("Fri, 02 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // leading zero ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC)", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // trailing comment - ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC)", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // intermixed arbitrary whitespace - ("Tue, 20 Jan 2015\t17:35:20\t-0800\t\t(UTC)", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // intermixed arbitrary whitespace ( r"Tue, 20 Jan 2015 17:35:20 -0800 ( (UTC ) (\( (a)\(( \t ) ) \\( \) ))", Ok("Tue, 20 Jan 2015 17:35:20 -0800"), @@ -1431,7 +910,6 @@ fn test_rfc2822() { ("Tue, 20 Jan 2015 17:35:20 k", Ok("Tue, 20 Jan 2015 17:35:20 +0000")), // named single-letter timezone "J" is specifically not valid ("Tue, 20 Jan 2015 17:35:20 J", Err(NOT_ENOUGH)), - ("Tue, 20 Jan 2015😈17:35:20 -0800", Err(INVALID)), // bad character! ]; fn rfc2822_to_datetime(date: &str) -> ParseResult> { @@ -1519,26 +997,11 @@ fn test_rfc3339() { ("2015-01-20T17:35:20.000031-08:00", Ok("2015-01-20T17:35:20.000031-08:00")), ("2015-01-20T17:35:20.000000004-08:00", Ok("2015-01-20T17:35:20.000000004-08:00")), ("2015-01-20T17:35:20.000000000452-08:00", Ok("2015-01-20T17:35:20-08:00")), // too small - ("2015-01-20 17:35:20.001-08:00", Err(INVALID)), // missing separator 'T' - ("2015/01/20T17:35:20.001-08:00", Err(INVALID)), // wrong separator char YMD - ("2015-01-20T17-35-20.001-08:00", Err(INVALID)), // wrong separator char HMS - ("99999-01-20T17:35:20-08:00", Err(INVALID)), // bad year value - ("-2000-01-20T17:35:20-08:00", Err(INVALID)), // bad year value - ("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month value - ("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour value - ("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute value - ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second value - ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset value - ("15-01-20T17:35:20-08:00", Err(INVALID)), // bad year format - ("15-01-20T17:35:20-08:00:00", Err(INVALID)), // bad year format, bad offset format - ("2015-01-20T17:35:20-0800", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20.001-08 : 00", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20-08:00:00", Err(TOO_LONG)), // bad offset format - ("2015-01-20T17:35:20-08:", Err(TOO_SHORT)), // bad offset format - ("2015-01-20T17:35:20-08", Err(TOO_SHORT)), // bad offset format - ("2015-01-20T", Err(TOO_SHORT)), // missing HMS - ("2015-01-20T00:00:1", Err(TOO_SHORT)), // missing complete S - ("2015-01-20T00:00:1-08:00", Err(INVALID)), // missing complete S + ("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month + ("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour + ("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute + ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second + ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset ]; fn rfc3339_to_datetime(date: &str) -> ParseResult> { @@ -1553,7 +1016,6 @@ fn test_rfc3339() { // Test against test data above for &(date, checkdate) in testdates.iter() { - eprintln!("test_rfc3339: date {:?}, expect {:?}", date, checkdate); let d = rfc3339_to_datetime(date); // parse a date let dt = match d { // did we get a value? diff --git a/src/format/scan.rs b/src/format/scan.rs index 50315fef60..2962ef162b 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -198,41 +198,9 @@ pub(super) fn space(s: &str) -> ParseResult<&str> { } } -/// Returns slice remaining after first char. -/// If <=1 chars in `s` then return an empty slice -pub(super) fn s_next(s: &str) -> &str { - match s.char_indices().nth(1) { - Some((offset, _)) => &s[offset..], - None => { - // one or zero chars in `s`, return empty string - &s[s.len()..] - } - } -} - -/// 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(super) fn consume_colon_maybe(mut s: &str) -> ParseResult<&str> { - if s.is_empty() { - // nothing consumed - return Ok(s); - } - - if s.starts_with(':') { - s = s_next(s); - // consumed `':'` - } - - Ok(s) +/// Consumes any number (including zero) of colon or spaces. +pub(super) fn colon_or_space(s: &str) -> ParseResult<&str> { + Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace())) } /// Tries to parse `[-+]\d\d` continued by `\d\d`. Return an offset in seconds if possible. @@ -447,65 +415,3 @@ fn test_rfc2822_comments() { ); } } - -#[test] -fn test_space() { - assert_eq!(space(""), Err(TOO_SHORT)); - assert_eq!(space(" "), Ok("")); - assert_eq!(space(" \t"), Ok("")); - assert_eq!(space(" \ta"), Ok("a")); - assert_eq!(space(" \ta "), Ok("a ")); - assert_eq!(space("a"), Err(INVALID)); - assert_eq!(space("a "), Err(INVALID)); -} - -#[test] -fn test_s_next() { - assert_eq!(s_next(""), ""); - assert_eq!(s_next(" "), ""); - assert_eq!(s_next("a"), ""); - assert_eq!(s_next("ab"), "b"); - assert_eq!(s_next("abc"), "bc"); - assert_eq!(s_next("😾b"), "b"); - assert_eq!(s_next("a😾"), "😾"); - assert_eq!(s_next("😾bc"), "bc"); - 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("")); - assert_eq!(consume_colon_maybe(" "), Ok(" ")); - assert_eq!(consume_colon_maybe("\n"), Ok("\n")); - assert_eq!(consume_colon_maybe(" "), Ok(" ")); - assert_eq!(consume_colon_maybe(":"), Ok("")); - assert_eq!(consume_colon_maybe(" :"), Ok(" :")); - assert_eq!(consume_colon_maybe(": "), Ok(" ")); - assert_eq!(consume_colon_maybe(" : "), Ok(" : ")); - assert_eq!(consume_colon_maybe(": "), Ok(" ")); - assert_eq!(consume_colon_maybe(" :"), Ok(" :")); - assert_eq!(consume_colon_maybe(":: "), Ok(": ")); - assert_eq!(consume_colon_maybe("😸"), Ok("😸")); - assert_eq!(consume_colon_maybe("😸😸"), Ok("😸😸")); - assert_eq!(consume_colon_maybe("😸:"), Ok("😸:")); - assert_eq!(consume_colon_maybe("😸 "), Ok("😸 ")); - assert_eq!(consume_colon_maybe(":😸"), Ok("😸")); - assert_eq!(consume_colon_maybe(":😸 "), Ok("😸 ")); - assert_eq!(consume_colon_maybe(": 😸"), Ok(" 😸")); - assert_eq!(consume_colon_maybe(": 😸"), Ok(" 😸")); - assert_eq!(consume_colon_maybe(": :😸"), Ok(" :😸")); -} diff --git a/src/format/strftime.rs b/src/format/strftime.rs index fc97da3c0f..24bae20c0a 100644 --- a/src/format/strftime.rs +++ b/src/format/strftime.rs @@ -513,32 +513,12 @@ impl<'a> Iterator for StrftimeItems<'a> { 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(" "), [sp!(" ")]); - assert_eq!(parse_and_collect(" "), [sp!(" ")]); - // ne! - assert_ne!(parse_and_collect(" "), [sp!(" "), sp!(" ")]); - // eq! - assert_eq!(parse_and_collect(" "), [sp!(" ")]); - assert_eq!(parse_and_collect("a"), [lit!("a")]); - assert_eq!(parse_and_collect("ab"), [lit!("ab")]); - assert_eq!(parse_and_collect("😽"), [lit!("😽")]); - assert_eq!(parse_and_collect("a😽"), [lit!("a😽")]); - assert_eq!(parse_and_collect("😽a"), [lit!("😽a")]); - assert_eq!(parse_and_collect(" 😽"), [sp!(" "), lit!("😽")]); - assert_eq!(parse_and_collect("😽 "), [lit!("😽"), sp!(" ")]); - // ne! - assert_ne!(parse_and_collect("😽😽"), [lit!("😽")]); - assert_ne!(parse_and_collect("😽"), [lit!("😽😽")]); - assert_ne!(parse_and_collect("😽😽"), [lit!("😽😽"), lit!("😽")]); - // eq! - assert_eq!(parse_and_collect("😽😽"), [lit!("😽😽")]); assert_eq!(parse_and_collect(" \t\n\r "), [sp!(" \t\n\r ")]); assert_eq!(parse_and_collect("hello?"), [lit!("hello?")]); assert_eq!( @@ -552,63 +532,12 @@ fn test_strftime_items() { parse_and_collect("%Y-%m-%d"), [num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)] ); - assert_eq!(parse_and_collect("😽 "), [lit!("😽"), sp!(" ")]); - assert_eq!(parse_and_collect("😽😽"), [lit!("😽😽")]); - assert_eq!(parse_and_collect("😽😽😽"), [lit!("😽😽😽")]); - assert_eq!(parse_and_collect("😽😽 😽"), [lit!("😽😽"), sp!(" "), lit!("😽")]); - assert_eq!(parse_and_collect("😽😽a 😽"), [lit!("😽😽a"), sp!(" "), lit!("😽")]); - assert_eq!(parse_and_collect("😽😽a b😽"), [lit!("😽😽a"), sp!(" "), lit!("b😽")]); - assert_eq!(parse_and_collect("😽😽a b😽c"), [lit!("😽😽a"), sp!(" "), lit!("b😽c")]); - assert_eq!(parse_and_collect("😽😽 "), [lit!("😽😽"), sp!(" ")]); - assert_eq!(parse_and_collect("😽😽 😽"), [lit!("😽😽"), sp!(" "), lit!("😽")]); - assert_eq!(parse_and_collect(" 😽"), [sp!(" "), lit!("😽")]); - assert_eq!(parse_and_collect(" 😽 "), [sp!(" "), lit!("😽"), sp!(" ")]); - assert_eq!(parse_and_collect(" 😽 😽"), [sp!(" "), lit!("😽"), sp!(" "), lit!("😽")]); - assert_eq!( - parse_and_collect(" 😽 😽 "), - [sp!(" "), lit!("😽"), sp!(" "), lit!("😽"), sp!(" ")] - ); - assert_eq!( - parse_and_collect(" 😽 😽 "), - [sp!(" "), lit!("😽"), sp!(" "), lit!("😽"), sp!(" ")] - ); - assert_eq!( - parse_and_collect(" 😽 😽😽 "), - [sp!(" "), lit!("😽"), sp!(" "), lit!("😽😽"), sp!(" ")] - ); - assert_eq!(parse_and_collect(" 😽😽"), [sp!(" "), lit!("😽😽")]); - assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]); - assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]); - assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]); - assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]); - assert_eq!( - parse_and_collect(" 😽 😽😽 "), - [sp!(" "), lit!("😽"), sp!(" "), lit!("😽😽"), sp!(" ")] - ); - assert_eq!( - parse_and_collect(" 😽 πŸ˜½γ―γ„πŸ˜½ ハンバーガー"), - [sp!(" "), lit!("😽"), sp!(" "), lit!("πŸ˜½γ―γ„πŸ˜½"), sp!(" "), lit!("ハンバーガー")] - ); - assert_eq!(parse_and_collect("%%😽%%😽"), [lit!("%"), lit!("😽"), lit!("%"), lit!("😽")]); - assert_eq!(parse_and_collect("%Y--%m"), [num0!(Year), lit!("--"), num0!(Month)]); assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]")); - assert_eq!(parse_and_collect("100%%😽"), [lit!("100"), lit!("%"), lit!("😽")]); - assert_eq!( - parse_and_collect("100%%😽%%a"), - [lit!("100"), lit!("%"), lit!("😽"), lit!("%"), lit!("a")] - ); - assert_eq!(parse_and_collect("😽100%%"), [lit!("😽100"), lit!("%")]); assert_eq!(parse_and_collect("%m %d"), [num0!(Month), sp!(" "), num0!(Day)]); assert_eq!(parse_and_collect("%"), [Item::Error]); assert_eq!(parse_and_collect("%%"), [lit!("%")]); assert_eq!(parse_and_collect("%%%"), [Item::Error]); - assert_eq!(parse_and_collect("%a"), [fix!(ShortWeekdayName)]); - assert_eq!(parse_and_collect("%aa"), [fix!(ShortWeekdayName), lit!("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("%%%%"), [lit!("%"), lit!("%")]); - assert_eq!(parse_and_collect("%%%%ハンバーガー"), [lit!("%"), lit!("%"), lit!("ハンバーガー")]); 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]); @@ -628,10 +557,6 @@ fn test_strftime_items() { assert_eq!(parse_and_collect("%0e"), [num0!(Day)]); assert_eq!(parse_and_collect("%_e"), [nums!(Day)]); assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]); - assert_eq!(parse_and_collect("%:z"), [fix!(TimezoneOffsetColon)]); - assert_eq!(parse_and_collect("%Z"), [fix!(TimezoneName)]); - assert_eq!(parse_and_collect("%ZZZZ"), [fix!(TimezoneName), lit!("ZZZ")]); - assert_eq!(parse_and_collect("%Z😽"), [fix!(TimezoneName), lit!("😽")]); assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]); assert_eq!(parse_and_collect("%#m"), [Item::Error]); } @@ -741,13 +666,6 @@ fn test_strftime_docs() { 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" - ); } #[cfg(feature = "unstable-locales")] diff --git a/src/naive/date.rs b/src/naive/date.rs index 4bcb306836..b9404a15f5 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -2061,10 +2061,13 @@ impl str::FromStr for NaiveDate { fn from_str(s: &str) -> ParseResult { const ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Year, Pad::Zero), + Item::Space(""), Item::Literal("-"), Item::Numeric(Numeric::Month, Pad::Zero), + Item::Space(""), Item::Literal("-"), Item::Numeric(Numeric::Day, Pad::Zero), + Item::Space(""), ]; let mut parsed = Parsed::new(); @@ -2868,28 +2871,24 @@ mod tests { // valid cases let valid = [ "-0000000123456-1-2", - "-123456-1-2", + " -123456 - 1 - 2 ", "-12345-1-2", "-1234-12-31", "-7-6-5", "350-2-28", "360-02-29", "0360-02-29", - "2015-2-18", - "2015-02-18", + "2015-2 -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, @@ -2897,7 +2896,6 @@ 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 \ @@ -2910,36 +2908,13 @@ mod tests { // some invalid cases // since `ParseErrorKind` is private, all we can do is to check if there was an error - 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 - "2001 -02-03", // space after year - "2001- 02-03", // space before month - "2001 - 02-03", // space around year-month divider - "2001-02 -03", // space after month - "2001-02- 03", // space before day - "2001-02 - 03", // space around month-day divider - "2001-02-03 ", // trailing space - " 2001-02-03", // leading space - " -123456 - 1 - 2 ", // many spaces - "9999999-9-9", // invalid year (out of bounds) - ]; - for &s in &invalid { - eprintln!("test_date_from_str invalid {:?}", s); - assert!(s.parse::().is_err()); - } + 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 } #[test] @@ -2950,7 +2925,7 @@ mod tests { Ok(ymd(2014, 5, 7)) ); // ignore time and offset assert_eq!( - NaiveDate::parse_from_str("2015-W06-1=2015-033", "%G-W%V-%u=%Y-%j"), + NaiveDate::parse_from_str("2015-W06-1=2015-033", "%G-W%V-%u = %Y-%j"), Ok(ymd(2015, 2, 2)) ); assert_eq!( diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 96ff3b15b5..07309a48ac 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -197,15 +197,11 @@ fn test_datetime_timestamp() { fn test_datetime_from_str() { // valid cases let valid = [ - "2001-02-03T04:05:06", - "2012-12-12T12:12:12", - "2015-02-18T23:16:09.153", - "2015-2-18T23:16:09.153", + "2015-2-18T23:16:9.15", "-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), @@ -230,48 +226,16 @@ 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 - "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", // 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() { - eprintln!("test_datetime_from_str invalid {:?}", s); - assert!(s.parse::().is_err()); - } + 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 } #[test] @@ -285,12 +249,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!( diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index 24453bf6a2..3700c5cba2 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -1305,11 +1305,14 @@ impl str::FromStr for NaiveTime { fn from_str(s: &str) -> ParseResult { const ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Hour, Pad::Zero), + Item::Space(""), Item::Literal(":"), Item::Numeric(Numeric::Minute, Pad::Zero), + Item::Space(""), Item::Literal(":"), Item::Numeric(Numeric::Second, Pad::Zero), Item::Fixed(Fixed::Nanosecond), + Item::Space(""), ]; let mut parsed = Parsed::new(); diff --git a/src/naive/time/tests.rs b/src/naive/time/tests.rs index c14c02fb72..62c46a247e 100644 --- a/src/naive/time/tests.rs +++ b/src/naive/time/tests.rs @@ -232,37 +232,12 @@ fn test_date_from_str() { "0:0:0", "0:0:0.0000000", "0:0:0.0000003", - "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", + " 4 : 3 : 2.1 ", + " 09:08:07 ", + " 9:8:07 ", "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), @@ -287,42 +262,15 @@ fn test_date_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 - "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 - "01 :02:03", // space after hour - "01: 02:03", // space before minute - "01 : 02:03", // space around hour-minute divider - "01:02 :03", // space after minute - "01:02: 03", // space before second - "01:02 : 03", // space around minute-second divider - "01:02:03 .456", // space after second - "01:02:03. 456", // space before fraction - "01:02:03 ", // trailing space - "01:02:03.456 ", // trailing space - " 01:02:03", // leading space - " 4 : 3 : 2.1 ", // spaces intermixed throughout - "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()); - } + 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()); } #[test] @@ -333,15 +281,6 @@ 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_err()); - assert!(NaiveTime::parse_from_str("\t\t12:59 PM\t", "\t\t%H:%M\t%P\t").is_err()); - assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M %P").is_err()); assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err()); }