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