Add various tests for current parsing

Add more varying testing for most parsing functions.
Tests emphasize whitespace, literals, timezones, and timezone
delimiters (colons and whitespace).

Add tests for multiple-byte characters and combining characters
in and around data and parsing formats.

These tests are added to aid humans verifying the next commit that
changes parsing behavior.

Issue #660
This commit is contained in:
jtmoon79 2022-09-08 16:25:18 -07:00 committed by Dirkjan Ochtman
parent 6655649e86
commit bc58a7db52
7 changed files with 1653 additions and 82 deletions

View File

@ -550,6 +550,9 @@ impl DateTime<FixedOffset> {
/// 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, NaiveDate};
/// assert_eq!(
@ -557,6 +560,8 @@ impl DateTime<FixedOffset> {
/// 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<DateTime<FixedOffset>> {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
let mut parsed = Parsed::new();

View File

@ -344,8 +344,10 @@ fn test_datetime_with_timezone() {
}
#[test]
fn test_datetime_rfc2822_and_rfc3339() {
fn test_datetime_rfc2822() {
let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap();
// timezone 0
assert_eq!(
Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(),
"Wed, 18 Feb 2015 23:16:09 +0000"
@ -354,6 +356,7 @@ fn test_datetime_rfc2822_and_rfc3339() {
Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(),
"2015-02-18T23:16:09+00:00"
);
// timezone +05
assert_eq!(
edt.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
@ -376,6 +379,7 @@ fn test_datetime_rfc2822_and_rfc3339() {
.to_rfc3339(),
"2015-02-18T23:16:09.150+05:00"
);
// seconds 60
assert_eq!(
edt.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
@ -408,7 +412,130 @@ fn test_datetime_rfc2822_and_rfc3339() {
Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap())
);
assert_eq!(
DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"),
edt.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_micro_opt(23, 59, 59, 1_234_567)
.unwrap()
.to_rfc2822(),
"Wed, 18 Feb 2015 23:59:60 +0500"
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap())
);
// many varying whitespace intermixed
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822(
"\t\t\tWed,\n\t\t18 \r\n\t\tFeb \u{3000} 2015\r\n\t\t\t23:59:60 \t+0500"
),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap())
);
// example from RFC 2822 Appendix A.5.
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822(
"Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330 (Newfoundland Time)"
),
Ok(FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60)
.unwrap()
.ymd_opt(1969, 2, 13)
.unwrap()
.and_hms_opt(23, 32, 0)
.unwrap()
)
);
// example from RFC 2822 Appendix A.5. without trailing " (Newfoundland Time)"
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822(
"Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330"
),
Ok(FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60)
.unwrap()
.ymd_opt(1969, 2, 13)
.unwrap()
.and_hms_opt(23, 32, 0)
.unwrap())
);
// bad year
assert!(DateTime::<FixedOffset>::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err());
// wrong format
assert!(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +00:00").is_err()
);
// full name day of week
assert!(DateTime::<FixedOffset>::parse_from_rfc2822("Wednesday, 18 Feb 2015 23:16:09 +0000")
.is_err());
// full name day of week
assert!(DateTime::<FixedOffset>::parse_from_rfc2822("Wednesday 18 Feb 2015 23:16:09 +0000")
.is_err());
// wrong day of week separator '.'
assert!(DateTime::<FixedOffset>::parse_from_rfc2822("Wed. 18 Feb 2015 23:16:09 +0000").is_err());
// *trailing* space causes failure
assert!(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000 ").is_err()
);
}
#[test]
fn test_datetime_rfc3339() {
let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap();
assert_eq!(
Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap().to_rfc3339(),
"2015-02-18T23:16:09+00:00"
);
assert_eq!(
edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap().to_rfc3339(),
"2015-02-18T23:16:09.150+05:00"
);
assert_eq!(
edt.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_micro_opt(23, 59, 59, 1_234_567)
.unwrap()
.to_rfc3339(),
"2015-02-18T23:59:60.234567+05:00"
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:59.123+05:00"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 123_000).unwrap())
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:59.123456+05:00"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 123_456).unwrap())
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:59.123456789+05:00"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_nano_opt(23, 59, 59, 123_456_789).unwrap())
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:16:09Z"),
Ok(FixedOffset::east_opt(0)
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_opt(23, 16, 9)
.unwrap())
);
assert_eq!(
edt.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_micro_opt(23, 59, 59, 1_234_567)
.unwrap()
.to_rfc3339(),
"2015-02-18T23:59:60.234567+05:00"
);
assert_eq!(
edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap().to_rfc3339(),
"2015-02-18T23:16:09.150+05:00"
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 1_234_567).unwrap())
);
assert_eq!(
DateTime::<FixedOffset>::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!(
@ -434,6 +561,44 @@ fn test_datetime_rfc2822_and_rfc3339() {
)
.unwrap())
);
assert_eq!(
Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap().to_rfc3339(),
"2015-02-18T23:16:09+00:00"
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567 +05:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:059:60.234567+05:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00PST").is_err()
);
assert!(DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+PST").is_err());
assert!(DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567PST").is_err());
assert!(DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+0500").is_err());
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18 23:59:60.234567+05:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567:+05:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00 ").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339(" 2015-02-18T23:59:60.234567+05:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015- 02-18T23:59:60.234567+05:00").is_err()
);
assert!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567A+05:00").is_err()
);
}
#[test]
@ -575,7 +740,94 @@ fn test_datetime_from_str() {
}
#[test]
fn test_datetime_parse_from_str() {
fn test_parse_datetime_utc() {
// valid cases
let valid = [
"2001-02-03T04:05:06Z",
"2001-02-03T04:05:06+0000",
"2001-02-03T04:05:06-00:00",
"2001-02-03T04:05:06-01:00",
"2012-12-12T12:12:12Z",
"2012 -12-12T12:12:12Z",
"2012 -12-12T12:12:12Z",
"2012- 12-12T12:12:12Z",
"2012- 12-12T12:12:12Z",
"2012-12-12T 12:12:12Z",
"2012-12-12T12 :12:12Z",
"2012-12-12T12 :12:12Z",
"2012-12-12T12: 12:12Z",
"2012-12-12T12: 12:12Z",
"2012-12-12T12 : 12:12Z",
"2012-12-12T12:12:12Z ",
" 2012-12-12T12:12:12Z",
"2015-02-18T23:16:09.153Z",
"2015-2-18T23:16:09.153Z",
"+2015-2-18T23:16:09.153Z",
"-77-02-18T23:16:09Z",
"+82701-05-6T15:9:60.898989898989Z",
];
for &s in &valid {
eprintln!("test_parse_datetime_utc valid {:?}", s);
let d = match s.parse::<DateTime<Utc>>() {
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::<DateTime<Utc>>() {
Ok(d) => d,
Err(e) => {
panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
}
};
assert!(
d == d_,
"`{}` is parsed into `{:?}`, but reparsed result \
`{:?}` does not match",
s,
d,
d_
);
}
// some invalid cases
// since `ParseErrorKind` is private, all we can do is to check if there was an error
let invalid = [
"", // empty
"Z", // missing data
"15Z", // missing data
"15:8:9Z", // missing date
"15-8-9Z", // missing time or date
"Fri, 09 Aug 2013 23:54:35 GMT", // valid datetime, wrong format
"Sat Jun 30 23:59:60 2012", // valid datetime, wrong format
"1441497364.649", // valid datetime, wrong format
"+1441497364.649", // valid datetime, wrong format
"+1441497364", // valid datetime, wrong format
"+1441497364Z", // valid datetime, wrong format
"2014/02/03 04:05:06Z", // valid datetime, wrong format
"2001-02-03T04:05:0600:00", // valid datetime, timezone too close
"2015-15-15T15:15:15Z", // invalid datetime
"2012-12-12T12:12:12x", // invalid timezone
"2012-123-12T12:12:12Z", // invalid month
"2012-12-77T12:12:12Z", // invalid day
"2012-12-12T26:12:12Z", // invalid hour
"2012-12-12T12:61:12Z", // invalid minute
"2012-12-12T12:12:62Z", // invalid second
"2012-12-12 T12:12:12Z", // space after date
"2012-12-12t12:12:12Z", // wrong divider 't'
"2012-12-12T12:12:12ZZ", // trailing literal 'Z'
"+802701-12-12T12:12:12Z", // invalid year (out of bounds)
"+ 2012-12-12T12:12:12Z", // invalid space before year
" +82701 - 05 - 6 T 15 : 9 : 60.898989898989 Z", // valid datetime, wrong format
];
for &s in &invalid {
eprintln!("test_parse_datetime_utc invalid {:?}", s);
assert!(s.parse::<DateTime<Utc>>().is_err());
}
}
#[test]
fn test_utc_datetime_from_str() {
let ymdhms = |y, m, d, h, n, s, off| {
FixedOffset::east_opt(off).unwrap().with_ymd_and_hms(y, m, d, h, n, s).unwrap()
};
@ -590,6 +842,645 @@ fn test_datetime_parse_from_str() {
Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"),
Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap())
);
assert_eq!(
"2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::east_opt(0)
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap())
);
assert_eq!(
"2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap())
);
assert_eq!(
"2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap())
);
assert_eq!(
"2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap())
);
assert_eq!(
"2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::east_opt(0)
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap())
);
assert_eq!(
"2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::west_opt(10 * 3600)
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(13, 16, 9, 150)
.unwrap())
);
assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
assert_eq!(
"2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap())
);
assert_eq!(
"2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap())
);
assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
// no test for `DateTime<Local>`, we cannot verify that much.
}
#[test]
fn test_utc_datetime_from_str_with_spaces() {
let dt = Utc.ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap();
// with varying spaces - should succeed
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S "), Ok(dt),);
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35 ", " %b %d %Y %H:%M:%S "), Ok(dt),);
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(
Utc.datetime_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "),
Ok(dt),
);
assert_eq!(Utc.datetime_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S "), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n"), Ok(dt),);
// with varying spaces - should fail
// leading space in data
assert!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
// trailing space in data
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err());
// trailing tab in data
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err());
// mismatched newlines
assert!(Utc.datetime_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
// trailing literal in data
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err());
}
#[test]
fn test_datetime_parse_from_str() {
let dt = FixedOffset::east_opt(-9 * 60 * 60)
.unwrap()
.ymd_opt(2013, 8, 9)
.unwrap()
.and_hms_opt(23, 54, 35)
.unwrap();
// timezone variations
//
// %Z
//
// wrong timezone format
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %Z"
)
.is_err());
// bad timezone data?
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 PST",
"%b %d %Y %H:%M:%S %Z"
)
.is_err());
// bad timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 XXXXX",
"%b %d %Y %H:%M:%S %Z"
)
.is_err());
//
// %z
//
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09 00",
"%b %d %Y %H:%M:%S %z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00",
"%b %d %Y %H:%M:%S %z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09 : 00",
"%b %d %Y %H:%M:%S %z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 --0900",
"%b %d %Y %H:%M:%S -%z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 +-0900",
"%b %d %Y %H:%M:%S +%z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00 ",
"%b %d %Y %H:%M:%S %z "
),
Ok(dt),
);
// trailing newline after timezone
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00\n",
"%b %d %Y %H:%M:%S %z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00\n",
"%b %d %Y %H:%M:%S %z "
),
Ok(dt),
);
// trailing colon
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:",
"%b %d %Y %H:%M:%S %z"
)
.is_err());
// trailing colon with space
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00: ",
"%b %d %Y %H:%M:%S %z "
)
.is_err());
// trailing colon, mismatch space
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:",
"%b %d %Y %H:%M:%S %z "
)
.is_err());
// wrong timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09",
"%b %d %Y %H:%M:%S %z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09::00",
"%b %d %Y %H:%M:%S %z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900::",
"%b %d %Y %H:%M:%S %z::"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00",
"%b %d %Y %H:%M:%S %z:00"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00 ",
"%b %d %Y %H:%M:%S %z:00 "
),
Ok(dt),
);
//
// %:z
//
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00",
"%b %d %Y %H:%M:%S %:z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %:z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09 00",
"%b %d %Y %H:%M:%S %:z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09 : 00",
"%b %d %Y %H:%M:%S %:z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09 : 00:",
"%b %d %Y %H:%M:%S %:z:"
),
Ok(dt),
);
// wrong timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09",
"%b %d %Y %H:%M:%S %:z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09::00",
"%b %d %Y %H:%M:%S %:z"
),
Ok(dt),
);
// timezone data hs too many colons
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:",
"%b %d %Y %H:%M:%S %:z"
)
.is_err());
// timezone data hs too many colons
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00::",
"%b %d %Y %H:%M:%S %:z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00::",
"%b %d %Y %H:%M:%S %:z::"
),
Ok(dt),
);
//
// %:::z
//
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %::z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00",
"%b %d %Y %H:%M:%S %::z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09 : 00",
"%b %d %Y %H:%M:%S %::z"
),
Ok(dt),
);
// mismatching colon expectations
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00",
"%b %d %Y %H:%M:%S %::z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09::00",
"%b %d %Y %H:%M:%S %::z"
),
Ok(dt),
);
// wrong timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09",
"%b %d %Y %H:%M:%S %::z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09001234",
"%b %d %Y %H:%M:%S %::z1234"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:001234",
"%b %d %Y %H:%M:%S %::z1234"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900 ",
"%b %d %Y %H:%M:%S %::z "
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900\t\n",
"%b %d %Y %H:%M:%S %::z\t\n"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900:",
"%b %d %Y %H:%M:%S %::z:"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 :-0900:0",
"%b %d %Y %H:%M:%S :%::z:0"
),
Ok(dt),
);
// mismatching colons and spaces
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 :-0900: ",
"%b %d %Y %H:%M:%S :%::z::"
)
.is_err());
// mismatching colons expectations
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00",
"%b %d %Y %H:%M:%S %::z"
)
.is_err());
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -0900: 23:54:35",
"%b %d %Y %::z: %H:%M:%S"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 :-0900:0 23:54:35",
"%b %d %Y :%::z:0 %H:%M:%S"
),
Ok(dt),
);
// mismatching colons expectations mid-string
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 :-0900: 23:54:35",
"%b %d %Y :%::z %H:%M:%S"
)
.is_err());
// mismatching colons expectations, before end
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00 ",
"%b %d %Y %H:%M:%S %::z "
)
.is_err());
//
// %:::z
//
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00",
"%b %d %Y %H:%M:%S %:::z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %:::z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900 ",
"%b %d %Y %H:%M:%S %:::z "
),
Ok(dt),
);
// wrong timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09",
"%b %d %Y %H:%M:%S %:::z"
)
.is_err());
//
// %::::z
//
// too many colons
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %::::z"
)
.is_err());
// too many colons
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00",
"%b %d %Y %H:%M:%S %::::z"
)
.is_err());
// too many colons
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:",
"%b %d %Y %H:%M:%S %::::z"
)
.is_err());
// too many colons
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00",
"%b %d %Y %H:%M:%S %::::z"
)
.is_err());
//
// %#z
//
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00",
"%b %d %Y %H:%M:%S %#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00 ",
"%b %d %Y %H:%M:%S %#z "
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900 ",
"%b %d %Y %H:%M:%S %#z "
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09",
"%b %d %Y %H:%M:%S %#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -0900",
"%b %d %Y %H:%M:%S %#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:",
"%b %d %Y %H:%M:%S %#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09: ",
"%b %d %Y %H:%M:%S %#z "
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35+-09",
"%b %d %Y %H:%M:%S+%#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35--09",
"%b %d %Y %H:%M:%S-%#z"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -09:00 23:54:35",
"%b %d %Y %#z%H:%M:%S"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -0900 23:54:35",
"%b %d %Y %#z%H:%M:%S"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -090023:54:35",
"%b %d %Y %#z%H:%M:%S"
),
Ok(dt),
);
assert_eq!(
DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -09:0023:54:35",
"%b %d %Y %#z%H:%M:%S"
),
Ok(dt),
);
// timezone with partial minutes adjacent hours
assert_ne!(
DateTime::<FixedOffset>::parse_from_str("Aug 09 2013 -09023:54:35", "%b %d %Y %#z%H:%M:%S"),
Ok(dt),
);
// bad timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -09:00:00",
"%b %d %Y %H:%M:%S %#z"
)
.is_err());
// bad timezone data (partial minutes)
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -090",
"%b %d %Y %H:%M:%S %#z"
)
.is_err());
// bad timezone data (partial minutes) with trailing space
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 23:54:35 -090 ",
"%b %d %Y %H:%M:%S %#z "
)
.is_err());
// bad timezone data (partial minutes) mid-string
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -090 23:54:35",
"%b %d %Y %#z %H:%M:%S"
)
.is_err());
// bad timezone data
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -09:00:00 23:54:35",
"%b %d %Y %#z %H:%M:%S"
)
.is_err());
// timezone data ambiguous with hours
assert!(DateTime::<FixedOffset>::parse_from_str(
"Aug 09 2013 -09:00:23:54:35",
"%b %d %Y %#z%H:%M:%S"
)
.is_err());
}
#[test]

View File

@ -536,9 +536,11 @@ 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))
@ -549,35 +551,114 @@ fn test_parse() {
check!("", []; );
check!(" ", []; TOO_LONG);
check!("a", []; TOO_LONG);
check!("abc", []; TOO_LONG);
check!("🤠", []; TOO_LONG);
// whitespaces
check!("", [sp!("")]; );
check!(" ", [sp!("")]; );
check!("\t", [sp!("")]; );
check!(" \n\r \n", [sp!("")]; );
check!(" ", [sp!(" ")]; );
check!(" ", [sp!(" ")]; );
check!(" ", [sp!(" "), sp!(" ")]; );
check!(" ", [sp!(" "), sp!(" ")]; );
check!(" ", [sp!(" "), sp!(" ")]; );
check!(" ", [sp!(" "), sp!(" "), sp!(" ")]; );
check!("\t", [sp!("\t")]; );
check!("\t\r", [sp!("\t\r")]; );
check!("\t\r ", [sp!("\t\r ")]; );
check!(" \n\r \n", [sp!(" \n\r \n")]; );
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!("a", [sp!("")]; TOO_LONG);
check!("a", [sp!(" ")]; TOO_LONG);
// a Space containing a literal cannot match a literal
check!("a", [sp!("a")]; TOO_LONG);
check!("abc", [sp!("")]; TOO_LONG);
check!(" ", [sp!(" ")]; );
check!(" \t\n", [sp!(" \t")]; );
check!("", [sp!(" ")]; );
check!(" ", [sp!(" ")]; );
check!(" ", [sp!(" ")]; );
check!(" ", [sp!(" "), sp!(" ")]; );
// `\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")]; );
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")]; );
// 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)]; year: 987);
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!("5", [num!(Year)]; year: 5);
check!("5\0", [num!(Year)]; TOO_LONG);
check!("\x005", [num!(Year)]; INVALID);
@ -587,11 +668,15 @@ fn test_parse() {
check!("12345", [num0!(Year), lit!("5")]; year: 1234);
check!("12341234", [num!(Year), num!(Year)]; year: 1234);
check!("1234 1234", [num!(Year), num!(Year)]; year: 1234);
check!("1234 1234", [num!(Year), sp!(" "), 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!("1234xx1234", [num!(Year), lit!("x"), num!(Year)]; INVALID);
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);
// signed numeric
check!("-42", [num!(Year)]; year: -42);
@ -602,8 +687,14 @@ fn test_parse() {
check!("+42195", [num!(Year)]; year: 42195);
check!(" -42195", [num!(Year)]; year: -42195);
check!(" +42195", [num!(Year)]; year: 42195);
check!("-42195 ", [num!(Year)]; TOO_LONG);
check!("+42195 ", [num!(Year)]; TOO_LONG);
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);
@ -612,8 +703,16 @@ fn test_parse() {
check!("+345", [num!(Ordinal)]; INVALID);
check!("-345", [num!(Ordinal)]; INVALID);
check!(" 345", [num!(Ordinal)]; ordinal: 345);
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)]; 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",
@ -639,6 +738,7 @@ 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);
@ -676,7 +776,15 @@ 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);
@ -693,10 +801,21 @@ 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);
@ -708,76 +827,400 @@ 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);
check!("", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("0", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("42195", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("1234", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("12345", [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);
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!("421950803547", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG);
check!("1234567890", [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
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);
// 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)]; offset: 45_240);
check!("+12 34", [fix!(TimezoneOffset)]; offset: 45_240);
check!("12:34", [fix!(TimezoneOffset)]; INVALID);
check!("12:34:56", [fix!(TimezoneOffset)]; INVALID);
check!("+12::34", [fix!(TimezoneOffset)]; offset: 45_240);
check!("+12: :34", [fix!(TimezoneOffset)]; offset: 45_240);
check!("+12:::34", [fix!(TimezoneOffset)]; offset: 45_240);
check!("+12::::34", [fix!(TimezoneOffset)]; offset: 45_240);
check!("+12::34", [fix!(TimezoneOffset)]; offset: 45_240);
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)]; TOO_LONG);
check!(" +12:34", [fix!(TimezoneOffset)]; offset: 45_240);
check!(" -12:34", [fix!(TimezoneOffset)]; offset: -45_240);
check!(" +12:34", [fix!(TimezoneOffset)]; offset: 45_240);
check!(" -12:34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("\t -12:34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("-12: 34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("-12 :34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("-12 : 34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("-12 : 34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("-12 : 34", [fix!(TimezoneOffset)]; offset: -45_240);
check!("-12: 34", [fix!(TimezoneOffset)]; offset: -45_240);
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!("", [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)]; offset: 45_240);
check!("+12: 34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12 :34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12::34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12: :34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12:::34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12::::34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
check!("+12::34", [fix!(TimezoneOffsetColon)]; offset: 45_240);
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)]; offset: 45_240);
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)]; offset: 45_240);
check!("+12:3456", [fix!(TimezoneOffsetZ)]; TOO_LONG);
check!("+1234:56", [fix!(TimezoneOffsetZ)]; TOO_LONG);
check!("+12 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12: 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12 :34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("+12 : 34", [fix!(TimezoneOffsetZ)]; offset: 45_240);
check!("12:34 ", [fix!(TimezoneOffsetZ)]; INVALID);
check!(" 12:34", [fix!(TimezoneOffsetZ)]; INVALID);
check!("+12:34 ", [fix!(TimezoneOffsetZ)]; TOO_LONG);
check!("+12 34 ", [fix!(TimezoneOffsetZ)]; TOO_LONG);
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)]; offset: 45_240);
check!("+12 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12 :34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12: 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12 : 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12 :34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12: 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12 : 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12::34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12 ::34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12: :34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12:: 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12 ::34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12: :34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12:: 34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12:::34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
check!("+12::::34", [internal_fix!(TimezoneOffsetPermissive)]; offset: 45_240);
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);
// some practical examples
check!("2015-02-04T14:37:05+09:00",
@ -790,10 +1233,31 @@ 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!("Mon, 10 Jun 2013 09:32:37 GMT",
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",
[fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "),
fix!(ShortMonthName), sp!(" "), num!(Year), sp!(" "), num!(Hour), lit!(":"),
num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")];
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!("🤠")];
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",
@ -814,6 +1278,22 @@ 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)]
@ -830,6 +1310,8 @@ 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"),
@ -876,6 +1358,7 @@ 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<DateTime<FixedOffset>> {
@ -963,11 +1446,26 @@ 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-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
("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
];
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
@ -982,6 +1480,7 @@ 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?

View File

@ -513,12 +513,32 @@ impl<'a> Iterator for StrftimeItems<'a> {
fn test_strftime_items() {
fn parse_and_collect(s: &str) -> Vec<Item<'_>> {
// 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::<Option<Vec<_>>>().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!(
@ -532,12 +552,63 @@ 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]);
@ -557,6 +628,10 @@ 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]);
}
@ -666,6 +741,13 @@ 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")]

View File

@ -2828,16 +2828,20 @@ mod tests {
"360-02-29",
"0360-02-29",
"2015-2 -18",
"2015-02-18",
"+70-2-18",
"+70000-2-18",
"+00007-2-18",
];
for &s in &valid {
eprintln!("test_date_from_str valid {:?}", s);
let d = match s.parse::<NaiveDate>() {
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::<NaiveDate>() {
Ok(d) => d,
@ -2845,6 +2849,7 @@ mod tests {
panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
}
};
eprintln!("d_ {:?} (NaiveDate)", d_);
assert!(
d == d_,
"`{}` is parsed into `{:?}`, but reparsed result \
@ -2857,13 +2862,27 @@ mod tests {
// some invalid cases
// since `ParseErrorKind` is private, all we can do is to check if there was an error
assert!("".parse::<NaiveDate>().is_err());
assert!("x".parse::<NaiveDate>().is_err());
assert!("2014".parse::<NaiveDate>().is_err());
assert!("2014-01".parse::<NaiveDate>().is_err());
assert!("2014-01-00".parse::<NaiveDate>().is_err());
assert!("2014-13-57".parse::<NaiveDate>().is_err());
assert!("9999999-9-9".parse::<NaiveDate>().is_err()); // out-of-bounds
let invalid = [
"", // empty
"x", // invalid
"Fri, 09 Aug 2013 GMT", // valid date, wrong format
"Sat Jun 30 2012", // valid date, wrong format
"1441497364.649", // valid datetime, wrong format
"+1441497364.649", // valid datetime, wrong format
"+1441497364", // valid datetime, wrong format
"2014/02/03", // valid date, wrong format
"2014", // datetime missing data
"2014-01", // datetime missing data
"2014-01-00", // invalid day
"2014-11-32", // invalid day
"2014-13-01", // invalid month
"2014-13-57", // invalid month, day
"9999999-9-9", // invalid year (out of bounds)
];
for &s in &invalid {
eprintln!("test_date_from_str invalid {:?}", s);
assert!(s.parse::<NaiveDate>().is_err());
}
}
#[test]

View File

@ -197,11 +197,16 @@ fn test_datetime_timestamp() {
fn test_datetime_from_str() {
// valid cases
let valid = [
"2015-2-18T23:16:9.15",
"2001-02-03T04:05:06",
"2012-12-12T12:12:12",
"2015-02-18T23:16:09.153",
"2015-2-18T23:16:09.153",
"-77-02-18T23:16:09",
"+82701-05-6T15:9:60.898989898989",
" +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ",
];
for &s in &valid {
eprintln!("test_parse_naivedatetime valid {:?}", s);
let d = match s.parse::<NaiveDateTime>() {
Ok(d) => d,
Err(e) => panic!("parsing `{}` has failed: {}", s, e),
@ -226,16 +231,34 @@ fn test_datetime_from_str() {
// some invalid cases
// since `ParseErrorKind` is private, all we can do is to check if there was an error
assert!("".parse::<NaiveDateTime>().is_err());
assert!("x".parse::<NaiveDateTime>().is_err());
assert!("15".parse::<NaiveDateTime>().is_err());
assert!("15:8:9".parse::<NaiveDateTime>().is_err());
assert!("15-8-9".parse::<NaiveDateTime>().is_err());
assert!("2015-15-15T15:15:15".parse::<NaiveDateTime>().is_err());
assert!("2012-12-12T12:12:12x".parse::<NaiveDateTime>().is_err());
assert!("2012-123-12T12:12:12".parse::<NaiveDateTime>().is_err());
assert!("+ 82701-123-12T12:12:12".parse::<NaiveDateTime>().is_err());
assert!("+802701-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); // out-of-bound
let invalid = [
"", // empty
"x", // invalid / missing data
"15", // missing data
"15:8:9", // looks like a time (invalid date)
"15-8-9", // looks like a date (invalid)
"Fri, 09 Aug 2013 23:54:35 GMT", // valid date, wrong format
"Sat Jun 30 23:59:60 2012", // valid date, wrong format
"1441497364.649", // valid date, wrong format
"+1441497364.649", // valid date, wrong format
"+1441497364", // valid date, wrong format
"2014/02/03 04:05:06", // valid date, wrong format
"2015-15-15T15:15:15", // invalid date
"2012-12-12T12:12:12x", // bad timezone / trailing literal
"2012-12-12T12:12:12+00:00", // unexpected timezone / trailing literal
"2012-12-12T12:12:12 +00:00", // unexpected timezone / trailing literal
"2012-12-12T12:12:12 GMT", // unexpected timezone / trailing literal
"2012-123-12T12:12:12", // invalid month
"2012-12-12t12:12:12", // bad divider 't'
"2012-12-12 12:12:12", // missing divider 'T'
"2012-12-12T12:12:12Z", // trailing char 'Z'
"+ 82701-123-12T12:12:12", // strange year, invalid month
"+802701-123-12T12:12:12", // out-of-bound year, invalid month
];
for &s in &invalid {
eprintln!("test_datetime_from_str invalid {:?}", s);
assert!(s.parse::<NaiveDateTime>().is_err());
}
}
#[test]

View File

@ -235,9 +235,37 @@ fn test_date_from_str() {
" 4 : 3 : 2.1 ",
" 09:08:07 ",
" 9:8:07 ",
"01:02:03",
"4:3:2.1",
"9:8:7",
"09:8:7",
"9:08:7",
"9:8:07",
"09:08:7",
"09:8:07",
"09:08:7",
"9:08:07",
"09:08:07",
"9:8:07.123",
"9:08:7.123",
"09:8:7.123",
"09:08:7.123",
"9:08:07.123",
"09:8:07.123",
"09:08:07.123",
"09:08:07.123",
"09:08:07.1234",
"09:08:07.12345",
"09:08:07.123456",
"09:08:07.1234567",
"09:08:07.12345678",
"09:08:07.123456789",
"09:08:07.1234567891",
"09:08:07.12345678912",
"23:59:60.373929310237",
];
for &s in &valid {
eprintln!("test_time_parse_from_str valid {:?}", s);
let d = match s.parse::<NaiveTime>() {
Ok(d) => d,
Err(e) => panic!("parsing `{}` has failed: {}", s, e),
@ -262,15 +290,30 @@ fn test_date_from_str() {
// some invalid cases
// since `ParseErrorKind` is private, all we can do is to check if there was an error
assert!("".parse::<NaiveTime>().is_err());
assert!("x".parse::<NaiveTime>().is_err());
assert!("15".parse::<NaiveTime>().is_err());
assert!("15:8".parse::<NaiveTime>().is_err());
assert!("15:8:x".parse::<NaiveTime>().is_err());
assert!("15:8:9x".parse::<NaiveTime>().is_err());
assert!("23:59:61".parse::<NaiveTime>().is_err());
assert!("12:34:56.x".parse::<NaiveTime>().is_err());
assert!("12:34:56. 0".parse::<NaiveTime>().is_err());
let invalid = [
"", // empty
"x", // invalid
"15", // missing data
"15:8", // missing data
"15:8:x", // missing data, invalid data
"15:8:9x", // missing data, invalid data
"23:59:61", // invalid second (out of bounds)
"23:54:35 GMT", // invalid (timezone non-sensical for NaiveTime)
"23:54:35 +0000", // invalid (timezone non-sensical for NaiveTime)
"1441497364.649", // valid datetime, not a NaiveTime
"+1441497364.649", // valid datetime, not a NaiveTime
"+1441497364", // valid datetime, not a NaiveTime
"001:02:03", // invalid hour
"01:002:03", // invalid minute
"01:02:003", // invalid second
"12:34:56.x", // invalid fraction
"12:34:56. 0", // invalid fraction format
"09:08:00000000007", // invalid second / invalid fraction format
];
for &s in &invalid {
eprintln!("test_time_parse_from_str invalid {:?}", s);
assert!(s.parse::<NaiveTime>().is_err());
}
}
#[test]
@ -281,6 +324,15 @@ fn test_time_parse_from_str() {
Ok(hms(12, 34, 56))
); // ignore date and offset
assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0)));
assert_eq!(NaiveTime::parse_from_str("12:59 \n\t PM", "%H:%M \n\t %P"), Ok(hms(12, 59, 0)));
assert_eq!(NaiveTime::parse_from_str("\t\t12:59\tPM\t", "\t\t%H:%M\t%P\t"), Ok(hms(12, 59, 0)));
assert_eq!(
NaiveTime::parse_from_str("\t\t1259\t\tPM\t", "\t\t%H%M\t\t%P\t"),
Ok(hms(12, 59, 0))
);
assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M\t%P").is_ok());
assert!(NaiveTime::parse_from_str("\t\t12:59 PM\t", "\t\t%H:%M\t%P\t").is_ok());
assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M %P").is_ok());
assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err());
}