• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // This is a part of Chrono.
2 // Portions copyright (c) 2015, John Nagle.
3 // See README.md and LICENSE.txt for details.
4 
5 //! Date and time parsing routines.
6 
7 use core::borrow::Borrow;
8 use core::str;
9 
10 use super::scan;
11 use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed};
12 use super::{ParseError, ParseResult};
13 use super::{BAD_FORMAT, INVALID, OUT_OF_RANGE, TOO_LONG, TOO_SHORT};
14 use crate::{DateTime, FixedOffset, Weekday};
15 
set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()>16 fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
17     p.set_weekday(match v {
18         0 => Weekday::Sun,
19         1 => Weekday::Mon,
20         2 => Weekday::Tue,
21         3 => Weekday::Wed,
22         4 => Weekday::Thu,
23         5 => Weekday::Fri,
24         6 => Weekday::Sat,
25         _ => return Err(OUT_OF_RANGE),
26     })
27 }
28 
set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()>29 fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()> {
30     p.set_weekday(match v {
31         1 => Weekday::Mon,
32         2 => Weekday::Tue,
33         3 => Weekday::Wed,
34         4 => Weekday::Thu,
35         5 => Weekday::Fri,
36         6 => Weekday::Sat,
37         7 => Weekday::Sun,
38         _ => return Err(OUT_OF_RANGE),
39     })
40 }
41 
parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())>42 fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
43     macro_rules! try_consume {
44         ($e:expr) => {{
45             let (s_, v) = $e?;
46             s = s_;
47             v
48         }};
49     }
50 
51     // an adapted RFC 2822 syntax from Section 3.3 and 4.3:
52     //
53     // c-char      = <any char except '(', ')' and '\\'>
54     // c-escape    = "\" <any char>
55     // comment     = "(" *(comment / c-char / c-escape) ")" *S
56     // date-time   = [ day-of-week "," ] date 1*S time *S *comment
57     // day-of-week = *S day-name *S
58     // day-name    = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
59     // date        = day month year
60     // day         = *S 1*2DIGIT *S
61     // month       = 1*S month-name 1*S
62     // month-name  = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
63     //               "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
64     // year        = *S 2*DIGIT *S
65     // time        = time-of-day 1*S zone
66     // time-of-day = hour ":" minute [ ":" second ]
67     // hour        = *S 2DIGIT *S
68     // minute      = *S 2DIGIT *S
69     // second      = *S 2DIGIT *S
70     // zone        = ( "+" / "-" ) 4DIGIT /
71     //               "UT" / "GMT" /                  ; same as +0000
72     //               "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800
73     //               "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700
74     //               1*(%d65-90 / %d97-122)          ; same as -0000
75     //
76     // some notes:
77     //
78     // - quoted characters can be in any mixture of lower and upper cases.
79     //
80     // - we do not recognize a folding white space (FWS) or comment (CFWS).
81     //   for our purposes, instead, we accept any sequence of Unicode
82     //   white space characters (denoted here to `S`). For comments, we accept
83     //   any text within parentheses while respecting escaped parentheses.
84     //   Any actual RFC 2822 parser is expected to parse FWS and/or CFWS themselves
85     //   and replace it with a single SP (`%x20`); this is legitimate.
86     //
87     // - two-digit year < 50 should be interpreted by adding 2000.
88     //   two-digit year >= 50 or three-digit year should be interpreted
89     //   by adding 1900. note that four-or-more-digit years less than 1000
90     //   are *never* affected by this rule.
91     //
92     // - mismatching day-of-week is always an error, which is consistent to
93     //   Chrono's own rules.
94     //
95     // - zones can range from `-9959` to `+9959`, but `FixedOffset` does not
96     //   support offsets larger than 24 hours. this is not *that* problematic
97     //   since we do not directly go to a `DateTime` so one can recover
98     //   the offset information from `Parsed` anyway.
99 
100     s = s.trim_start();
101 
102     if let Ok((s_, weekday)) = scan::short_weekday(s) {
103         if !s_.starts_with(',') {
104             return Err(INVALID);
105         }
106         s = &s_[1..];
107         parsed.set_weekday(weekday)?;
108     }
109 
110     s = s.trim_start();
111     parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
112     s = scan::space(s)?; // mandatory
113     parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
114     s = scan::space(s)?; // mandatory
115 
116     // distinguish two- and three-digit years from four-digit years
117     let prevlen = s.len();
118     let mut year = try_consume!(scan::number(s, 2, usize::MAX));
119     let yearlen = prevlen - s.len();
120     match (yearlen, year) {
121         (2, 0..=49) => {
122             year += 2000;
123         } //   47 -> 2047,   05 -> 2005
124         (2, 50..=99) => {
125             year += 1900;
126         } //   79 -> 1979
127         (3, _) => {
128             year += 1900;
129         } //  112 -> 2012,  009 -> 1909
130         (_, _) => {} // 1987 -> 1987, 0654 -> 0654
131     }
132     parsed.set_year(year)?;
133 
134     s = scan::space(s)?; // mandatory
135     parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
136     s = scan::char(s.trim_start(), b':')?.trim_start(); // *S ":" *S
137     parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
138     if let Ok(s_) = scan::char(s.trim_start(), b':') {
139         // [ ":" *S 2DIGIT ]
140         parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?;
141     }
142 
143     s = scan::space(s)?; // mandatory
144     parsed.set_offset(i64::from(try_consume!(scan::timezone_offset_2822(s))))?;
145 
146     // optional comments
147     while let Ok((s_out, ())) = scan::comment_2822(s) {
148         s = s_out;
149     }
150 
151     Ok((s, ()))
152 }
153 
parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())>154 pub(crate) fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
155     macro_rules! try_consume {
156         ($e:expr) => {{
157             let (s_, v) = $e?;
158             s = s_;
159             v
160         }};
161     }
162 
163     // an adapted RFC 3339 syntax from Section 5.6:
164     //
165     // date-fullyear  = 4DIGIT
166     // date-month     = 2DIGIT ; 01-12
167     // date-mday      = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
168     // time-hour      = 2DIGIT ; 00-23
169     // time-minute    = 2DIGIT ; 00-59
170     // time-second    = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
171     // time-secfrac   = "." 1*DIGIT
172     // time-numoffset = ("+" / "-") time-hour ":" time-minute
173     // time-offset    = "Z" / time-numoffset
174     // partial-time   = time-hour ":" time-minute ":" time-second [time-secfrac]
175     // full-date      = date-fullyear "-" date-month "-" date-mday
176     // full-time      = partial-time time-offset
177     // date-time      = full-date "T" full-time
178     //
179     // some notes:
180     //
181     // - quoted characters can be in any mixture of lower and upper cases.
182     //
183     // - it may accept any number of fractional digits for seconds.
184     //   for Chrono, this means that we should skip digits past first 9 digits.
185     //
186     // - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59.
187     //   note that this restriction is unique to RFC 3339 and not ISO 8601.
188     //   since this is not a typical Chrono behavior, we check it earlier.
189     //
190     // - For readability a full-date and a full-time may be separated by a space character.
191 
192     parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
193     s = scan::char(s, b'-')?;
194     parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
195     s = scan::char(s, b'-')?;
196     parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;
197 
198     s = match s.as_bytes().first() {
199         Some(&b't' | &b'T' | &b' ') => &s[1..],
200         Some(_) => return Err(INVALID),
201         None => return Err(TOO_SHORT),
202     };
203 
204     parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
205     s = scan::char(s, b':')?;
206     parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
207     s = scan::char(s, b':')?;
208     parsed.set_second(try_consume!(scan::number(s, 2, 2)))?;
209     if s.starts_with('.') {
210         let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
211         parsed.set_nanosecond(nanosecond)?;
212     }
213 
214     let offset = try_consume!(scan::timezone_offset(s, |s| scan::char(s, b':'), true, false, true));
215     // This range check is similar to the one in `FixedOffset::east_opt`, so it would be redundant.
216     // But it is possible to read the offset directly from `Parsed`. We want to only successfully
217     // populate `Parsed` if the input is fully valid RFC 3339.
218     // Max for the hours field is `23`, and for the minutes field `59`.
219     const MAX_RFC3339_OFFSET: i32 = (23 * 60 + 59) * 60;
220     if !(-MAX_RFC3339_OFFSET..=MAX_RFC3339_OFFSET).contains(&offset) {
221         return Err(OUT_OF_RANGE);
222     }
223     parsed.set_offset(i64::from(offset))?;
224 
225     Ok((s, ()))
226 }
227 
228 /// Tries to parse given string into `parsed` with given formatting items.
229 /// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used).
230 /// There should be no trailing string after parsing;
231 /// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces.
232 ///
233 /// This particular date and time parser is:
234 ///
235 /// - Greedy. It will consume the longest possible prefix.
236 ///   For example, `April` is always consumed entirely when the long month name is requested;
237 ///   it equally accepts `Apr`, but prefers the longer prefix in this case.
238 ///
239 /// - Padding-agnostic (for numeric items).
240 ///   The [`Pad`](./enum.Pad.html) field is completely ignored,
241 ///   so one can prepend any number of whitespace then any number of zeroes before numbers.
242 ///
243 /// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()> where I: Iterator<Item = B>, B: Borrow<Item<'a>>,244 pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()>
245 where
246     I: Iterator<Item = B>,
247     B: Borrow<Item<'a>>,
248 {
249     match parse_internal(parsed, s, items) {
250         Ok("") => Ok(()),
251         Ok(_) => Err(TOO_LONG), // if there are trailing chars it is an error
252         Err(e) => Err(e),
253     }
254 }
255 
256 /// Tries to parse given string into `parsed` with given formatting items.
257 /// Returns `Ok` with a slice of the unparsed remainder.
258 ///
259 /// This particular date and time parser is:
260 ///
261 /// - Greedy. It will consume the longest possible prefix.
262 ///   For example, `April` is always consumed entirely when the long month name is requested;
263 ///   it equally accepts `Apr`, but prefers the longer prefix in this case.
264 ///
265 /// - Padding-agnostic (for numeric items).
266 ///   The [`Pad`](./enum.Pad.html) field is completely ignored,
267 ///   so one can prepend any number of zeroes before numbers.
268 ///
269 /// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
parse_and_remainder<'a, 'b, I, B>( parsed: &mut Parsed, s: &'b str, items: I, ) -> ParseResult<&'b str> where I: Iterator<Item = B>, B: Borrow<Item<'a>>,270 pub fn parse_and_remainder<'a, 'b, I, B>(
271     parsed: &mut Parsed,
272     s: &'b str,
273     items: I,
274 ) -> ParseResult<&'b str>
275 where
276     I: Iterator<Item = B>,
277     B: Borrow<Item<'a>>,
278 {
279     parse_internal(parsed, s, items)
280 }
281 
parse_internal<'a, 'b, I, B>( parsed: &mut Parsed, mut s: &'b str, items: I, ) -> Result<&'b str, ParseError> where I: Iterator<Item = B>, B: Borrow<Item<'a>>,282 fn parse_internal<'a, 'b, I, B>(
283     parsed: &mut Parsed,
284     mut s: &'b str,
285     items: I,
286 ) -> Result<&'b str, ParseError>
287 where
288     I: Iterator<Item = B>,
289     B: Borrow<Item<'a>>,
290 {
291     macro_rules! try_consume {
292         ($e:expr) => {{
293             match $e {
294                 Ok((s_, v)) => {
295                     s = s_;
296                     v
297                 }
298                 Err(e) => return Err(e),
299             }
300         }};
301     }
302 
303     for item in items {
304         match *item.borrow() {
305             Item::Literal(prefix) => {
306                 if s.len() < prefix.len() {
307                     return Err(TOO_SHORT);
308                 }
309                 if !s.starts_with(prefix) {
310                     return Err(INVALID);
311                 }
312                 s = &s[prefix.len()..];
313             }
314 
315             #[cfg(feature = "alloc")]
316             Item::OwnedLiteral(ref prefix) => {
317                 if s.len() < prefix.len() {
318                     return Err(TOO_SHORT);
319                 }
320                 if !s.starts_with(&prefix[..]) {
321                     return Err(INVALID);
322                 }
323                 s = &s[prefix.len()..];
324             }
325 
326             Item::Space(_) => {
327                 s = s.trim_start();
328             }
329 
330             #[cfg(feature = "alloc")]
331             Item::OwnedSpace(_) => {
332                 s = s.trim_start();
333             }
334 
335             Item::Numeric(ref spec, ref _pad) => {
336                 use super::Numeric::*;
337                 type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
338 
339                 let (width, signed, set): (usize, bool, Setter) = match *spec {
340                     Year => (4, true, Parsed::set_year),
341                     YearDiv100 => (2, false, Parsed::set_year_div_100),
342                     YearMod100 => (2, false, Parsed::set_year_mod_100),
343                     IsoYear => (4, true, Parsed::set_isoyear),
344                     IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
345                     IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
346                     Month => (2, false, Parsed::set_month),
347                     Day => (2, false, Parsed::set_day),
348                     WeekFromSun => (2, false, Parsed::set_week_from_sun),
349                     WeekFromMon => (2, false, Parsed::set_week_from_mon),
350                     IsoWeek => (2, false, Parsed::set_isoweek),
351                     NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
352                     WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
353                     Ordinal => (3, false, Parsed::set_ordinal),
354                     Hour => (2, false, Parsed::set_hour),
355                     Hour12 => (2, false, Parsed::set_hour12),
356                     Minute => (2, false, Parsed::set_minute),
357                     Second => (2, false, Parsed::set_second),
358                     Nanosecond => (9, false, Parsed::set_nanosecond),
359                     Timestamp => (usize::MAX, false, Parsed::set_timestamp),
360 
361                     // for the future expansion
362                     Internal(ref int) => match int._dummy {},
363                 };
364 
365                 s = s.trim_start();
366                 let v = if signed {
367                     if s.starts_with('-') {
368                         let v = try_consume!(scan::number(&s[1..], 1, usize::MAX));
369                         0i64.checked_sub(v).ok_or(OUT_OF_RANGE)?
370                     } else if s.starts_with('+') {
371                         try_consume!(scan::number(&s[1..], 1, usize::MAX))
372                     } else {
373                         // if there is no explicit sign, we respect the original `width`
374                         try_consume!(scan::number(s, 1, width))
375                     }
376                 } else {
377                     try_consume!(scan::number(s, 1, width))
378                 };
379                 set(parsed, v)?;
380             }
381 
382             Item::Fixed(ref spec) => {
383                 use super::Fixed::*;
384 
385                 match spec {
386                     &ShortMonthName => {
387                         let month0 = try_consume!(scan::short_month0(s));
388                         parsed.set_month(i64::from(month0) + 1)?;
389                     }
390 
391                     &LongMonthName => {
392                         let month0 = try_consume!(scan::short_or_long_month0(s));
393                         parsed.set_month(i64::from(month0) + 1)?;
394                     }
395 
396                     &ShortWeekdayName => {
397                         let weekday = try_consume!(scan::short_weekday(s));
398                         parsed.set_weekday(weekday)?;
399                     }
400 
401                     &LongWeekdayName => {
402                         let weekday = try_consume!(scan::short_or_long_weekday(s));
403                         parsed.set_weekday(weekday)?;
404                     }
405 
406                     &LowerAmPm | &UpperAmPm => {
407                         if s.len() < 2 {
408                             return Err(TOO_SHORT);
409                         }
410                         let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
411                             (b'a', b'm') => false,
412                             (b'p', b'm') => true,
413                             _ => return Err(INVALID),
414                         };
415                         parsed.set_ampm(ampm)?;
416                         s = &s[2..];
417                     }
418 
419                     &Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
420                         if s.starts_with('.') {
421                             let nano = try_consume!(scan::nanosecond(&s[1..]));
422                             parsed.set_nanosecond(nano)?;
423                         }
424                     }
425 
426                     &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
427                         if s.len() < 3 {
428                             return Err(TOO_SHORT);
429                         }
430                         let nano = try_consume!(scan::nanosecond_fixed(s, 3));
431                         parsed.set_nanosecond(nano)?;
432                     }
433 
434                     &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
435                         if s.len() < 6 {
436                             return Err(TOO_SHORT);
437                         }
438                         let nano = try_consume!(scan::nanosecond_fixed(s, 6));
439                         parsed.set_nanosecond(nano)?;
440                     }
441 
442                     &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
443                         if s.len() < 9 {
444                             return Err(TOO_SHORT);
445                         }
446                         let nano = try_consume!(scan::nanosecond_fixed(s, 9));
447                         parsed.set_nanosecond(nano)?;
448                     }
449 
450                     &TimezoneName => {
451                         try_consume!(Ok((s.trim_start_matches(|c: char| !c.is_whitespace()), ())));
452                     }
453 
454                     &TimezoneOffsetColon
455                     | &TimezoneOffsetDoubleColon
456                     | &TimezoneOffsetTripleColon
457                     | &TimezoneOffset => {
458                         let offset = try_consume!(scan::timezone_offset(
459                             s.trim_start(),
460                             scan::colon_or_space,
461                             false,
462                             false,
463                             true,
464                         ));
465                         parsed.set_offset(i64::from(offset))?;
466                     }
467 
468                     &TimezoneOffsetColonZ | &TimezoneOffsetZ => {
469                         let offset = try_consume!(scan::timezone_offset(
470                             s.trim_start(),
471                             scan::colon_or_space,
472                             true,
473                             false,
474                             true,
475                         ));
476                         parsed.set_offset(i64::from(offset))?;
477                     }
478                     &Internal(InternalFixed {
479                         val: InternalInternal::TimezoneOffsetPermissive,
480                     }) => {
481                         let offset = try_consume!(scan::timezone_offset(
482                             s.trim_start(),
483                             scan::colon_or_space,
484                             true,
485                             true,
486                             true,
487                         ));
488                         parsed.set_offset(i64::from(offset))?;
489                     }
490 
491                     &RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
492                     &RFC3339 => {
493                         // Used for the `%+` specifier, which has the description:
494                         // "Same as `%Y-%m-%dT%H:%M:%S%.f%:z` (...)
495                         // This format also supports having a `Z` or `UTC` in place of `%:z`."
496                         // Use the relaxed parser to match this description.
497                         try_consume!(parse_rfc3339_relaxed(parsed, s))
498                     }
499                 }
500             }
501 
502             Item::Error => {
503                 return Err(BAD_FORMAT);
504             }
505         }
506     }
507     Ok(s)
508 }
509 
510 /// Accepts a relaxed form of RFC3339.
511 /// A space or a 'T' are accepted as the separator between the date and time
512 /// parts. Additional spaces are allowed between each component.
513 ///
514 /// All of these examples are equivalent:
515 /// ```
516 /// # use chrono::{DateTime, offset::FixedOffset};
517 /// "2012-12-12T12:12:12Z".parse::<DateTime<FixedOffset>>()?;
518 /// "2012-12-12 12:12:12Z".parse::<DateTime<FixedOffset>>()?;
519 /// "2012-  12-12T12:  12:12Z".parse::<DateTime<FixedOffset>>()?;
520 /// # Ok::<(), chrono::ParseError>(())
521 /// ```
522 impl str::FromStr for DateTime<FixedOffset> {
523     type Err = ParseError;
524 
from_str(s: &str) -> ParseResult<DateTime<FixedOffset>>525     fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
526         let mut parsed = Parsed::new();
527         let (s, _) = parse_rfc3339_relaxed(&mut parsed, s)?;
528         if !s.trim_start().is_empty() {
529             return Err(TOO_LONG);
530         }
531         parsed.to_datetime()
532     }
533 }
534 
535 /// Accepts a relaxed form of RFC3339.
536 ///
537 /// Differences with RFC3339:
538 /// - Values don't require padding to two digits.
539 /// - Years outside the range 0...=9999 are accepted, but they must include a sign.
540 /// - `UTC` is accepted as a valid timezone name/offset (for compatibility with the debug format of
541 ///   `DateTime<Utc>`.
542 /// - There can be spaces between any of the components.
543 /// - The colon in the offset may be missing.
parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())>544 fn parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
545     const DATE_ITEMS: &[Item<'static>] = &[
546         Item::Numeric(Numeric::Year, Pad::Zero),
547         Item::Space(""),
548         Item::Literal("-"),
549         Item::Numeric(Numeric::Month, Pad::Zero),
550         Item::Space(""),
551         Item::Literal("-"),
552         Item::Numeric(Numeric::Day, Pad::Zero),
553     ];
554     const TIME_ITEMS: &[Item<'static>] = &[
555         Item::Numeric(Numeric::Hour, Pad::Zero),
556         Item::Space(""),
557         Item::Literal(":"),
558         Item::Numeric(Numeric::Minute, Pad::Zero),
559         Item::Space(""),
560         Item::Literal(":"),
561         Item::Numeric(Numeric::Second, Pad::Zero),
562         Item::Fixed(Fixed::Nanosecond),
563         Item::Space(""),
564     ];
565 
566     s = parse_internal(parsed, s, DATE_ITEMS.iter())?;
567 
568     s = match s.as_bytes().first() {
569         Some(&b't' | &b'T' | &b' ') => &s[1..],
570         Some(_) => return Err(INVALID),
571         None => return Err(TOO_SHORT),
572     };
573 
574     s = parse_internal(parsed, s, TIME_ITEMS.iter())?;
575     s = s.trim_start();
576     let (s, offset) = if s.len() >= 3 && "UTC".as_bytes().eq_ignore_ascii_case(&s.as_bytes()[..3]) {
577         (&s[3..], 0)
578     } else {
579         scan::timezone_offset(s, scan::colon_or_space, true, false, true)?
580     };
581     parsed.set_offset(i64::from(offset))?;
582     Ok((s, ()))
583 }
584 
585 #[cfg(test)]
586 mod tests {
587     use crate::format::*;
588     use crate::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Timelike, Utc};
589 
590     macro_rules! parsed {
591         ($($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
592             let mut expected = Parsed::new();
593             $(expected.$k = Some($v);)*
594             Ok(expected)
595         });
596     }
597 
598     #[test]
test_parse_whitespace_and_literal()599     fn test_parse_whitespace_and_literal() {
600         use crate::format::Item::{Literal, Space};
601 
602         // empty string
603         parses("", &[]);
604         check(" ", &[], Err(TOO_LONG));
605         check("a", &[], Err(TOO_LONG));
606         check("abc", &[], Err(TOO_LONG));
607         check("��", &[], Err(TOO_LONG));
608 
609         // whitespaces
610         parses("", &[Space("")]);
611         parses(" ", &[Space(" ")]);
612         parses("  ", &[Space("  ")]);
613         parses("   ", &[Space("   ")]);
614         parses(" ", &[Space("")]);
615         parses("  ", &[Space(" ")]);
616         parses("   ", &[Space("  ")]);
617         parses("    ", &[Space("  ")]);
618         parses("", &[Space(" ")]);
619         parses(" ", &[Space("  ")]);
620         parses("  ", &[Space("   ")]);
621         parses("  ", &[Space("  "), Space("  ")]);
622         parses("   ", &[Space("  "), Space("  ")]);
623         parses("  ", &[Space(" "), Space(" ")]);
624         parses("   ", &[Space("  "), Space(" ")]);
625         parses("   ", &[Space(" "), Space("  ")]);
626         parses("   ", &[Space(" "), Space(" "), Space(" ")]);
627         parses("\t", &[Space("")]);
628         parses(" \n\r  \n", &[Space("")]);
629         parses("\t", &[Space("\t")]);
630         parses("\t", &[Space(" ")]);
631         parses(" ", &[Space("\t")]);
632         parses("\t\r", &[Space("\t\r")]);
633         parses("\t\r ", &[Space("\t\r ")]);
634         parses("\t \r", &[Space("\t \r")]);
635         parses(" \t\r", &[Space(" \t\r")]);
636         parses(" \n\r  \n", &[Space(" \n\r  \n")]);
637         parses(" \t\n", &[Space(" \t")]);
638         parses(" \n\t", &[Space(" \t\n")]);
639         parses("\u{2002}", &[Space("\u{2002}")]);
640         // most unicode whitespace characters
641         parses(
642             "\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}",
643             &[Space("\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}")]
644         );
645         // most unicode whitespace characters
646         parses(
647             "\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}",
648             &[
649                 Space("\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}"),
650                 Space("\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}")
651             ]
652         );
653         check("a", &[Space("")], Err(TOO_LONG));
654         check("a", &[Space(" ")], Err(TOO_LONG));
655         // a Space containing a literal does not match a literal
656         check("a", &[Space("a")], Err(TOO_LONG));
657         check("abc", &[Space("")], Err(TOO_LONG));
658         check("abc", &[Space(" ")], Err(TOO_LONG));
659         check(" abc", &[Space("")], Err(TOO_LONG));
660         check(" abc", &[Space(" ")], Err(TOO_LONG));
661 
662         // `\u{0363}` is combining diacritic mark "COMBINING LATIN SMALL LETTER A"
663 
664         // literal
665         parses("", &[Literal("")]);
666         check("", &[Literal("a")], Err(TOO_SHORT));
667         check(" ", &[Literal("a")], Err(INVALID));
668         parses("a", &[Literal("a")]);
669         parses("+", &[Literal("+")]);
670         parses("-", &[Literal("-")]);
671         parses("−", &[Literal("−")]); // MINUS SIGN (U+2212)
672         parses(" ", &[Literal(" ")]); // a Literal may contain whitespace and match whitespace
673         check("aa", &[Literal("a")], Err(TOO_LONG));
674         check("��", &[Literal("a")], Err(INVALID));
675         check("A", &[Literal("a")], Err(INVALID));
676         check("a", &[Literal("z")], Err(INVALID));
677         check("a", &[Literal("��")], Err(TOO_SHORT));
678         check("a", &[Literal("\u{0363}a")], Err(TOO_SHORT));
679         check("\u{0363}a", &[Literal("a")], Err(INVALID));
680         parses("\u{0363}a", &[Literal("\u{0363}a")]);
681         check("a", &[Literal("ab")], Err(TOO_SHORT));
682         parses("xy", &[Literal("xy")]);
683         parses("xy", &[Literal("x"), Literal("y")]);
684         parses("1", &[Literal("1")]);
685         parses("1234", &[Literal("1234")]);
686         parses("+1234", &[Literal("+1234")]);
687         parses("-1234", &[Literal("-1234")]);
688         parses("−1234", &[Literal("−1234")]); // MINUS SIGN (U+2212)
689         parses("PST", &[Literal("PST")]);
690         parses("��", &[Literal("��")]);
691         parses("��a", &[Literal("��"), Literal("a")]);
692         parses("��a��", &[Literal("��"), Literal("a��")]);
693         parses("a��b", &[Literal("a"), Literal("��"), Literal("b")]);
694         // literals can be together
695         parses("xy", &[Literal("xy")]);
696         parses("xyz", &[Literal("xyz")]);
697         // or literals can be apart
698         parses("xy", &[Literal("x"), Literal("y")]);
699         parses("xyz", &[Literal("x"), Literal("yz")]);
700         parses("xyz", &[Literal("xy"), Literal("z")]);
701         parses("xyz", &[Literal("x"), Literal("y"), Literal("z")]);
702         //
703         check("x y", &[Literal("x"), Literal("y")], Err(INVALID));
704         parses("xy", &[Literal("x"), Space(""), Literal("y")]);
705         parses("x y", &[Literal("x"), Space(""), Literal("y")]);
706         parses("x y", &[Literal("x"), Space(" "), Literal("y")]);
707 
708         // whitespaces + literals
709         parses("a\n", &[Literal("a"), Space("\n")]);
710         parses("\tab\n", &[Space("\t"), Literal("ab"), Space("\n")]);
711         parses(
712             "ab\tcd\ne",
713             &[Literal("ab"), Space("\t"), Literal("cd"), Space("\n"), Literal("e")],
714         );
715         parses(
716             "+1ab\tcd\r\n+,.",
717             &[Literal("+1ab"), Space("\t"), Literal("cd"), Space("\r\n"), Literal("+,.")],
718         );
719         // whitespace and literals can be intermixed
720         parses("a\tb", &[Literal("a\tb")]);
721         parses("a\tb", &[Literal("a"), Space("\t"), Literal("b")]);
722     }
723 
724     #[test]
test_parse_numeric()725     fn test_parse_numeric() {
726         use crate::format::Item::{Literal, Space};
727         use crate::format::Numeric::*;
728 
729         // numeric
730         check("1987", &[num(Year)], parsed!(year: 1987));
731         check("1987 ", &[num(Year)], Err(TOO_LONG));
732         check("0x12", &[num(Year)], Err(TOO_LONG)); // `0` is parsed
733         check("x123", &[num(Year)], Err(INVALID));
734         check("o123", &[num(Year)], Err(INVALID));
735         check("2015", &[num(Year)], parsed!(year: 2015));
736         check("0000", &[num(Year)], parsed!(year: 0));
737         check("9999", &[num(Year)], parsed!(year: 9999));
738         check(" \t987", &[num(Year)], parsed!(year: 987));
739         check(" \t987", &[Space(" \t"), num(Year)], parsed!(year: 987));
740         check(" \t987��", &[Space(" \t"), num(Year), Literal("��")], parsed!(year: 987));
741         check("987��", &[num(Year), Literal("��")], parsed!(year: 987));
742         check("5", &[num(Year)], parsed!(year: 5));
743         check("5\0", &[num(Year)], Err(TOO_LONG));
744         check("\x005", &[num(Year)], Err(INVALID));
745         check("", &[num(Year)], Err(TOO_SHORT));
746         check("12345", &[num(Year), Literal("5")], parsed!(year: 1234));
747         check("12345", &[nums(Year), Literal("5")], parsed!(year: 1234));
748         check("12345", &[num0(Year), Literal("5")], parsed!(year: 1234));
749         check("12341234", &[num(Year), num(Year)], parsed!(year: 1234));
750         check("1234 1234", &[num(Year), num(Year)], parsed!(year: 1234));
751         check("1234 1234", &[num(Year), Space(" "), num(Year)], parsed!(year: 1234));
752         check("1234 1235", &[num(Year), num(Year)], Err(IMPOSSIBLE));
753         check("1234 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID));
754         check("1234x1234", &[num(Year), Literal("x"), num(Year)], parsed!(year: 1234));
755         check("1234 x 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID));
756         check("1234xx1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID));
757         check("1234xx1234", &[num(Year), Literal("xx"), num(Year)], parsed!(year: 1234));
758         check(
759             "1234 x 1234",
760             &[num(Year), Space(" "), Literal("x"), Space(" "), num(Year)],
761             parsed!(year: 1234),
762         );
763         check(
764             "1234 x 1235",
765             &[num(Year), Space(" "), Literal("x"), Space(" "), Literal("1235")],
766             parsed!(year: 1234),
767         );
768 
769         // signed numeric
770         check("-42", &[num(Year)], parsed!(year: -42));
771         check("+42", &[num(Year)], parsed!(year: 42));
772         check("-0042", &[num(Year)], parsed!(year: -42));
773         check("+0042", &[num(Year)], parsed!(year: 42));
774         check("-42195", &[num(Year)], parsed!(year: -42195));
775         check("−42195", &[num(Year)], Err(INVALID)); // MINUS SIGN (U+2212)
776         check("+42195", &[num(Year)], parsed!(year: 42195));
777         check("  -42195", &[num(Year)], parsed!(year: -42195));
778         check(" +42195", &[num(Year)], parsed!(year: 42195));
779         check("  -42195", &[num(Year)], parsed!(year: -42195));
780         check("  +42195", &[num(Year)], parsed!(year: 42195));
781         check("-42195 ", &[num(Year)], Err(TOO_LONG));
782         check("+42195 ", &[num(Year)], Err(TOO_LONG));
783         check("  -   42", &[num(Year)], Err(INVALID));
784         check("  +   42", &[num(Year)], Err(INVALID));
785         check("  -42195", &[Space("  "), num(Year)], parsed!(year: -42195));
786         check("  −42195", &[Space("  "), num(Year)], Err(INVALID)); // MINUS SIGN (U+2212)
787         check("  +42195", &[Space("  "), num(Year)], parsed!(year: 42195));
788         check("  -   42", &[Space("  "), num(Year)], Err(INVALID));
789         check("  +   42", &[Space("  "), num(Year)], Err(INVALID));
790         check("-", &[num(Year)], Err(TOO_SHORT));
791         check("+", &[num(Year)], Err(TOO_SHORT));
792 
793         // unsigned numeric
794         check("345", &[num(Ordinal)], parsed!(ordinal: 345));
795         check("+345", &[num(Ordinal)], Err(INVALID));
796         check("-345", &[num(Ordinal)], Err(INVALID));
797         check(" 345", &[num(Ordinal)], parsed!(ordinal: 345));
798         check("−345", &[num(Ordinal)], Err(INVALID)); // MINUS SIGN (U+2212)
799         check("345 ", &[num(Ordinal)], Err(TOO_LONG));
800         check(" 345", &[Space(" "), num(Ordinal)], parsed!(ordinal: 345));
801         check("345 ", &[num(Ordinal), Space(" ")], parsed!(ordinal: 345));
802         check("345�� ", &[num(Ordinal), Literal("��"), Space(" ")], parsed!(ordinal: 345));
803         check("345��", &[num(Ordinal)], Err(TOO_LONG));
804         check("\u{0363}345", &[num(Ordinal)], Err(INVALID));
805         check(" +345", &[num(Ordinal)], Err(INVALID));
806         check(" -345", &[num(Ordinal)], Err(INVALID));
807         check("\t345", &[Space("\t"), num(Ordinal)], parsed!(ordinal: 345));
808         check(" +345", &[Space(" "), num(Ordinal)], Err(INVALID));
809         check(" -345", &[Space(" "), num(Ordinal)], Err(INVALID));
810 
811         // various numeric fields
812         check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678));
813         check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678));
814         check(
815             "12 34 56 78",
816             &[num(YearDiv100), num(YearMod100), num(IsoYearDiv100), num(IsoYearMod100)],
817             parsed!(year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78),
818         );
819         check(
820             "1 2 3 4 5",
821             &[num(Month), num(Day), num(WeekFromSun), num(NumDaysFromSun), num(IsoWeek)],
822             parsed!(month: 1, day: 2, week_from_sun: 3, weekday: Weekday::Thu, isoweek: 5),
823         );
824         check(
825             "6 7 89 01",
826             &[num(WeekFromMon), num(WeekdayFromMon), num(Ordinal), num(Hour12)],
827             parsed!(week_from_mon: 6, weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1),
828         );
829         check(
830             "23 45 6 78901234 567890123",
831             &[num(Hour), num(Minute), num(Second), num(Nanosecond), num(Timestamp)],
832             parsed!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234, timestamp: 567_890_123),
833         );
834     }
835 
836     #[test]
test_parse_fixed()837     fn test_parse_fixed() {
838         use crate::format::Fixed::*;
839         use crate::format::Item::{Literal, Space};
840 
841         // fixed: month and weekday names
842         check("apr", &[fixed(ShortMonthName)], parsed!(month: 4));
843         check("Apr", &[fixed(ShortMonthName)], parsed!(month: 4));
844         check("APR", &[fixed(ShortMonthName)], parsed!(month: 4));
845         check("ApR", &[fixed(ShortMonthName)], parsed!(month: 4));
846         check("\u{0363}APR", &[fixed(ShortMonthName)], Err(INVALID));
847         check("April", &[fixed(ShortMonthName)], Err(TOO_LONG)); // `Apr` is parsed
848         check("A", &[fixed(ShortMonthName)], Err(TOO_SHORT));
849         check("Sol", &[fixed(ShortMonthName)], Err(INVALID));
850         check("Apr", &[fixed(LongMonthName)], parsed!(month: 4));
851         check("Apri", &[fixed(LongMonthName)], Err(TOO_LONG)); // `Apr` is parsed
852         check("April", &[fixed(LongMonthName)], parsed!(month: 4));
853         check("Aprill", &[fixed(LongMonthName)], Err(TOO_LONG));
854         check("Aprill", &[fixed(LongMonthName), Literal("l")], parsed!(month: 4));
855         check("Aprl", &[fixed(LongMonthName), Literal("l")], parsed!(month: 4));
856         check("April", &[fixed(LongMonthName), Literal("il")], Err(TOO_SHORT)); // do not backtrack
857         check("thu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
858         check("Thu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
859         check("THU", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
860         check("tHu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
861         check("Thursday", &[fixed(ShortWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed
862         check("T", &[fixed(ShortWeekdayName)], Err(TOO_SHORT));
863         check("The", &[fixed(ShortWeekdayName)], Err(INVALID));
864         check("Nop", &[fixed(ShortWeekdayName)], Err(INVALID));
865         check("Thu", &[fixed(LongWeekdayName)], parsed!(weekday: Weekday::Thu));
866         check("Thur", &[fixed(LongWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed
867         check("Thurs", &[fixed(LongWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed
868         check("Thursday", &[fixed(LongWeekdayName)], parsed!(weekday: Weekday::Thu));
869         check("Thursdays", &[fixed(LongWeekdayName)], Err(TOO_LONG));
870         check("Thursdays", &[fixed(LongWeekdayName), Literal("s")], parsed!(weekday: Weekday::Thu));
871         check("Thus", &[fixed(LongWeekdayName), Literal("s")], parsed!(weekday: Weekday::Thu));
872         check("Thursday", &[fixed(LongWeekdayName), Literal("rsday")], Err(TOO_SHORT)); // do not backtrack
873 
874         // fixed: am/pm
875         check("am", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0));
876         check("pm", &[fixed(LowerAmPm)], parsed!(hour_div_12: 1));
877         check("AM", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0));
878         check("PM", &[fixed(LowerAmPm)], parsed!(hour_div_12: 1));
879         check("am", &[fixed(UpperAmPm)], parsed!(hour_div_12: 0));
880         check("pm", &[fixed(UpperAmPm)], parsed!(hour_div_12: 1));
881         check("AM", &[fixed(UpperAmPm)], parsed!(hour_div_12: 0));
882         check("PM", &[fixed(UpperAmPm)], parsed!(hour_div_12: 1));
883         check("Am", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0));
884         check(" Am", &[Space(" "), fixed(LowerAmPm)], parsed!(hour_div_12: 0));
885         check("Am��", &[fixed(LowerAmPm), Literal("��")], parsed!(hour_div_12: 0));
886         check("��Am", &[Literal("��"), fixed(LowerAmPm)], parsed!(hour_div_12: 0));
887         check("\u{0363}am", &[fixed(LowerAmPm)], Err(INVALID));
888         check("\u{0360}am", &[fixed(LowerAmPm)], Err(INVALID));
889         check(" Am", &[fixed(LowerAmPm)], Err(INVALID));
890         check("Am ", &[fixed(LowerAmPm)], Err(TOO_LONG));
891         check("a.m.", &[fixed(LowerAmPm)], Err(INVALID));
892         check("A.M.", &[fixed(LowerAmPm)], Err(INVALID));
893         check("ame", &[fixed(LowerAmPm)], Err(TOO_LONG)); // `am` is parsed
894         check("a", &[fixed(LowerAmPm)], Err(TOO_SHORT));
895         check("p", &[fixed(LowerAmPm)], Err(TOO_SHORT));
896         check("x", &[fixed(LowerAmPm)], Err(TOO_SHORT));
897         check("xx", &[fixed(LowerAmPm)], Err(INVALID));
898         check("", &[fixed(LowerAmPm)], Err(TOO_SHORT));
899     }
900 
901     #[test]
test_parse_fixed_nanosecond()902     fn test_parse_fixed_nanosecond() {
903         use crate::format::Fixed::Nanosecond;
904         use crate::format::InternalInternal::*;
905         use crate::format::Item::Literal;
906         use crate::format::Numeric::Second;
907 
908         // fixed: dot plus nanoseconds
909         check("", &[fixed(Nanosecond)], parsed!()); // no field set, but not an error
910         check(".", &[fixed(Nanosecond)], Err(TOO_SHORT));
911         check("4", &[fixed(Nanosecond)], Err(TOO_LONG)); // never consumes `4`
912         check("4", &[fixed(Nanosecond), num(Second)], parsed!(second: 4));
913         check(".0", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
914         check(".4", &[fixed(Nanosecond)], parsed!(nanosecond: 400_000_000));
915         check(".42", &[fixed(Nanosecond)], parsed!(nanosecond: 420_000_000));
916         check(".421", &[fixed(Nanosecond)], parsed!(nanosecond: 421_000_000));
917         check(".42195", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_000));
918         check(".421951", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_000));
919         check(".4219512", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_200));
920         check(".42195123", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_230));
921         check(".421950803", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
922         check(".4219508035", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
923         check(".42195080354", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
924         check(".421950803547", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
925         check(".000000003", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
926         check(".0000000031", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
927         check(".0000000035", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
928         check(".000000003547", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
929         check(".0000000009", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
930         check(".000000000547", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
931         check(".0000000009999999999999999999999999", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
932         check(".4��", &[fixed(Nanosecond), Literal("��")], parsed!(nanosecond: 400_000_000));
933         check(".4x", &[fixed(Nanosecond)], Err(TOO_LONG));
934         check(".  4", &[fixed(Nanosecond)], Err(INVALID));
935         check("  .4", &[fixed(Nanosecond)], Err(TOO_LONG)); // no automatic trimming
936 
937         // fixed: nanoseconds without the dot
938         check("", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
939         check(".", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
940         check("0", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
941         check("4", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
942         check("42", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
943         check("421", &[internal_fixed(Nanosecond3NoDot)], parsed!(nanosecond: 421_000_000));
944         check("4210", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG));
945         check(
946             "42143",
947             &[internal_fixed(Nanosecond3NoDot), num(Second)],
948             parsed!(nanosecond: 421_000_000, second: 43),
949         );
950         check(
951             "421��",
952             &[internal_fixed(Nanosecond3NoDot), Literal("��")],
953             parsed!(nanosecond: 421_000_000),
954         );
955         check(
956             "��421",
957             &[Literal("��"), internal_fixed(Nanosecond3NoDot)],
958             parsed!(nanosecond: 421_000_000),
959         );
960         check("42195", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG));
961         check("123456789", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG));
962         check("4x", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
963         check("  4", &[internal_fixed(Nanosecond3NoDot)], Err(INVALID));
964         check(".421", &[internal_fixed(Nanosecond3NoDot)], Err(INVALID));
965 
966         check("", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
967         check(".", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
968         check("0", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
969         check("1234", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
970         check("12345", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
971         check("421950", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 421_950_000));
972         check("000003", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 3000));
973         check("000000", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 0));
974         check("1234567", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_LONG));
975         check("123456789", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_LONG));
976         check("4x", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
977         check("     4", &[internal_fixed(Nanosecond6NoDot)], Err(INVALID));
978         check(".42100", &[internal_fixed(Nanosecond6NoDot)], Err(INVALID));
979 
980         check("", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
981         check(".", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
982         check("42195", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
983         check("12345678", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
984         check("421950803", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 421_950_803));
985         check("000000003", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 3));
986         check(
987             "42195080354",
988             &[internal_fixed(Nanosecond9NoDot), num(Second)],
989             parsed!(nanosecond: 421_950_803, second: 54),
990         ); // don't skip digits that come after the 9
991         check("1234567890", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_LONG));
992         check("000000000", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 0));
993         check("00000000x", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID));
994         check("        4", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID));
995         check(".42100000", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID));
996     }
997 
998     #[test]
test_parse_fixed_timezone_offset()999     fn test_parse_fixed_timezone_offset() {
1000         use crate::format::Fixed::*;
1001         use crate::format::InternalInternal::*;
1002         use crate::format::Item::Literal;
1003 
1004         // TimezoneOffset
1005         check("1", &[fixed(TimezoneOffset)], Err(INVALID));
1006         check("12", &[fixed(TimezoneOffset)], Err(INVALID));
1007         check("123", &[fixed(TimezoneOffset)], Err(INVALID));
1008         check("1234", &[fixed(TimezoneOffset)], Err(INVALID));
1009         check("12345", &[fixed(TimezoneOffset)], Err(INVALID));
1010         check("123456", &[fixed(TimezoneOffset)], Err(INVALID));
1011         check("1234567", &[fixed(TimezoneOffset)], Err(INVALID));
1012         check("+1", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1013         check("+12", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1014         check("+123", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1015         check("+1234", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1016         check("+12345", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1017         check("+123456", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1018         check("+1234567", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1019         check("+12345678", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1020         check("+12:", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1021         check("+12:3", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1022         check("+12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1023         check("-12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1024         check("−12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1025         check("+12:34:", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1026         check("+12:34:5", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1027         check("+12:34:56", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1028         check("+12:34:56:", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1029         check("+12 34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1030         check("+12  34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1031         check("12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1032         check("12:34:56", &[fixed(TimezoneOffset)], Err(INVALID));
1033         check("+12::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1034         check("+12: :34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1035         check("+12:::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1036         check("+12::::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1037         check("+12::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1038         check("+12:34:56", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1039         check("+12:3456", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1040         check("+1234:56", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1041         check("+1234:567", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1042         check("+00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0));
1043         check("-00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0));
1044         check("−00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0)); // MINUS SIGN (U+2212)
1045         check("+00:01", &[fixed(TimezoneOffset)], parsed!(offset: 60));
1046         check("-00:01", &[fixed(TimezoneOffset)], parsed!(offset: -60));
1047         check("+00:30", &[fixed(TimezoneOffset)], parsed!(offset: 1_800));
1048         check("-00:30", &[fixed(TimezoneOffset)], parsed!(offset: -1_800));
1049         check("+24:00", &[fixed(TimezoneOffset)], parsed!(offset: 86_400));
1050         check("-24:00", &[fixed(TimezoneOffset)], parsed!(offset: -86_400));
1051         check("−24:00", &[fixed(TimezoneOffset)], parsed!(offset: -86_400)); // MINUS SIGN (U+2212)
1052         check("+99:59", &[fixed(TimezoneOffset)], parsed!(offset: 359_940));
1053         check("-99:59", &[fixed(TimezoneOffset)], parsed!(offset: -359_940));
1054         check("+00:60", &[fixed(TimezoneOffset)], Err(OUT_OF_RANGE));
1055         check("+00:99", &[fixed(TimezoneOffset)], Err(OUT_OF_RANGE));
1056         check("#12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1057         check("+12:34 ", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1058         check("+12 34 ", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1059         check(" +12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1060         check(" -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1061         check(" −12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1062         check("  +12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
1063         check("  -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1064         check("\t -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1065         check("-12: 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1066         check("-12 :34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1067         check("-12 : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1068         check("-12 :  34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1069         check("-12  : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1070         check("-12:  34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1071         check("-12  :34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1072         check("-12  :  34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
1073         check("12:34 ", &[fixed(TimezoneOffset)], Err(INVALID));
1074         check(" 12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1075         check("", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1076         check("+", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1077         check(
1078             "+12345",
1079             &[fixed(TimezoneOffset), num(Numeric::Day)],
1080             parsed!(offset: 45_240, day: 5),
1081         );
1082         check(
1083             "+12:345",
1084             &[fixed(TimezoneOffset), num(Numeric::Day)],
1085             parsed!(offset: 45_240, day: 5),
1086         );
1087         check("+12:34:", &[fixed(TimezoneOffset), Literal(":")], parsed!(offset: 45_240));
1088         check("Z12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1089         check("X12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1090         check("Z+12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1091         check("X+12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1092         check("X−12:34", &[fixed(TimezoneOffset)], Err(INVALID)); // MINUS SIGN (U+2212)
1093         check("��+12:34", &[fixed(TimezoneOffset)], Err(INVALID));
1094         check("+12:34��", &[fixed(TimezoneOffset)], Err(TOO_LONG));
1095         check("+12:��34", &[fixed(TimezoneOffset)], Err(INVALID));
1096         check("+1234��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: 45_240));
1097         check("-1234��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240));
1098         check("−1234��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1099         check("+12:34��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: 45_240));
1100         check("-12:34��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240));
1101         check("−12:34��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1102         check("��+12:34", &[Literal("��"), fixed(TimezoneOffset)], parsed!(offset: 45_240));
1103         check("Z", &[fixed(TimezoneOffset)], Err(INVALID));
1104         check("A", &[fixed(TimezoneOffset)], Err(INVALID));
1105         check("PST", &[fixed(TimezoneOffset)], Err(INVALID));
1106         check("#Z", &[fixed(TimezoneOffset)], Err(INVALID));
1107         check(":Z", &[fixed(TimezoneOffset)], Err(INVALID));
1108         check("+Z", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
1109         check("+:Z", &[fixed(TimezoneOffset)], Err(INVALID));
1110         check("+Z:", &[fixed(TimezoneOffset)], Err(INVALID));
1111         check("z", &[fixed(TimezoneOffset)], Err(INVALID));
1112         check(" :Z", &[fixed(TimezoneOffset)], Err(INVALID));
1113         check(" Z", &[fixed(TimezoneOffset)], Err(INVALID));
1114         check(" z", &[fixed(TimezoneOffset)], Err(INVALID));
1115 
1116         // TimezoneOffsetColon
1117         check("1", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1118         check("12", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1119         check("123", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1120         check("1234", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1121         check("12345", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1122         check("123456", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1123         check("1234567", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1124         check("12345678", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1125         check("+1", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1126         check("+12", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1127         check("+123", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1128         check("+1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1129         check("-1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240));
1130         check("−1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1131         check("+12345", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1132         check("+123456", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1133         check("+1234567", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1134         check("+12345678", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1135         check("1:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1136         check("12:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1137         check("12:3", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1138         check("12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1139         check("12:34:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1140         check("12:34:5", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1141         check("12:34:56", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1142         check("+1:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1143         check("+12:", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1144         check("+12:3", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1145         check("+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1146         check("-12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240));
1147         check("−12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1148         check("+12:34:", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1149         check("+12:34:5", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1150         check("+12:34:56", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1151         check("+12:34:56:", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1152         check("+12:34:56:7", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1153         check("+12:34:56:78", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1154         check("+12:3456", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1155         check("+1234:56", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1156         check("−12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1157         check("−12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1158         check("+12 :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1159         check("+12: 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1160         check("+12 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1161         check("+12: 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1162         check("+12 :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1163         check("+12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1164         check("-12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240));
1165         check("+12  : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1166         check("+12 :  34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1167         check("+12  :  34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1168         check("+12::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1169         check("+12: :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1170         check("+12:::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1171         check("+12::::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1172         check("+12::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1173         check("#1234", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1174         check("#12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1175         check("+12:34 ", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
1176         check(" +12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1177         check("\t+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1178         check("\t\t+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
1179         check("12:34 ", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1180         check(" 12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1181         check("", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1182         check("+", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1183         check(":", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1184         check(
1185             "+12345",
1186             &[fixed(TimezoneOffsetColon), num(Numeric::Day)],
1187             parsed!(offset: 45_240, day: 5),
1188         );
1189         check(
1190             "+12:345",
1191             &[fixed(TimezoneOffsetColon), num(Numeric::Day)],
1192             parsed!(offset: 45_240, day: 5),
1193         );
1194         check("+12:34:", &[fixed(TimezoneOffsetColon), Literal(":")], parsed!(offset: 45_240));
1195         check("Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1196         check("A", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1197         check("PST", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1198         check("#Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1199         check(":Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1200         check("+Z", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
1201         check("+:Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1202         check("+Z:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1203         check("z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1204         check(" :Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1205         check(" Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1206         check(" z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
1207         // testing `TimezoneOffsetColon` also tests same path as `TimezoneOffsetDoubleColon`
1208         // and `TimezoneOffsetTripleColon` for function `parse_internal`.
1209         // No need for separate tests for `TimezoneOffsetDoubleColon` and
1210         // `TimezoneOffsetTripleColon`.
1211 
1212         // TimezoneOffsetZ
1213         check("1", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1214         check("12", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1215         check("123", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1216         check("1234", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1217         check("12345", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1218         check("123456", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1219         check("1234567", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1220         check("12345678", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1221         check("+1", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1222         check("+12", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1223         check("+123", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1224         check("+1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1225         check("-1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240));
1226         check("−1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1227         check("+12345", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1228         check("+123456", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1229         check("+1234567", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1230         check("+12345678", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1231         check("1:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1232         check("12:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1233         check("12:3", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1234         check("12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1235         check("12:34:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1236         check("12:34:5", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1237         check("12:34:56", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1238         check("+1:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1239         check("+12:", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1240         check("+12:3", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1241         check("+12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1242         check("-12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240));
1243         check("−12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1244         check("+12:34:", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1245         check("+12:34:5", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1246         check("+12:34:56", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1247         check("+12:34:56:", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1248         check("+12:34:56:7", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1249         check("+12:34:56:78", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1250         check("+12::34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1251         check("+12:3456", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1252         check("+1234:56", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1253         check("+12 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1254         check("+12  34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1255         check("+12: 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1256         check("+12 :34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1257         check("+12 : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1258         check("+12  : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1259         check("+12 :  34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1260         check("+12  :  34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1261         check("12:34 ", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1262         check(" 12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1263         check("+12:34 ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1264         check("+12 34 ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1265         check(" +12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
1266         check(
1267             "+12345",
1268             &[fixed(TimezoneOffsetZ), num(Numeric::Day)],
1269             parsed!(offset: 45_240, day: 5),
1270         );
1271         check(
1272             "+12:345",
1273             &[fixed(TimezoneOffsetZ), num(Numeric::Day)],
1274             parsed!(offset: 45_240, day: 5),
1275         );
1276         check("+12:34:", &[fixed(TimezoneOffsetZ), Literal(":")], parsed!(offset: 45_240));
1277         check("Z12:34", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1278         check("X12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1279         check("Z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
1280         check("z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
1281         check(" Z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
1282         check(" z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
1283         check("\u{0363}Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1284         check("Z ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
1285         check("A", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1286         check("PST", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1287         check("#Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1288         check(":Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1289         check(":z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1290         check("+Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1291         check("-Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1292         check("+A", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1293         check("+��", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1294         check("+Z:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1295         check(" :Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1296         check(" +Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1297         check(" -Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
1298         check("+:Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1299         check("Y", &[fixed(TimezoneOffsetZ)], Err(INVALID));
1300         check("Zulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 0));
1301         check("zulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 0));
1302         check("+1234ulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 45_240));
1303         check("+12:34ulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 45_240));
1304         // Testing `TimezoneOffsetZ` also tests same path as `TimezoneOffsetColonZ`
1305         // in function `parse_internal`.
1306         // No need for separate tests for `TimezoneOffsetColonZ`.
1307 
1308         // TimezoneOffsetPermissive
1309         check("1", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1310         check("12", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1311         check("123", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1312         check("1234", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1313         check("12345", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1314         check("123456", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1315         check("1234567", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1316         check("12345678", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1317         check("+1", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1318         check("+12", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 43_200));
1319         check("+123", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1320         check("+1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1321         check("-1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240));
1322         check("−1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1323         check("+12345", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1324         check("+123456", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1325         check("+1234567", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1326         check("+12345678", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1327         check("1:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1328         check("12:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1329         check("12:3", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1330         check("12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1331         check("12:34:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1332         check("12:34:5", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1333         check("12:34:56", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1334         check("+1:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1335         check("+12:", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 43_200));
1336         check("+12:3", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1337         check("+12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1338         check("-12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240));
1339         check("−12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1340         check("+12:34:", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1341         check("+12:34:5", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1342         check("+12:34:56", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1343         check("+12:34:56:", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1344         check("+12:34:56:7", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1345         check("+12:34:56:78", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1346         check("+12 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1347         check("+12  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1348         check("+12 :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1349         check("+12: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1350         check("+12 : 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1351         check("+12  :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1352         check("+12:  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1353         check("+12  :  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1354         check("+12::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1355         check("+12 ::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1356         check("+12: :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1357         check("+12:: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1358         check("+12  ::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1359         check("+12:  :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1360         check("+12::  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1361         check("+12:::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1362         check("+12::::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1363         check("12:34 ", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1364         check(" 12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1365         check("+12:34 ", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1366         check(" +12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
1367         check(" -12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240));
1368         check(" −12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
1369         check(
1370             "+12345",
1371             &[internal_fixed(TimezoneOffsetPermissive), num(Numeric::Day)],
1372             parsed!(offset: 45_240, day: 5),
1373         );
1374         check(
1375             "+12:345",
1376             &[internal_fixed(TimezoneOffsetPermissive), num(Numeric::Day)],
1377             parsed!(offset: 45_240, day: 5),
1378         );
1379         check(
1380             "+12:34:",
1381             &[internal_fixed(TimezoneOffsetPermissive), Literal(":")],
1382             parsed!(offset: 45_240),
1383         );
1384         check("��+12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1385         check("+12:34��", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1386         check("+12:��34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1387         check(
1388             "+12:34��",
1389             &[internal_fixed(TimezoneOffsetPermissive), Literal("��")],
1390             parsed!(offset: 45_240),
1391         );
1392         check(
1393             "��+12:34",
1394             &[Literal("��"), internal_fixed(TimezoneOffsetPermissive)],
1395             parsed!(offset: 45_240),
1396         );
1397         check("Z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
1398         check("A", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1399         check("PST", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1400         check("z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
1401         check(" Z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
1402         check(" z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
1403         check("Z ", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
1404         check("#Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1405         check(":Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1406         check(":z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1407         check("+Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1408         check("-Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1409         check("+A", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1410         check("+PST", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1411         check("+��", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1412         check("+Z:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1413         check(" :Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1414         check(" +Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1415         check(" -Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
1416         check("+:Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1417         check("Y", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
1418 
1419         // TimezoneName
1420         check("CEST", &[fixed(TimezoneName)], parsed!());
1421         check("cest", &[fixed(TimezoneName)], parsed!()); // lowercase
1422         check("XXXXXXXX", &[fixed(TimezoneName)], parsed!()); // not a real timezone name
1423         check("!!!!", &[fixed(TimezoneName)], parsed!()); // not a real timezone name!
1424         check("CEST 5", &[fixed(TimezoneName), Literal(" "), num(Numeric::Day)], parsed!(day: 5));
1425         check("CEST ", &[fixed(TimezoneName)], Err(TOO_LONG));
1426         check(" CEST", &[fixed(TimezoneName)], Err(TOO_LONG));
1427         check("CE ST", &[fixed(TimezoneName)], Err(TOO_LONG));
1428     }
1429 
1430     #[test]
1431     #[rustfmt::skip]
test_parse_practical_examples()1432     fn test_parse_practical_examples() {
1433         use crate::format::InternalInternal::*;
1434         use crate::format::Item::{Literal, Space};
1435         use crate::format::Numeric::*;
1436 
1437         // some practical examples
1438         check(
1439             "2015-02-04T14:37:05+09:00",
1440             &[
1441                 num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"),
1442                 num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second),
1443                 fixed(Fixed::TimezoneOffset),
1444             ],
1445             parsed!(
1446                 year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
1447                 second: 5, offset: 32400
1448             ),
1449         );
1450         check(
1451             "2015-02-04T14:37:05-09:00",
1452             &[
1453                 num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"),
1454                 num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second),
1455                 fixed(Fixed::TimezoneOffset),
1456             ],
1457             parsed!(
1458                 year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
1459                 second: 5, offset: -32400
1460             ),
1461         );
1462         check(
1463             "2015-02-04T14:37:05−09:00", // timezone offset using MINUS SIGN (U+2212)
1464             &[
1465                 num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"),
1466                 num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second),
1467                 fixed(Fixed::TimezoneOffset)
1468             ],
1469             parsed!(
1470                 year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
1471                 second: 5, offset: -32400
1472             ),
1473         );
1474         check(
1475             "20150204143705567",
1476             &[
1477                 num(Year), num(Month), num(Day), num(Hour), num(Minute), num(Second),
1478                 internal_fixed(Nanosecond3NoDot)
1479             ],
1480             parsed!(
1481                 year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
1482                 second: 5, nanosecond: 567000000
1483             ),
1484         );
1485         check(
1486             "Mon, 10 Jun 2013 09:32:37 GMT",
1487             &[
1488                 fixed(Fixed::ShortWeekdayName), Literal(","), Space(" "), num(Day), Space(" "),
1489                 fixed(Fixed::ShortMonthName), Space(" "), num(Year), Space(" "), num(Hour),
1490                 Literal(":"), num(Minute), Literal(":"), num(Second), Space(" "), Literal("GMT")
1491             ],
1492             parsed!(
1493                 year: 2013, month: 6, day: 10, weekday: Weekday::Mon,
1494                 hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37
1495             ),
1496         );
1497         check(
1498             "��Mon, 10 Jun��2013 09:32:37  GMT��",
1499             &[
1500                 Literal("��"), fixed(Fixed::ShortWeekdayName), Literal(","), Space(" "), num(Day),
1501                 Space(" "), fixed(Fixed::ShortMonthName), Literal("��"), num(Year), Space(" "),
1502                 num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), Space("  "),
1503                 Literal("GMT"), Literal("��")
1504             ],
1505             parsed!(
1506                 year: 2013, month: 6, day: 10, weekday: Weekday::Mon,
1507                 hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37
1508             ),
1509         );
1510         check(
1511             "Sun Aug 02 13:39:15 CEST 2020",
1512             &[
1513                 fixed(Fixed::ShortWeekdayName), Space(" "), fixed(Fixed::ShortMonthName),
1514                 Space(" "), num(Day), Space(" "), num(Hour), Literal(":"), num(Minute),
1515                 Literal(":"), num(Second), Space(" "), fixed(Fixed::TimezoneName), Space(" "),
1516                 num(Year)
1517             ],
1518             parsed!(
1519                 year: 2020, month: 8, day: 2, weekday: Weekday::Sun,
1520                 hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15
1521             ),
1522         );
1523         check(
1524             "20060102150405",
1525             &[num(Year), num(Month), num(Day), num(Hour), num(Minute), num(Second)],
1526             parsed!(
1527                 year: 2006, month: 1, day: 2, hour_div_12: 1, hour_mod_12: 3, minute: 4, second: 5
1528             ),
1529         );
1530         check(
1531             "3:14PM",
1532             &[num(Hour12), Literal(":"), num(Minute), fixed(Fixed::LowerAmPm)],
1533             parsed!(hour_div_12: 1, hour_mod_12: 3, minute: 14),
1534         );
1535         check(
1536             "12345678901234.56789",
1537             &[num(Timestamp), Literal("."), num(Nanosecond)],
1538             parsed!(nanosecond: 56_789, timestamp: 12_345_678_901_234),
1539         );
1540         check(
1541             "12345678901234.56789",
1542             &[num(Timestamp), fixed(Fixed::Nanosecond)],
1543             parsed!(nanosecond: 567_890_000, timestamp: 12_345_678_901_234),
1544         );
1545 
1546         // docstring examples from `impl str::FromStr`
1547         check(
1548             "2000-01-02T03:04:05Z",
1549             &[
1550                 num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"),
1551                 num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second),
1552                 internal_fixed(TimezoneOffsetPermissive)
1553             ],
1554             parsed!(
1555                 year: 2000, month: 1, day: 2, hour_div_12: 0, hour_mod_12: 3, minute: 4, second: 5,
1556                 offset: 0
1557             ),
1558         );
1559         check(
1560             "2000-01-02 03:04:05Z",
1561             &[
1562                 num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Space(" "),
1563                 num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second),
1564                 internal_fixed(TimezoneOffsetPermissive)
1565             ],
1566             parsed!(
1567                 year: 2000, month: 1, day: 2, hour_div_12: 0, hour_mod_12: 3, minute: 4, second: 5,
1568                 offset: 0
1569             ),
1570         );
1571     }
1572 
1573     #[track_caller]
parses(s: &str, items: &[Item])1574     fn parses(s: &str, items: &[Item]) {
1575         let mut parsed = Parsed::new();
1576         assert!(parse(&mut parsed, s, items.iter()).is_ok());
1577     }
1578 
1579     #[track_caller]
check(s: &str, items: &[Item], expected: ParseResult<Parsed>)1580     fn check(s: &str, items: &[Item], expected: ParseResult<Parsed>) {
1581         let mut parsed = Parsed::new();
1582         let result = parse(&mut parsed, s, items.iter());
1583         let parsed = result.map(|_| parsed);
1584         assert_eq!(parsed, expected);
1585     }
1586 
1587     #[test]
test_rfc2822()1588     fn test_rfc2822() {
1589         let ymd_hmsn = |y, m, d, h, n, s, nano, off| {
1590             FixedOffset::east_opt(off * 60 * 60)
1591                 .unwrap()
1592                 .with_ymd_and_hms(y, m, d, h, n, s)
1593                 .unwrap()
1594                 .with_nanosecond(nano)
1595                 .unwrap()
1596         };
1597 
1598         // Test data - (input, Ok(expected result) or Err(error code))
1599         let testdates = [
1600             ("Tue, 20 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // normal case
1601             ("Fri,  2 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 2, 17, 35, 20, 0, -8))), // folding whitespace
1602             ("Fri, 02 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 2, 17, 35, 20, 0, -8))), // leading zero
1603             ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC)", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // trailing comment
1604             (
1605                 r"Tue, 20 Jan 2015 17:35:20 -0800 ( (UTC ) (\( (a)\(( \t ) ) \\( \) ))",
1606                 Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)),
1607             ), // complex trailing comment
1608             (r"Tue, 20 Jan 2015 17:35:20 -0800 (UTC\)", Err(TOO_LONG)), // incorrect comment, not enough closing parentheses
1609             (
1610                 "Tue, 20 Jan 2015 17:35:20 -0800 (UTC)\t \r\n(Anothercomment)",
1611                 Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)),
1612             ), // multiple comments
1613             ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC) ", Err(TOO_LONG)), // trailing whitespace after comment
1614             ("20 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // no day of week
1615             ("20 JAN 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // upper case month
1616             ("Tue, 20 Jan 2015 17:35 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 0, 0, -8))), // no second
1617             ("11 Sep 2001 09:45:00 +0000", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, 0))),
1618             ("11 Sep 2001 09:45:00 EST", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, -5))),
1619             ("11 Sep 2001 09:45:00 GMT", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, 0))),
1620             ("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month
1621             ("Tue, 20 Jan 2015", Err(TOO_SHORT)),              // omitted fields
1622             ("Tue, 20 Avr 2015 17:35:20 -0800", Err(INVALID)), // bad month name
1623             ("Tue, 20 Jan 2015 25:35:20 -0800", Err(OUT_OF_RANGE)), // bad hour
1624             ("Tue, 20 Jan 2015 7:35:20 -0800", Err(INVALID)),  // bad # of digits in hour
1625             ("Tue, 20 Jan 2015 17:65:20 -0800", Err(OUT_OF_RANGE)), // bad minute
1626             ("Tue, 20 Jan 2015 17:35:90 -0800", Err(OUT_OF_RANGE)), // bad second
1627             ("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset
1628             ("6 Jun 1944 04:00:00Z", Err(INVALID)),            // bad offset (zulu not allowed)
1629             // named timezones that have specific timezone offsets
1630             // see https://www.rfc-editor.org/rfc/rfc2822#section-4.3
1631             ("Tue, 20 Jan 2015 17:35:20 GMT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1632             ("Tue, 20 Jan 2015 17:35:20 UT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1633             ("Tue, 20 Jan 2015 17:35:20 ut", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1634             ("Tue, 20 Jan 2015 17:35:20 EDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -4))),
1635             ("Tue, 20 Jan 2015 17:35:20 EST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -5))),
1636             ("Tue, 20 Jan 2015 17:35:20 CDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -5))),
1637             ("Tue, 20 Jan 2015 17:35:20 CST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -6))),
1638             ("Tue, 20 Jan 2015 17:35:20 MDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -6))),
1639             ("Tue, 20 Jan 2015 17:35:20 MST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -7))),
1640             ("Tue, 20 Jan 2015 17:35:20 PDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -7))),
1641             ("Tue, 20 Jan 2015 17:35:20 PST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))),
1642             ("Tue, 20 Jan 2015 17:35:20 pst", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))),
1643             // named single-letter military timezones must fallback to +0000
1644             ("Tue, 20 Jan 2015 17:35:20 Z", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1645             ("Tue, 20 Jan 2015 17:35:20 A", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1646             ("Tue, 20 Jan 2015 17:35:20 a", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1647             ("Tue, 20 Jan 2015 17:35:20 K", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1648             ("Tue, 20 Jan 2015 17:35:20 k", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))),
1649             // named single-letter timezone "J" is specifically not valid
1650             ("Tue, 20 Jan 2015 17:35:20 J", Err(INVALID)),
1651             ("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset minutes
1652             ("Tue, 20 Jan 2015 17:35:20Z", Err(INVALID)),           // bad offset: zulu not allowed
1653             ("Tue, 20 Jan 2015 17:35:20 Zulu", Err(INVALID)),       // bad offset: zulu not allowed
1654             ("Tue, 20 Jan 2015 17:35:20 ZULU", Err(INVALID)),       // bad offset: zulu not allowed
1655             ("Tue, 20 Jan 2015 17:35:20 −0800", Err(INVALID)), // bad offset: timezone offset using MINUS SIGN (U+2212), not specified for RFC 2822
1656             ("Tue, 20 Jan 2015 17:35:20 0800", Err(INVALID)),  // missing offset sign
1657             ("Tue, 20 Jan 2015 17:35:20 HAS", Err(INVALID)),   // bad named timezone
1658             ("Tue, 20 Jan 2015��17:35:20 -0800", Err(INVALID)), // bad character!
1659         ];
1660 
1661         fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
1662             let mut parsed = Parsed::new();
1663             parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?;
1664             parsed.to_datetime()
1665         }
1666 
1667         // Test against test data above
1668         for &(date, checkdate) in testdates.iter() {
1669             #[cfg(feature = "std")]
1670             eprintln!("Test input: {:?}\n    Expect: {:?}", date, checkdate);
1671             let dt = rfc2822_to_datetime(date); // parse a date
1672             if dt != checkdate {
1673                 // check for expected result
1674                 panic!(
1675                     "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
1676                     date, dt, checkdate
1677                 );
1678             }
1679         }
1680     }
1681 
1682     #[test]
parse_rfc850()1683     fn parse_rfc850() {
1684         static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT";
1685 
1686         let dt = Utc.with_ymd_and_hms(1994, 11, 6, 8, 49, 37).unwrap();
1687 
1688         // Check that the format is what we expect
1689         #[cfg(feature = "alloc")]
1690         assert_eq!(dt.format(RFC850_FMT).to_string(), "Sunday, 06-Nov-94 08:49:37 GMT");
1691 
1692         // Check that it parses correctly
1693         assert_eq!(
1694             NaiveDateTime::parse_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT),
1695             Ok(dt.naive_utc())
1696         );
1697 
1698         // Check that the rest of the weekdays parse correctly (this test originally failed because
1699         // Sunday parsed incorrectly).
1700         let testdates = [
1701             (
1702                 Utc.with_ymd_and_hms(1994, 11, 7, 8, 49, 37).unwrap(),
1703                 "Monday, 07-Nov-94 08:49:37 GMT",
1704             ),
1705             (
1706                 Utc.with_ymd_and_hms(1994, 11, 8, 8, 49, 37).unwrap(),
1707                 "Tuesday, 08-Nov-94 08:49:37 GMT",
1708             ),
1709             (
1710                 Utc.with_ymd_and_hms(1994, 11, 9, 8, 49, 37).unwrap(),
1711                 "Wednesday, 09-Nov-94 08:49:37 GMT",
1712             ),
1713             (
1714                 Utc.with_ymd_and_hms(1994, 11, 10, 8, 49, 37).unwrap(),
1715                 "Thursday, 10-Nov-94 08:49:37 GMT",
1716             ),
1717             (
1718                 Utc.with_ymd_and_hms(1994, 11, 11, 8, 49, 37).unwrap(),
1719                 "Friday, 11-Nov-94 08:49:37 GMT",
1720             ),
1721             (
1722                 Utc.with_ymd_and_hms(1994, 11, 12, 8, 49, 37).unwrap(),
1723                 "Saturday, 12-Nov-94 08:49:37 GMT",
1724             ),
1725         ];
1726 
1727         for val in &testdates {
1728             assert_eq!(NaiveDateTime::parse_from_str(val.1, RFC850_FMT), Ok(val.0.naive_utc()));
1729         }
1730 
1731         let test_dates_fail = [
1732             "Saturday, 12-Nov-94 08:49:37",
1733             "Saturday, 12-Nov-94 08:49:37 Z",
1734             "Saturday, 12-Nov-94 08:49:37 GMTTTT",
1735             "Saturday, 12-Nov-94 08:49:37 gmt",
1736             "Saturday, 12-Nov-94 08:49:37 +08:00",
1737             "Caturday, 12-Nov-94 08:49:37 GMT",
1738             "Saturday, 99-Nov-94 08:49:37 GMT",
1739             "Saturday, 12-Nov-2000 08:49:37 GMT",
1740             "Saturday, 12-Mop-94 08:49:37 GMT",
1741             "Saturday, 12-Nov-94 28:49:37 GMT",
1742             "Saturday, 12-Nov-94 08:99:37 GMT",
1743             "Saturday, 12-Nov-94 08:49:99 GMT",
1744         ];
1745 
1746         for val in &test_dates_fail {
1747             assert!(NaiveDateTime::parse_from_str(val, RFC850_FMT).is_err());
1748         }
1749     }
1750 
1751     #[test]
test_rfc3339()1752     fn test_rfc3339() {
1753         let ymd_hmsn = |y, m, d, h, n, s, nano, off| {
1754             FixedOffset::east_opt(off * 60 * 60)
1755                 .unwrap()
1756                 .with_ymd_and_hms(y, m, d, h, n, s)
1757                 .unwrap()
1758                 .with_nanosecond(nano)
1759                 .unwrap()
1760         };
1761 
1762         // Test data - (input, Ok(expected result) or Err(error code))
1763         let testdates = [
1764             ("2015-01-20T17:35:20-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // normal case
1765             ("2015-01-20T17:35:20−08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // normal case with MINUS SIGN (U+2212)
1766             ("1944-06-06T04:04:00Z", Ok(ymd_hmsn(1944, 6, 6, 4, 4, 0, 0, 0))),           // D-day
1767             ("2001-09-11T09:45:00-08:00", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, -8))),
1768             ("2015-01-20T17:35:20.001-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 1_000_000, -8))),
1769             ("2015-01-20T17:35:20.001−08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 1_000_000, -8))), // with MINUS SIGN (U+2212)
1770             ("2015-01-20T17:35:20.000031-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 31_000, -8))),
1771             ("2015-01-20T17:35:20.000000004-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 4, -8))),
1772             ("2015-01-20T17:35:20.000000004−08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 4, -8))), // with MINUS SIGN (U+2212)
1773             (
1774                 "2015-01-20T17:35:20.000000000452-08:00",
1775                 Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)),
1776             ), // too small
1777             (
1778                 "2015-01-20T17:35:20.000000000452−08:00",
1779                 Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)),
1780             ), // too small with MINUS SIGN (U+2212)
1781             ("2015-01-20 17:35:20-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // without 'T'
1782             ("2015/01/20T17:35:20.001-08:00", Err(INVALID)), // wrong separator char YMD
1783             ("2015-01-20T17-35-20.001-08:00", Err(INVALID)), // wrong separator char HMS
1784             ("-01-20T17:35:20-08:00", Err(INVALID)),         // missing year
1785             ("99-01-20T17:35:20-08:00", Err(INVALID)),       // bad year format
1786             ("99999-01-20T17:35:20-08:00", Err(INVALID)),    // bad year value
1787             ("-2000-01-20T17:35:20-08:00", Err(INVALID)),    // bad year value
1788             ("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month value
1789             ("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour value
1790             ("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute value
1791             ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second value
1792             ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset value
1793             ("15-01-20T17:35:20-08:00", Err(INVALID)),       // bad year format
1794             ("15-01-20T17:35:20-08:00:00", Err(INVALID)),    // bad year format, bad offset format
1795             ("2015-01-20T17:35:2008:00", Err(INVALID)),      // missing offset sign
1796             ("2015-01-20T17:35:20 08:00", Err(INVALID)),     // missing offset sign
1797             ("2015-01-20T17:35:20Zulu", Err(TOO_LONG)),      // bad offset format
1798             ("2015-01-20T17:35:20 Zulu", Err(INVALID)),      // bad offset format
1799             ("2015-01-20T17:35:20GMT", Err(INVALID)),        // bad offset format
1800             ("2015-01-20T17:35:20 GMT", Err(INVALID)),       // bad offset format
1801             ("2015-01-20T17:35:20+GMT", Err(INVALID)),       // bad offset format
1802             ("2015-01-20T17:35:20++08:00", Err(INVALID)),    // bad offset format
1803             ("2015-01-20T17:35:20--08:00", Err(INVALID)),    // bad offset format
1804             ("2015-01-20T17:35:20−−08:00", Err(INVALID)), // bad offset format with MINUS SIGN (U+2212)
1805             ("2015-01-20T17:35:20±08:00", Err(INVALID)),  // bad offset sign
1806             ("2015-01-20T17:35:20-08-00", Err(INVALID)),  // bad offset separator
1807             ("2015-01-20T17:35:20-08;00", Err(INVALID)),  // bad offset separator
1808             ("2015-01-20T17:35:20-0800", Err(INVALID)),   // bad offset separator
1809             ("2015-01-20T17:35:20-08:0", Err(TOO_SHORT)), // bad offset minutes
1810             ("2015-01-20T17:35:20-08:AA", Err(INVALID)),  // bad offset minutes
1811             ("2015-01-20T17:35:20-08:ZZ", Err(INVALID)),  // bad offset minutes
1812             ("2015-01-20T17:35:20.001-08 : 00", Err(INVALID)), // bad offset separator
1813             ("2015-01-20T17:35:20-08:00:00", Err(TOO_LONG)), // bad offset format
1814             ("2015-01-20T17:35:20+08:", Err(TOO_SHORT)),  // bad offset format
1815             ("2015-01-20T17:35:20-08:", Err(TOO_SHORT)),  // bad offset format
1816             ("2015-01-20T17:35:20−08:", Err(TOO_SHORT)), // bad offset format with MINUS SIGN (U+2212)
1817             ("2015-01-20T17:35:20-08", Err(TOO_SHORT)),  // bad offset format
1818             ("2015-01-20T", Err(TOO_SHORT)),             // missing HMS
1819             ("2015-01-20T00:00:1", Err(TOO_SHORT)),      // missing complete S
1820             ("2015-01-20T00:00:1-08:00", Err(INVALID)),  // missing complete S
1821         ];
1822 
1823         // Test against test data above
1824         for &(date, checkdate) in testdates.iter() {
1825             let dt = DateTime::<FixedOffset>::parse_from_rfc3339(date);
1826             if dt != checkdate {
1827                 // check for expected result
1828                 panic!(
1829                     "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
1830                     date, dt, checkdate
1831                 );
1832             }
1833         }
1834     }
1835 
1836     #[test]
test_issue_1010()1837     fn test_issue_1010() {
1838         let dt = crate::NaiveDateTime::parse_from_str("\u{c}SUN\u{e}\u{3000}\0m@J\u{3000}\0\u{3000}\0m\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}\0SU\u{c}\u{c}",
1839         "\u{c}\u{c}%A\u{c}\u{b}\0SUN\u{c}\u{c}\u{c}SUNN\u{c}\u{c}\u{c}SUN\u{c}\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}%a");
1840         assert_eq!(dt, Err(ParseError(ParseErrorKind::Invalid)));
1841     }
1842 }
1843