Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/chrono/src/format/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 95 kB image not shown  

Quelle  parse.rs   Sprache: Rust

 

// This is a part of Chrono.
// Portions copyright (c) 2015, John Nagle.
// See README.md and LICENSE.txt for details.

//! Date and time parsing routines.

use core::borrow::Borrow;
use core::str;

use super::scan;
use super::{BAD_FORMAT, INVALID, OUT_OF_RANGE, TOO_LONG, TOO_SHORT};
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed};
use super::{ParseError, ParseResult};
use crate::{DateTime, FixedOffset, Weekday};

fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
    p.set_weekday(match v {
        0 => Weekday::Sun,
        1 => Weekday::Mon,
        2 => Weekday::Tue,
        3 => Weekday::Wed,
        4 => Weekday::Thu,
        5 => Weekday::Fri,
        6 => Weekday::Sat,
        _ => return Err(OUT_OF_RANGE),
    })
}

fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()> {
    p.set_weekday(match v {
        1 => Weekday::Mon,
        2 => Weekday::Tue,
        3 => Weekday::Wed,
        4 => Weekday::Thu,
        5 => Weekday::Fri,
        6 => Weekday::Sat,
        7 => Weekday::Sun,
        _ => return Err(OUT_OF_RANGE),
    })
}

fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
    macro_rules! try_consume {
        ($e:expr) => {{
            let (s_, v) = $e?;
            s = s_;
            v
        }};
    }

    // an adapted RFC 2822 syntax from Section 3.3 and 4.3:
    //
    // c-char      = <any char except '(', ')' and '\\'>
    // c-escape    = "\" <any char>
    // comment     = "(" *(comment / c-char / c-escape) ")" *S
    // date-time   = [ day-of-week "," ] date 1*S time *S *comment
    // day-of-week = *S day-name *S
    // day-name    = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
    // date        = day month year
    // day         = *S 1*2DIGIT *S
    // month       = 1*S month-name 1*S
    // month-name  = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
    //               "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
    // year        = *S 2*DIGIT *S
    // time        = time-of-day 1*S zone
    // time-of-day = hour ":" minute [ ":" second ]
    // hour        = *S 2DIGIT *S
    // minute      = *S 2DIGIT *S
    // second      = *S 2DIGIT *S
    // zone        = ( "+" / "-" ) 4DIGIT /
    //               "UT" / "GMT" /                  ; same as +0000
    //               "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800
    //               "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700
    //               1*(%d65-90 / %d97-122)          ; same as -0000
    //
    // some notes:
    //
    // - quoted characters can be in any mixture of lower and upper cases.
    //
    // - we do not recognize a folding white space (FWS) or comment (CFWS).
    //   for our purposes, instead, we accept any sequence of Unicode
    //   white space characters (denoted here to `S`). For comments, we accept
    //   any text within parentheses while respecting escaped parentheses.
    //   Any actual RFC 2822 parser is expected to parse FWS and/or CFWS themselves
    //   and replace it with a single SP (`%x20`); this is legitimate.
    //
    // - two-digit year < 50 should be interpreted by adding 2000.
    //   two-digit year >= 50 or three-digit year should be interpreted
    //   by adding 1900. note that four-or-more-digit years less than 1000
    //   are *never* affected by this rule.
    //
    // - mismatching day-of-week is always an error, which is consistent to
    //   Chrono's own rules.
    //
    // - zones can range from `-9959` to `+9959`, but `FixedOffset` does not
    //   support offsets larger than 24 hours. this is not *that* problematic
    //   since we do not directly go to a `DateTime` so one can recover
    //   the offset information from `Parsed` anyway.

    s = s.trim_start();

    if let Ok((s_, weekday)) = scan::short_weekday(s) {
        if !s_.starts_with(',') {
            return Err(INVALID);
        }
        s = &s_[1..];
        parsed.set_weekday(weekday)?;
    }

    s = s.trim_start();
    parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
    s = scan::space(s)?; // mandatory
    parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
    s = scan::space(s)?; // mandatory

    // distinguish two- and three-digit years from four-digit years
    let prevlen = s.len();
    let mut year = try_consume!(scan::number(s, 2, usize::MAX));
    let yearlen = prevlen - s.len();
    match (yearlen, year) {
        (2, 0..=49) => {
            year += 2000;
        } //   47 -> 2047,   05 -> 2005
        (2, 50..=99) => {
            year += 1900;
        } //   79 -> 1979
        (3, _) => {
            year += 1900;
        } //  112 -> 2012,  009 -> 1909
        (_, _) => {} // 1987 -> 1987, 0654 -> 0654
    }
    parsed.set_year(year)?;

    s = scan::space(s)?; // mandatory
    parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
    s = scan::char(s.trim_start(), b':')?.trim_start(); // *S ":" *S
    parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
    if let Ok(s_) = scan::char(s.trim_start(), b':') {
        // [ ":" *S 2DIGIT ]
        parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?;
    }

    s = scan::space(s)?; // mandatory
    parsed.set_offset(i64::from(try_consume!(scan::timezone_offset_2822(s))))?;

    // optional comments
    while let Ok((s_out, ())) = scan::comment_2822(s) {
        s = s_out;
    }

    Ok((s, ()))
}

pub(crate) fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(& style='color:blue'>'a str, ())> {
    macro_rules! try_consume {
        ($e:expr) => {{
            let (s_, v) = $e?;
            s = s_;
            v
        }};
    }

    // an adapted RFC 3339 syntax from Section 5.6:
    //
    // date-fullyear  = 4DIGIT
    // date-month     = 2DIGIT ; 01-12
    // date-mday      = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
    // time-hour      = 2DIGIT ; 00-23
    // time-minute    = 2DIGIT ; 00-59
    // time-second    = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
    // time-secfrac   = "." 1*DIGIT
    // time-numoffset = ("+" / "-") time-hour ":" time-minute
    // time-offset    = "Z" / time-numoffset
    // partial-time   = time-hour ":" time-minute ":" time-second [time-secfrac]
    // full-date      = date-fullyear "-" date-month "-" date-mday
    // full-time      = partial-time time-offset
    // date-time      = full-date "T" full-time
    //
    // some notes:
    //
    // - quoted characters can be in any mixture of lower and upper cases.
    //
    // - it may accept any number of fractional digits for seconds.
    //   for Chrono, this means that we should skip digits past first 9 digits.
    //
    // - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59.
    //   note that this restriction is unique to RFC 3339 and not ISO 8601.
    //   since this is not a typical Chrono behavior, we check it earlier.
    //
    // - For readability a full-date and a full-time may be separated by a space character.

    parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
    s = scan::char(s, b'-')?;
    parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
    s = scan::char(s, b'-')?;
    parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;

    s = match s.as_bytes().first() {
        Some(&b't' | &b'T' | &b' ') => &s[1..],
        Some(_) => return Err(INVALID),
        None => return Err(TOO_SHORT),
    };

    parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
    s = scan::char(s, b':')?;
    parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
    s = scan::char(s, b':')?;
    parsed.set_second(try_consume!(scan::number(s, 2, 2)))?;
    if s.starts_with('.') {
        let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
        parsed.set_nanosecond(nanosecond)?;
    }

    let offset = try_consume!(scan::timezone_offset(s, |s| scan::char(s, b':'), true, false, true));
    // This range check is similar to the one in `FixedOffset::east_opt`, so it would be redundant.
    // But it is possible to read the offset directly from `Parsed`. We want to only successfully
    // populate `Parsed` if the input is fully valid RFC 3339.
    // Max for the hours field is `23`, and for the minutes field `59`.
    const MAX_RFC3339_OFFSET: i32 = (23 * 60 + 59) * 60;
    if !(-MAX_RFC3339_OFFSET..=MAX_RFC3339_OFFSET).contains(&offset) {
        return Err(OUT_OF_RANGE);
    }
    parsed.set_offset(i64::from(offset))?;

    Ok((s, ()))
}

/// Tries to parse given string into `parsed` with given formatting items.
/// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used).
/// There should be no trailing string after parsing;
/// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces.
///
/// This particular date and time parser is:
///
/// - Greedy. It will consume the longest possible prefix.
///   For example, `April` is always consumed entirely when the long month name is requested;
///   it equally accepts `Apr`, but prefers the longer prefix in this case.
///
/// - Padding-agnostic (for numeric items).
///   The [`Pad`](./enum.Pad.html) field is completely ignored,
///   so one can prepend any number of whitespace then any number of zeroes before numbers.
///
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()>
where
    I: Iterator<Item = B>,
    B: Borrow<Item<'a>>,
{
    match parse_internal(parsed, s, items) {
        Ok("") => Ok(()),
        Ok(_) => Err(TOO_LONG), // if there are trailing chars it is an error
        Err(e) => Err(e),
    }
}

/// Tries to parse given string into `parsed` with given formatting items.
/// Returns `Ok` with a slice of the unparsed remainder.
///
/// This particular date and time parser is:
///
/// - Greedy. It will consume the longest possible prefix.
///   For example, `April` is always consumed entirely when the long month name is requested;
///   it equally accepts `Apr`, but prefers the longer prefix in this case.
///
/// - Padding-agnostic (for numeric items).
///   The [`Pad`](./enum.Pad.html) field is completely ignored,
///   so one can prepend any number of zeroes before numbers.
///
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
pub fn 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>>,
{
    parse_internal(parsed, s, items)
}

fn parse_internal<'a, 'b, I, B>(
    parsed: &mut Parsed,
    mut s: &'b str,
    items: I,
) -> Result<&'b str, ParseError>
where
    I: Iterator<Item = B>,
    B: Borrow<Item<'a>>,
{
    macro_rules! try_consume {
        ($e:expr) => {{
            match $e {
                Ok((s_, v)) => {
                    s = s_;
                    v
                }
                Err(e) => return Err(e),
            }
        }};
    }

    for item in items {
        match *item.borrow() {
            Item::Literal(prefix) => {
                if s.len() < prefix.len() {
                    return Err(TOO_SHORT);
                }
                if !s.starts_with(prefix) {
                    return Err(INVALID);
                }
                s = &s[prefix.len()..];
            }

            #[cfg(feature = "alloc")]
            Item::OwnedLiteral(ref prefix) => {
                if s.len() < prefix.len() {
                    return Err(TOO_SHORT);
                }
                if !s.starts_with(&prefix[..]) {
                    return Err(INVALID);
                }
                s = &s[prefix.len()..];
            }

            Item::Space(_) => {
                s = s.trim_start();
            }

            #[cfg(feature = "alloc")]
            Item::OwnedSpace(_) => {
                s = s.trim_start();
            }

            Item::Numeric(ref spec, ref _pad) => {
                use super::Numeric::*;
                type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;

                let (width, signed, set): (usize, bool, Setter) = match *spec {
                    Year => (4, true, Parsed::set_year),
                    YearDiv100 => (2, false, Parsed::set_year_div_100),
                    YearMod100 => (2, false, Parsed::set_year_mod_100),
                    IsoYear => (4, true, Parsed::set_isoyear),
                    IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
                    IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
                    Quarter => (1, false, Parsed::set_quarter),
                    Month => (2, false, Parsed::set_month),
                    Day => (2, false, Parsed::set_day),
                    WeekFromSun => (2, false, Parsed::set_week_from_sun),
                    WeekFromMon => (2, false, Parsed::set_week_from_mon),
                    IsoWeek => (2, false, Parsed::set_isoweek),
                    NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
                    WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
                    Ordinal => (3, false, Parsed::set_ordinal),
                    Hour => (2, false, Parsed::set_hour),
                    Hour12 => (2, false, Parsed::set_hour12),
                    Minute => (2, false, Parsed::set_minute),
                    Second => (2, false, Parsed::set_second),
                    Nanosecond => (9, false, Parsed::set_nanosecond),
                    Timestamp => (usize::MAX, false, Parsed::set_timestamp),

                    // for the future expansion
                    Internal(ref int) => match int._dummy {},
                };

                s = s.trim_start();
                let v = if signed {
                    if s.starts_with('-') {
                        let v = try_consume!(scan::number(&s[1..], 1, usize::MAX));
                        0i64.checked_sub(v).ok_or(OUT_OF_RANGE)?
                    } else if s.starts_with('+') {
                        try_consume!(scan::number(&s[1..], 1, usize::MAX))
                    } else {
                        // if there is no explicit sign, we respect the original `width`
                        try_consume!(scan::number(s, 1, width))
                    }
                } else {
                    try_consume!(scan::number(s, 1, width))
                };
                set(parsed, v)?;
            }

            Item::Fixed(ref spec) => {
                use super::Fixed::*;

                match spec {
                    &ShortMonthName => {
                        let month0 = try_consume!(scan::short_month0(s));
                        parsed.set_month(i64::from(month0) + 1)?;
                    }

                    &LongMonthName => {
                        let month0 = try_consume!(scan::short_or_long_month0(s));
                        parsed.set_month(i64::from(month0) + 1)?;
                    }

                    &ShortWeekdayName => {
                        let weekday = try_consume!(scan::short_weekday(s));
                        parsed.set_weekday(weekday)?;
                    }

                    &LongWeekdayName => {
                        let weekday = try_consume!(scan::short_or_long_weekday(s));
                        parsed.set_weekday(weekday)?;
                    }

                    &LowerAmPm | &UpperAmPm => {
                        if s.len() < 2 {
                            return Err(TOO_SHORT);
                        }
                        let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
                            (b'a', b'm') => false,
                            (b'p', b'm') => true,
                            _ => return Err(INVALID),
                        };
                        parsed.set_ampm(ampm)?;
                        s = &s[2..];
                    }

                    &Nanosecond => {
                        if s.starts_with('.') {
                            let nano = try_consume!(scan::nanosecond(&s[1..]));
                            parsed.set_nanosecond(nano)?;
                        }
                    }

                    &Nanosecond3 => {
                        if s.starts_with('.') {
                            let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 3));
                            parsed.set_nanosecond(nano)?;
                        }
                    }

                    &Nanosecond6 => {
                        if s.starts_with('.') {
                            let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 6));
                            parsed.set_nanosecond(nano)?;
                        }
                    }

                    &Nanosecond9 => {
                        if s.starts_with('.') {
                            let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 9));
                            parsed.set_nanosecond(nano)?;
                        }
                    }

                    &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
                        if s.len() < 3 {
                            return Err(TOO_SHORT);
                        }
                        let nano = try_consume!(scan::nanosecond_fixed(s, 3));
                        parsed.set_nanosecond(nano)?;
                    }

                    &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
                        if s.len() < 6 {
                            return Err(TOO_SHORT);
                        }
                        let nano = try_consume!(scan::nanosecond_fixed(s, 6));
                        parsed.set_nanosecond(nano)?;
                    }

                    &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
                        if s.len() < 9 {
                            return Err(TOO_SHORT);
                        }
                        let nano = try_consume!(scan::nanosecond_fixed(s, 9));
                        parsed.set_nanosecond(nano)?;
                    }

                    &TimezoneName => {
                        try_consume!(Ok((s.trim_start_matches(|c: char| !c.is_whitespace()), ())));
                    }

                    &TimezoneOffsetColon
                    | &TimezoneOffsetDoubleColon
                    | &TimezoneOffsetTripleColon
                    | &TimezoneOffset => {
                        let offset = try_consume!(scan::timezone_offset(
                            s.trim_start(),
                            scan::colon_or_space,
                            false,
                            false,
                            true,
                        ));
                        parsed.set_offset(i64::from(offset))?;
                    }

                    &TimezoneOffsetColonZ | &TimezoneOffsetZ => {
                        let offset = try_consume!(scan::timezone_offset(
                            s.trim_start(),
                            scan::colon_or_space,
                            true,
                            false,
                            true,
                        ));
                        parsed.set_offset(i64::from(offset))?;
                    }
                    &Internal(InternalFixed {
                        val: InternalInternal::TimezoneOffsetPermissive,
                    }) => {
                        let offset = try_consume!(scan::timezone_offset(
                            s.trim_start(),
                            scan::colon_or_space,
                            true,
                            true,
                            true,
                        ));
                        parsed.set_offset(i64::from(offset))?;
                    }

                    &RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
                    &RFC3339 => {
                        // Used for the `%+` specifier, which has the description:
                        // "Same as `%Y-%m-%dT%H:%M:%S%.f%:z` (...)
                        // This format also supports having a `Z` or `UTC` in place of `%:z`."
                        // Use the relaxed parser to match this description.
                        try_consume!(parse_rfc3339_relaxed(parsed, s))
                    }
                }
            }

            Item::Error => {
                return Err(BAD_FORMAT);
            }
        }
    }
    Ok(s)
}

/// Accepts a relaxed form of RFC3339.
/// A space or a 'T' are accepted as the separator between the date and time
/// parts. Additional spaces are allowed between each component.
///
/// All of these examples are equivalent:
/// ```
/// # use chrono::{DateTime, offset::FixedOffset};
/// "2012-12-12T12:12:12Z".parse::<DateTime<FixedOffset>>()?;
/// "2012-12-12 12:12:12Z".parse::<DateTime<FixedOffset>>()?;
/// "2012-  12-12T12:  12:12Z".parse::<DateTime<FixedOffset>>()?;
/// # Ok::<(), chrono::ParseError>(())
/// ```
impl str::FromStr for DateTime<FixedOffset> {
    type Err = ParseError;

    fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
        let mut parsed = Parsed::new();
        let (s, _) = parse_rfc3339_relaxed(&mut parsed, s)?;
        if !s.trim_start().is_empty() {
            return Err(TOO_LONG);
        }
        parsed.to_datetime()
    }
}

/// Accepts a relaxed form of RFC3339.
///
/// Differences with RFC3339:
/// - Values don't require padding to two digits.
/// - Years outside the range 0...=9999 are accepted, but they must include a sign.
/// - `UTC` is accepted as a valid timezone name/offset (for compatibility with the debug format of
///   `DateTime<Utc>`.
/// - There can be spaces between any of the components.
/// - The colon in the offset may be missing.
fn parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&tyle='color:blue'>'a str, ())> {
    const DATE_ITEMS: &[Item<'static>] = &[
        Item::Numeric(Numeric::Year, Pad::Zero),
        Item::Space(""),
        Item::Literal("-"),
        Item::Numeric(Numeric::Month, Pad::Zero),
        Item::Space(""),
        Item::Literal("-"),
        Item::Numeric(Numeric::Day, Pad::Zero),
    ];
    const TIME_ITEMS: &[Item<'static>] = &[
        Item::Numeric(Numeric::Hour, Pad::Zero),
        Item::Space(""),
        Item::Literal(":"),
        Item::Numeric(Numeric::Minute, Pad::Zero),
        Item::Space(""),
        Item::Literal(":"),
        Item::Numeric(Numeric::Second, Pad::Zero),
        Item::Fixed(Fixed::Nanosecond),
        Item::Space(""),
    ];

    s = parse_internal(parsed, s, DATE_ITEMS.iter())?;

    s = match s.as_bytes().first() {
        Some(&b't' | &b'T' | &b' ') => &s[1..],
        Some(_) => return Err(INVALID),
        None => return Err(TOO_SHORT),
    };

    s = parse_internal(parsed, s, TIME_ITEMS.iter())?;
    s = s.trim_start();
    let (s, offset) = if s.len() >= 3 && "UTC".as_bytes().eq_ignore_ascii_case(&s.as_bytes()[..3]) {
        (&s[3..], 0)
    } else {
        scan::timezone_offset(s, scan::colon_or_space, true, false, true)?
    };
    parsed.set_offset(i64::from(offset))?;
    Ok((s, ()))
}

#[cfg(test)]
mod tests {
    use crate::format::*;
    use crate::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Timelike, Utc};

    macro_rules! parsed {
        ($($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
            let mut expected = Parsed::new();
            $(expected.$k = Some($v);)*
            Ok(expected)
        });
    }

    #[test]
    fn test_parse_whitespace_and_literal() {
        use crate::format::Item::{Literal, Space};

        // empty string
        parses("", &[]);
        check(" ", &[], Err(TOO_LONG));
        check("a", &[], Err(TOO_LONG));
        check("abc", &[], Err(TOO_LONG));
        check("��", &[], Err(TOO_LONG));

        // whitespaces
        parses("", &[Space("")]);
        parses(" ", &[Space(" ")]);
        parses("  ", &[Space("  ")]);
        parses("   ", &[Space("   ")]);
        parses(" ", &[Space("")]);
        parses("  ", &[Space(" ")]);
        parses("   ", &[Space("  ")]);
        parses("    ", &[Space("  ")]);
        parses("", &[Space(" ")]);
        parses(" ", &[Space("  ")]);
        parses("  ", &[Space("   ")]);
        parses("  ", &[Space("  "), Space("  ")]);
        parses("   ", &[Space("  "), Space("  ")]);
        parses("  ", &[Space(" "), Space(" ")]);
        parses("   ", &[Space("  "), Space(" ")]);
        parses("   ", &[Space(" "), Space("  ")]);
        parses("   ", &[Space(" "), Space(" "), Space(" ")]);
        parses("\t", &[Space("")]);
        parses(" \n\r  \n", &[Space("")]);
        parses("\t", &[Space("\t")]);
        parses("\t", &[Space(" ")]);
        parses(" ", &[Space("\t")]);
        parses("\t\r", &[Space("\t\r")]);
        parses("\t\r ", &[Space("\t\r ")]);
        parses("\t \r", &[Space("\t \r")]);
        parses(" \t\r", &[Space(" \t\r")]);
        parses(" \n\r  \n", &[Space(" \n\r  \n")]);
        parses(" \t\n", &[Space(" \t")]);
        parses(" \n\t", &[Space(" \t\n")]);
        parses("\u{2002}", &[Space("\u{2002}")]);
        // most unicode whitespace characters
        parses(
            "\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}",
            &[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}",
            )],
        );
        // most unicode whitespace characters
        parses(
            "\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}",
            &[
                Space("\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}"),
                Space("\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}"),
            ],
        );
        check("a", &[Space("")], Err(TOO_LONG));
        check("a", &[Space(" ")], Err(TOO_LONG));
        // a Space containing a literal does not match a literal
        check("a", &[Space("a")], Err(TOO_LONG));
        check("abc", &[Space("")], Err(TOO_LONG));
        check("abc", &[Space(" ")], Err(TOO_LONG));
        check(" abc", &[Space("")], Err(TOO_LONG));
        check(" abc", &[Space(" ")], Err(TOO_LONG));

        // `\u{0363}` is combining diacritic mark "COMBINING LATIN SMALL LETTER A"

        // literal
        parses("", &[Literal("")]);
        check("", &[Literal("a")], Err(TOO_SHORT));
        check(" ", &[Literal("a")], Err(INVALID));
        parses("a", &[Literal("a")]);
        parses("+", &[Literal("+")]);
        parses("-", &[Literal("-")]);
        parses("−", &[Literal("−")]); // MINUS SIGN (U+2212)
        parses(" ", &[Literal(" ")]); // a Literal may contain whitespace and match whitespace
        check("aa", &[Literal("a")], Err(TOO_LONG));
        check("��", &[Literal("a")], Err(INVALID));
        check("A", &[Literal("a")], Err(INVALID));
        check("a", &[Literal("z")], Err(INVALID));
        check("a", &[Literal("��")], Err(TOO_SHORT));
        check("a", &[Literal("\u{0363}a")], Err(TOO_SHORT));
        check("\u{0363}a", &[Literal("a")], Err(INVALID));
        parses("\u{0363}a", &[Literal("\u{0363}a")]);
        check("a", &[Literal("ab")], Err(TOO_SHORT));
        parses("xy", &[Literal("xy")]);
        parses("xy", &[Literal("x"), Literal("y")]);
        parses("1", &[Literal("1")]);
        parses("1234", &[Literal("1234")]);
        parses("+1234", &[Literal("+1234")]);
        parses("-1234", &[Literal("-1234")]);
        parses("−1234", &[Literal("−1234")]); // MINUS SIGN (U+2212)
        parses("PST", &[Literal("PST")]);
        parses("��", &[Literal("��")]);
        parses("��a", &[Literal("��"), Literal("a")]);
        parses("��a��", &[Literal("��"), Literal("a��")]);
        parses("a��b", &[Literal("a"), Literal("��"), Literal("b")]);
        // literals can be together
        parses("xy", &[Literal("xy")]);
        parses("xyz", &[Literal("xyz")]);
        // or literals can be apart
        parses("xy", &[Literal("x"), Literal("y")]);
        parses("xyz", &[Literal("x"), Literal("yz")]);
        parses("xyz", &[Literal("xy"), Literal("z")]);
        parses("xyz", &[Literal("x"), Literal("y"), Literal("z")]);
        //
        check("x y", &[Literal("x"), Literal("y")], Err(INVALID));
        parses("xy", &[Literal("x"), Space(""), Literal("y")]);
        parses("x y", &[Literal("x"), Space(""), Literal("y")]);
        parses("x y", &[Literal("x"), Space(" "), Literal("y")]);

        // whitespaces + literals
        parses("a\n", &[Literal("a"), Space("\n")]);
        parses("\tab\n", &[Space("\t"), Literal("ab"), Space("\n")]);
        parses(
            "ab\tcd\ne",
            &[Literal("ab"), Space("\t"), Literal("cd"), Space("\n"), Literal("e")],
        );
        parses(
            "+1ab\tcd\r\n+,.",
            &[Literal("+1ab"), Space("\t"), Literal("cd"), Space("\r\n"), Literal("+,.")],
        );
        // whitespace and literals can be intermixed
        parses("a\tb", &[Literal("a\tb")]);
        parses("a\tb", &[Literal("a"), Space("\t"), Literal("b")]);
    }

    #[test]
    fn test_parse_numeric() {
        use crate::format::Item::{Literal, Space};
        use crate::format::Numeric::*;

        // numeric
        check("1987", &[num(Year)], parsed!(year: 1987));
        check("1987 ", &[num(Year)], Err(TOO_LONG));
        check("0x12", &[num(Year)], Err(TOO_LONG)); // `0` is parsed
        check("x123", &[num(Year)], Err(INVALID));
        check("o123", &[num(Year)], Err(INVALID));
        check("2015", &[num(Year)], parsed!(year: 2015));
        check("0000", &[num(Year)], parsed!(year: 0));
        check("9999", &[num(Year)], parsed!(year: 9999));
        check(" \t987", &[num(Year)], parsed!(year: 987));
        check(" \t987", &[Space(" \t"), num(Year)], parsed!(year: 987));
        check(" \t987��", &[Space(" \t"), num(Year), Literal("��")], parsed!(year: 987));
        check("987��", &[num(Year), Literal("��")], parsed!(year: 987));
        check("5", &[num(Year)], parsed!(year: 5));
        check("5\0", &[num(Year)], Err(TOO_LONG));
        check("\x005", &[num(Year)], Err(INVALID));
        check("", &[num(Year)], Err(TOO_SHORT));
        check("12345", &[num(Year), Literal("5")], parsed!(year: 1234));
        check("12345", &[nums(Year), Literal("5")], parsed!(year: 1234));
        check("12345", &[num0(Year), Literal("5")], parsed!(year: 1234));
        check("12341234", &[num(Year), num(Year)], parsed!(year: 1234));
        check("1234 1234", &[num(Year), num(Year)], parsed!(year: 1234));
        check("1234 1234", &[num(Year), Space(" "), num(Year)], parsed!(year: 1234));
        check("1234 1235", &[num(Year), num(Year)], Err(IMPOSSIBLE));
        check("1234 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID));
        check("1234x1234", &[num(Year), Literal("x"), num(Year)], parsed!(year: 1234));
        check("1234 x 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID));
        check("1234xx1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID));
        check("1234xx1234", &[num(Year), Literal("xx"), num(Year)], parsed!(year: 1234));
        check(
            "1234 x 1234",
            &[num(Year), Space(" "), Literal("x"), Space(" "), num(Year)],
            parsed!(year: 1234),
        );
        check(
            "1234 x 1235",
            &[num(Year), Space(" "), Literal("x"), Space(" "), Literal("1235")],
            parsed!(year: 1234),
        );

        // signed numeric
        check("-42", &[num(Year)], parsed!(year: -42));
        check("+42", &[num(Year)], parsed!(year: 42));
        check("-0042", &[num(Year)], parsed!(year: -42));
        check("+0042", &[num(Year)], parsed!(year: 42));
        check("-42195", &[num(Year)], parsed!(year: -42195));
        check("−42195", &[num(Year)], Err(INVALID)); // MINUS SIGN (U+2212)
        check("+42195", &[num(Year)], parsed!(year: 42195));
        check("  -42195", &[num(Year)], parsed!(year: -42195));
        check(" +42195", &[num(Year)], parsed!(year: 42195));
        check("  -42195", &[num(Year)], parsed!(year: -42195));
        check("  +42195", &[num(Year)], parsed!(year: 42195));
        check("-42195 ", &[num(Year)], Err(TOO_LONG));
        check("+42195 ", &[num(Year)], Err(TOO_LONG));
        check("  -   42", &[num(Year)], Err(INVALID));
        check("  +   42", &[num(Year)], Err(INVALID));
        check("  -42195", &[Space("  "), num(Year)], parsed!(year: -42195));
        check("  −42195", &[Space("  "), num(Year)], Err(INVALID)); // MINUS SIGN (U+2212)
        check("  +42195", &[Space("  "), num(Year)], parsed!(year: 42195));
        check("  -   42", &[Space("  "), num(Year)], Err(INVALID));
        check("  +   42", &[Space("  "), num(Year)], Err(INVALID));
        check("-", &[num(Year)], Err(TOO_SHORT));
        check("+", &[num(Year)], Err(TOO_SHORT));

        // unsigned numeric
        check("345", &[num(Ordinal)], parsed!(ordinal: 345));
        check("+345", &[num(Ordinal)], Err(INVALID));
        check("-345", &[num(Ordinal)], Err(INVALID));
        check(" 345", &[num(Ordinal)], parsed!(ordinal: 345));
        check("−345", &[num(Ordinal)], Err(INVALID)); // MINUS SIGN (U+2212)
        check("345 ", &[num(Ordinal)], Err(TOO_LONG));
        check(" 345", &[Space(" "), num(Ordinal)], parsed!(ordinal: 345));
        check("345 ", &[num(Ordinal), Space(" ")], parsed!(ordinal: 345));
        check("345�� ", &[num(Ordinal), Literal("��"), Space(" ")], parsed!(ordinal: 345));
        check("345��", &[num(Ordinal)], Err(TOO_LONG));
        check("\u{0363}345", &[num(Ordinal)], Err(INVALID));
        check(" +345", &[num(Ordinal)], Err(INVALID));
        check(" -345", &[num(Ordinal)], Err(INVALID));
        check("\t345", &[Space("\t"), num(Ordinal)], parsed!(ordinal: 345));
        check(" +345", &[Space(" "), num(Ordinal)], Err(INVALID));
        check(" -345", &[Space(" "), num(Ordinal)], Err(INVALID));

        // various numeric fields
        check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678));
        check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678));
        check(
            "12 34 56 78",
            &[num(YearDiv100), num(YearMod100), num(IsoYearDiv100), num(IsoYearMod100)],
            parsed!(year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78),
        );
        check(
            "1 1 2 3 4 5",
            &[
                num(Quarter),
                num(Month),
                num(Day),
                num(WeekFromSun),
                num(NumDaysFromSun),
                num(IsoWeek),
            ],
            parsed!(quarter: 1, month: 1, day: 2, week_from_sun: 3, weekday: Weekday::Thu, isoweek: 5),
        );
        check(
            "6 7 89 01",
            &[num(WeekFromMon), num(WeekdayFromMon), num(Ordinal), num(Hour12)],
            parsed!(week_from_mon: 6, weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1),
        );
        check(
            "23 45 6 78901234 567890123",
            &[num(Hour), num(Minute), num(Second), num(Nanosecond), num(Timestamp)],
            parsed!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234, timestamp: 567_890_123),
        );
    }

    #[test]
    fn test_parse_fixed() {
        use crate::format::Fixed::*;
        use crate::format::Item::{Literal, Space};

        // fixed: month and weekday names
        check("apr", &[fixed(ShortMonthName)], parsed!(month: 4));
        check("Apr", &[fixed(ShortMonthName)], parsed!(month: 4));
        check("APR", &[fixed(ShortMonthName)], parsed!(month: 4));
        check("ApR", &[fixed(ShortMonthName)], parsed!(month: 4));
        check("\u{0363}APR", &[fixed(ShortMonthName)], Err(INVALID));
        check("April", &[fixed(ShortMonthName)], Err(TOO_LONG)); // `Apr` is parsed
        check("A", &[fixed(ShortMonthName)], Err(TOO_SHORT));
        check("Sol", &[fixed(ShortMonthName)], Err(INVALID));
        check("Apr", &[fixed(LongMonthName)], parsed!(month: 4));
        check("Apri", &[fixed(LongMonthName)], Err(TOO_LONG)); // `Apr` is parsed
        check("April", &[fixed(LongMonthName)], parsed!(month: 4));
        check("Aprill", &[fixed(LongMonthName)], Err(TOO_LONG));
        check("Aprill", &[fixed(LongMonthName), Literal("l")], parsed!(month: 4));
        check("Aprl", &[fixed(LongMonthName), Literal("l")], parsed!(month: 4));
        check("April", &[fixed(LongMonthName), Literal("il")], Err(TOO_SHORT)); // do not backtrack
        check("thu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
        check("Thu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
        check("THU", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
        check("tHu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu));
        check("Thursday", &[fixed(ShortWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed
        check("T", &[fixed(ShortWeekdayName)], Err(TOO_SHORT));
        check("The", &[fixed(ShortWeekdayName)], Err(INVALID));
        check("Nop", &[fixed(ShortWeekdayName)], Err(INVALID));
        check("Thu", &[fixed(LongWeekdayName)], parsed!(weekday: Weekday::Thu));
        check("Thur", &[fixed(LongWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed
        check("Thurs", &[fixed(LongWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed
        check("Thursday", &[fixed(LongWeekdayName)], parsed!(weekday: Weekday::Thu));
        check("Thursdays", &[fixed(LongWeekdayName)], Err(TOO_LONG));
        check("Thursdays", &[fixed(LongWeekdayName), Literal("s")], parsed!(weekday: Weekday::Thu));
        check("Thus", &[fixed(LongWeekdayName), Literal("s")], parsed!(weekday: Weekday::Thu));
        check("Thursday", &[fixed(LongWeekdayName), Literal("rsday")], Err(TOO_SHORT)); // do not backtrack

        // fixed: am/pm
        check("am", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0));
        check("pm", &[fixed(LowerAmPm)], parsed!(hour_div_12: 1));
        check("AM", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0));
        check("PM", &[fixed(LowerAmPm)], parsed!(hour_div_12: 1));
        check("am", &[fixed(UpperAmPm)], parsed!(hour_div_12: 0));
        check("pm", &[fixed(UpperAmPm)], parsed!(hour_div_12: 1));
        check("AM", &[fixed(UpperAmPm)], parsed!(hour_div_12: 0));
        check("PM", &[fixed(UpperAmPm)], parsed!(hour_div_12: 1));
        check("Am", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0));
        check(" Am", &[Space(" "), fixed(LowerAmPm)], parsed!(hour_div_12: 0));
        check("Am��", &[fixed(LowerAmPm), Literal("��")], parsed!(hour_div_12: 0));
        check("��Am", &[Literal("��"), fixed(LowerAmPm)], parsed!(hour_div_12: 0));
        check("\u{0363}am", &[fixed(LowerAmPm)], Err(INVALID));
        check("\u{0360}am", &[fixed(LowerAmPm)], Err(INVALID));
        check(" Am", &[fixed(LowerAmPm)], Err(INVALID));
        check("Am ", &[fixed(LowerAmPm)], Err(TOO_LONG));
        check("a.m.", &[fixed(LowerAmPm)], Err(INVALID));
        check("A.M.", &[fixed(LowerAmPm)], Err(INVALID));
        check("ame", &[fixed(LowerAmPm)], Err(TOO_LONG)); // `am` is parsed
        check("a", &[fixed(LowerAmPm)], Err(TOO_SHORT));
        check("p", &[fixed(LowerAmPm)], Err(TOO_SHORT));
        check("x", &[fixed(LowerAmPm)], Err(TOO_SHORT));
        check("xx", &[fixed(LowerAmPm)], Err(INVALID));
        check("", &[fixed(LowerAmPm)], Err(TOO_SHORT));
    }

    #[test]
    fn test_parse_fixed_nanosecond() {
        use crate::format::Fixed::{Nanosecond, Nanosecond3, Nanosecond6, Nanosecond9};
        use crate::format::InternalInternal::*;
        use crate::format::Item::Literal;
        use crate::format::Numeric::Second;

        // fixed: dot plus nanoseconds
        check("", &[fixed(Nanosecond)], parsed!()); // no field set, but not an error
        check(".", &[fixed(Nanosecond)], Err(TOO_SHORT));
        check("4", &[fixed(Nanosecond)], Err(TOO_LONG)); // never consumes `4`
        check("4", &[fixed(Nanosecond), num(Second)], parsed!(second: 4));
        check(".0", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
        check(".4", &[fixed(Nanosecond)], parsed!(nanosecond: 400_000_000));
        check(".42", &[fixed(Nanosecond)], parsed!(nanosecond: 420_000_000));
        check(".421", &[fixed(Nanosecond)], parsed!(nanosecond: 421_000_000));
        check(".42195", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_000));
        check(".421951", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_000));
        check(".4219512", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_200));
        check(".42195123", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_230));
        check(".421950803", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
        check(".4219508035", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
        check(".42195080354", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
        check(".421950803547", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803));
        check(".000000003", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
        check(".0000000031", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
        check(".0000000035", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
        check(".000000003547", &[fixed(Nanosecond)], parsed!(nanosecond: 3));
        check(".0000000009", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
        check(".000000000547", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
        check(".0000000009999999999999999999999999", &[fixed(Nanosecond)], parsed!(nanosecond: 0));
        check(".4��", &[fixed(Nanosecond), Literal("��")], parsed!(nanosecond: 400_000_000));
        check(".4x", &[fixed(Nanosecond)], Err(TOO_LONG));
        check(".  4", &[fixed(Nanosecond)], Err(INVALID));
        check("  .4", &[fixed(Nanosecond)], Err(TOO_LONG)); // no automatic trimming

        // fixed-length fractions of a second
        check("", &[fixed(Nanosecond3)], parsed!()); // no field set, but not an error
        check("4", &[fixed(Nanosecond3)], Err(TOO_LONG)); // never consumes `4`
        check(".12", &[fixed(Nanosecond3)], Err(TOO_SHORT));
        check(".123", &[fixed(Nanosecond3)], parsed!(nanosecond: 123_000_000));
        check(".1234", &[fixed(Nanosecond3)], Err(TOO_LONG));
        check(".1234", &[fixed(Nanosecond3), Literal("4")], parsed!(nanosecond: 123_000_000));

        check("", &[fixed(Nanosecond6)], parsed!()); // no field set, but not an error
        check("4", &[fixed(Nanosecond6)], Err(TOO_LONG)); // never consumes `4`
        check(".12345", &[fixed(Nanosecond6)], Err(TOO_SHORT));
        check(".123456", &[fixed(Nanosecond6)], parsed!(nanosecond: 123_456_000));
        check(".1234567", &[fixed(Nanosecond6)], Err(TOO_LONG));
        check(".1234567", &[fixed(Nanosecond6), Literal("7")], parsed!(nanosecond: 123_456_000));

        check("", &[fixed(Nanosecond9)], parsed!()); // no field set, but not an error
        check("4", &[fixed(Nanosecond9)], Err(TOO_LONG)); // never consumes `4`
        check(".12345678", &[fixed(Nanosecond9)], Err(TOO_SHORT));
        check(".123456789", &[fixed(Nanosecond9)], parsed!(nanosecond: 123_456_789));
        check(".1234567890", &[fixed(Nanosecond9)], Err(TOO_LONG));
        check(".1234567890", &[fixed(Nanosecond9), Literal("0")], parsed!(nanosecond: 123_456_789));

        // fixed: nanoseconds without the dot
        check("", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
        check(".", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
        check("0", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
        check("4", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
        check("42", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
        check("421", &[internal_fixed(Nanosecond3NoDot)], parsed!(nanosecond: 421_000_000));
        check("4210", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG));
        check(
            "42143",
            &[internal_fixed(Nanosecond3NoDot), num(Second)],
            parsed!(nanosecond: 421_000_000, second: 43),
        );
        check(
            "421��",
            &[internal_fixed(Nanosecond3NoDot), Literal("��")],
            parsed!(nanosecond: 421_000_000),
        );
        check(
            "��421",
            &[Literal("��"), internal_fixed(Nanosecond3NoDot)],
            parsed!(nanosecond: 421_000_000),
        );
        check("42195", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG));
        check("123456789", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG));
        check("4x", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT));
        check("  4", &[internal_fixed(Nanosecond3NoDot)], Err(INVALID));
        check(".421", &[internal_fixed(Nanosecond3NoDot)], Err(INVALID));

        check("", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
        check(".", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
        check("0", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
        check("1234", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
        check("12345", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
        check("421950", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 421_950_000));
        check("000003", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 3000));
        check("000000", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 0));
        check("1234567", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_LONG));
        check("123456789", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_LONG));
        check("4x", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT));
        check("     4", &[internal_fixed(Nanosecond6NoDot)], Err(INVALID));
        check(".42100", &[internal_fixed(Nanosecond6NoDot)], Err(INVALID));

        check("", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
        check(".", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
        check("42195", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
        check("12345678", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT));
        check("421950803", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 421_950_803));
        check("000000003", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 3));
        check(
            "42195080354",
            &[internal_fixed(Nanosecond9NoDot), num(Second)],
            parsed!(nanosecond: 421_950_803, second: 54),
        ); // don't skip digits that come after the 9
        check("1234567890", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_LONG));
        check("000000000", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 0));
        check("00000000x", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID));
        check("        4", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID));
        check(".42100000", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID));
    }

    #[test]
    fn test_parse_fixed_timezone_offset() {
        use crate::format::Fixed::*;
        use crate::format::InternalInternal::*;
        use crate::format::Item::Literal;

        // TimezoneOffset
        check("1", &[fixed(TimezoneOffset)], Err(INVALID));
        check("12", &[fixed(TimezoneOffset)], Err(INVALID));
        check("123", &[fixed(TimezoneOffset)], Err(INVALID));
        check("1234", &[fixed(TimezoneOffset)], Err(INVALID));
        check("12345", &[fixed(TimezoneOffset)], Err(INVALID));
        check("123456", &[fixed(TimezoneOffset)], Err(INVALID));
        check("1234567", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+1", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+12", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+123", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+1234", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12345", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+123456", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+1234567", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12345678", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12:", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+12:3", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("-12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("−12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12:34:", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12:34:5", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12:34:56", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12:34:56:", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12 34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12  34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("12:34:56", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+12::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12: :34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12:::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12::::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("+12:34:56", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12:3456", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+1234:56", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+1234:567", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0));
        check("-00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0));
        check("−00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0)); // MINUS SIGN (U+2212)
        check("+00:01", &[fixed(TimezoneOffset)], parsed!(offset: 60));
        check("-00:01", &[fixed(TimezoneOffset)], parsed!(offset: -60));
        check("+00:30", &[fixed(TimezoneOffset)], parsed!(offset: 1_800));
        check("-00:30", &[fixed(TimezoneOffset)], parsed!(offset: -1_800));
        check("+24:00", &[fixed(TimezoneOffset)], parsed!(offset: 86_400));
        check("-24:00", &[fixed(TimezoneOffset)], parsed!(offset: -86_400));
        check("−24:00", &[fixed(TimezoneOffset)], parsed!(offset: -86_400)); // MINUS SIGN (U+2212)
        check("+99:59", &[fixed(TimezoneOffset)], parsed!(offset: 359_940));
        check("-99:59", &[fixed(TimezoneOffset)], parsed!(offset: -359_940));
        check("+00:60", &[fixed(TimezoneOffset)], Err(OUT_OF_RANGE));
        check("+00:99", &[fixed(TimezoneOffset)], Err(OUT_OF_RANGE));
        check("#12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+12:34 ", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12 34 ", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check(" +12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check(" -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check(" −12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("  +12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("  -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("\t -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12: 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12 :34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12 : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12 :  34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12  : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12:  34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12  :34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("-12  :  34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240));
        check("12:34 ", &[fixed(TimezoneOffset)], Err(INVALID));
        check(" 12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check(
            "+12345",
            &[fixed(TimezoneOffset), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check(
            "+12:345",
            &[fixed(TimezoneOffset), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check("+12:34:", &[fixed(TimezoneOffset), Literal(":")], parsed!(offset: 45_240));
        check("Z12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("X12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("Z+12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("X+12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("X−12:34", &[fixed(TimezoneOffset)], Err(INVALID)); // MINUS SIGN (U+2212)
        check("��+12:34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+12:34��", &[fixed(TimezoneOffset)], Err(TOO_LONG));
        check("+12:��34", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+1234��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: 45_240));
        check("-1234��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240));
        check("−1234��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12:34��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: 45_240));
        check("-12:34��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240));
        check("−12:34��", &[fixed(TimezoneOffset), Literal("��")], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("��+12:34", &[Literal("��"), fixed(TimezoneOffset)], parsed!(offset: 45_240));
        check("Z", &[fixed(TimezoneOffset)], Err(INVALID));
        check("A", &[fixed(TimezoneOffset)], Err(INVALID));
        check("PST", &[fixed(TimezoneOffset)], Err(INVALID));
        check("#Z", &[fixed(TimezoneOffset)], Err(INVALID));
        check(":Z", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+Z", &[fixed(TimezoneOffset)], Err(TOO_SHORT));
        check("+:Z", &[fixed(TimezoneOffset)], Err(INVALID));
        check("+Z:", &[fixed(TimezoneOffset)], Err(INVALID));
        check("z", &[fixed(TimezoneOffset)], Err(INVALID));
        check(" :Z", &[fixed(TimezoneOffset)], Err(INVALID));
        check(" Z", &[fixed(TimezoneOffset)], Err(INVALID));
        check(" z", &[fixed(TimezoneOffset)], Err(INVALID));

        // TimezoneOffsetColon
        check("1", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("123", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("1234", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12345", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("123456", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("1234567", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12345678", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("+1", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+12", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+123", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("-1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240));
        check("−1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12345", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+123456", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+1234567", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12345678", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("1:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12:3", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12:34:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12:34:5", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("12:34:56", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("+1:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("+12:", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+12:3", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("-12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240));
        check("−12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12:34:", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12:34:5", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12:34:56", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12:34:56:", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12:34:56:7", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12:34:56:78", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+12:3456", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("+1234:56", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check("−12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("−12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12 :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12: 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12: 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12 :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("-12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240));
        check("+12  : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12 :  34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12  :  34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12: :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12:::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12::::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("+12::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("#1234", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("#12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("+12:34 ", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG));
        check(" +12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("\t+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("\t\t+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240));
        check("12:34 ", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check(" 12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check(":", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check(
            "+12345",
            &[fixed(TimezoneOffsetColon), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check(
            "+12:345",
            &[fixed(TimezoneOffsetColon), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check("+12:34:", &[fixed(TimezoneOffsetColon), Literal(":")], parsed!(offset: 45_240));
        check("Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("A", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("PST", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("#Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check(":Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("+Z", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT));
        check("+:Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("+Z:", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check("z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check(" :Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check(" Z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        check(" z", &[fixed(TimezoneOffsetColon)], Err(INVALID));
        // testing `TimezoneOffsetColon` also tests same path as `TimezoneOffsetDoubleColon`
        // and `TimezoneOffsetTripleColon` for function `parse_internal`.
        // No need for separate tests for `TimezoneOffsetDoubleColon` and
        // `TimezoneOffsetTripleColon`.

        // TimezoneOffsetZ
        check("1", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("123", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("1234", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12345", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("123456", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("1234567", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12345678", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("+1", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+12", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+123", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("-1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240));
        check("−1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12345", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+123456", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+1234567", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12345678", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("1:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12:3", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12:34:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12:34:5", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("12:34:56", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("+1:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("+12:", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+12:3", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("-12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240));
        check("−12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12:34:", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12:34:5", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12:34:56", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12:34:56:", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12:34:56:7", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12:34:56:78", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12::34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12:3456", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+1234:56", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12  34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12: 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12 :34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12 : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12  : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12 :  34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("+12  :  34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check("12:34 ", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check(" 12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("+12:34 ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("+12 34 ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check(" +12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240));
        check(
            "+12345",
            &[fixed(TimezoneOffsetZ), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check(
            "+12:345",
            &[fixed(TimezoneOffsetZ), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check("+12:34:", &[fixed(TimezoneOffsetZ), Literal(":")], parsed!(offset: 45_240));
        check("Z12:34", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("X12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("Z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
        check("z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
        check(" Z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
        check(" z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0));
        check("\u{0363}Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("Z ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG));
        check("A", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("PST", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("#Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check(":Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check(":z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("+Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("-Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+A", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+��", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("+Z:", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check(" :Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check(" +Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check(" -Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT));
        check("+:Z", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("Y", &[fixed(TimezoneOffsetZ)], Err(INVALID));
        check("Zulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 0));
        check("zulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 0));
        check("+1234ulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 45_240));
        check("+12:34ulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 45_240));
        // Testing `TimezoneOffsetZ` also tests same path as `TimezoneOffsetColonZ`
        // in function `parse_internal`.
        // No need for separate tests for `TimezoneOffsetColonZ`.

        // TimezoneOffsetPermissive
        check("1", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("123", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("1234", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12345", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("123456", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("1234567", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12345678", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+1", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("+12", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 43_200));
        check("+123", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("+1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("-1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240));
        check("−1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12345", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+123456", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+1234567", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12345678", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("1:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12:3", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12:34:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12:34:5", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("12:34:56", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+1:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+12:", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 43_200));
        check("+12:3", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("+12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("-12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240));
        check("−12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check("+12:34:", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12:34:5", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12:34:56", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12:34:56:", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12:34:56:7", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12:34:56:78", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12 :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12 : 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12  :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12:  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12  :  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12 ::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12: :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12:: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12  ::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12:  :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12::  34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12:::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("+12::::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check("12:34 ", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check(" 12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+12:34 ", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check(" +12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240));
        check(" -12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240));
        check(" −12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212)
        check(
            "+12345",
            &[internal_fixed(TimezoneOffsetPermissive), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check(
            "+12:345",
            &[internal_fixed(TimezoneOffsetPermissive), num(Numeric::Day)],
            parsed!(offset: 45_240, day: 5),
        );
        check(
            "+12:34:",
            &[internal_fixed(TimezoneOffsetPermissive), Literal(":")],
            parsed!(offset: 45_240),
        );
        check("��+12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+12:34��", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("+12:��34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check(
            "+12:34��",
            &[internal_fixed(TimezoneOffsetPermissive), Literal("��")],
            parsed!(offset: 45_240),
        );
        check(
            "��+12:34",
            &[Literal("��"), internal_fixed(TimezoneOffsetPermissive)],
            parsed!(offset: 45_240),
        );
        check("Z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
        check("A", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("PST", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
        check(" Z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
        check(" z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0));
        check("Z ", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG));
        check("#Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check(":Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check(":z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("-Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("+A", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("+PST", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+��", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("+Z:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check(" :Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check(" +Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check(" -Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT));
        check("+:Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));
        check("Y", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID));

        // TimezoneName
        check("CEST", &[fixed(TimezoneName)], parsed!());
        check("cest", &[fixed(TimezoneName)], parsed!()); // lowercase
        check("XXXXXXXX", &[fixed(TimezoneName)], parsed!()); // not a real timezone name
        check("!!!!", &[fixed(TimezoneName)], parsed!()); // not a real timezone name!
        check("CEST 5", &[fixed(TimezoneName), Literal(" "), num(Numeric::Day)], parsed!(day: 5));
        check("CEST ", &[fixed(TimezoneName)], Err(TOO_LONG));
        check(" CEST", &[fixed(TimezoneName)], Err(TOO_LONG));
        check("CE ST", &[fixed(TimezoneName)], Err(TOO_LONG));
    }

    #[test]
    #[rustfmt::skip]
    fn test_parse_practical_examples() {
        use crate::format::InternalInternal::*;
        use crate::format::Item::{Literal, Space};
        use crate::format::Numeric::*;

        // some practical examples
        check(
            "2015-02-04T14:37:05+09:00",
            &[
                num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"),
                num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second),
                fixed(Fixed::TimezoneOffset),
            ],
            parsed!(
                year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
                second: 5, offset: 32400
            ),
        );
        check(
            "2015-02-04T14:37:05-09:00",
            &[
                num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"),
                2014,2020 Red All reserved.
                fixed(Fixed::TimezoneOffset),
            ],
parsedjava.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
                year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
                second: 5, offset: -32400
            ),
        );
        check(
            "2015-02-04T14:37:05−09:00", // timezone offset using MINUS SIGN (U+2212)
            &[
                num(Year), Literal("-"), num *published Free Foundation
                Hour),Literal"") num(Minute) (:",num(Second),
                fixed(Fixed::TimezoneOffset)
            ],
            parsed!(
                year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
                second: 5, offset: -32400
            ),
        );
        check(
            "20150204143705567",
            &[
                numYear,num(,num(ay),num() numM) num(Second,
                internal_fixed(Nanosecond3NoDot)
            ],
            parsed!(
                year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37,
                second:5 : 567000000
            ),
        );
        check(
            "Mon, 10 Jun 2013 09:32:37 GMT",
            &[
                fixed(Fixed::ShortWeekdayName), Literal(","), Space(" "), num(Day), Space(" "),
                fixed(Fixed::ShortMonthName), Space(" "), num(Year), Space(" "), num(Hour),
                Literal(":" *
            ],
            parsed!(
                year: 2013, month: 6, day: 10, weekday: Weekday::Mon,
                hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37
            ),
        );
        check(
            "��Mon, 10 Jun��2013 09:32:37  GMT��",
            &[
                Literal("�) fixed(Fixed:ShortWeekdayName),Literal("",Space(" ) num(Day),
(" "),fixed(Fixed::hortMonthName), Literal"��"), num(ear,Space"")
                num(Hour), Literal(#include"interpreterRuntime.pp"
                Literal("nclude "opsmethodData"
java.lang.StringIndexOutOfBoundsException: Range [35, 36) out of bounds for length 35
            (java.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 20
                  bne(t0, t1, notBytet0 t1,notByte);
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
            bindnotChar);
        );
        
            "Sun Aug 02 ::15 CEST 2020",
            &[
                  if (JvmtiExport:can_pop_frame()) {
                Space(" "), num(Day), Space(/
                Literal(":"), num(Second), Space("     // call_VM_base, so the arg registers are available.
                num(Year
            ],
                // address of the same-named entrypoint in the generated interpreter code.
                year: 2020, month: 8, dayjrx10;
                hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15
            )java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
        ;
        check(
            ld(x10 val_addr;
            
rsed(
year:2006, month: 1, day: day:2,hour_div_12:1,hour_mod_12:3 minute 4 :5
            )
        );
        check(
            :14PM"
            &Hour12) (""),num) fixedFixed:LowerAmPm]java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79
parsed(hour_div_12: 1, hour_mod_12: 3, minute: 14),
        );
        check(
            "12345678901234.56789",
            &[num(Timestamp), Literal("lwut0 Address( JvmtiThreadState:earlyret_state_offset()))
            parsed
        ld(,Address(xthread JavaThread::jvmti_thread_state_offset());
        check(
            "1234567890123456789,
            &[() Fixed:anosecond]
            java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 38
        ;

        // docstring examples from `impl str::FromStr`
        check(
            "2000-01-02T03:04:05Z"(cp_offset >0 bcp stillpointing tostartofbytecode)
  }else if (index_size == sizeof(u4)) {
                num(Year), Literal    java.lang.StringIndexOutOfBoundsException: Index 70 out of bounds for length 70
                num( else if (index_size= (u1) {
                internal_fixed(imezoneOffsetPermissive)
            ],
            parsed!(
                year: 2000, month: 1, day: 2                                                            {
                 0
            ),
        );
        checkjava.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
            "2000-01-2 ::5Z",
            voidInterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register cache,
                num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Space(" "),
                num(Hour, Literal(""), num(inute), Literal"", (Second)java.lang.StringIndexOutOfBoundsException: Index 80 out of bounds for length 80
                internal_fixed(TimezoneOffsetPermissive)
            ],
            parsed!
                year: 2000, month: 1, day                                                                        size_t index_size){
                offset: 0
            ),
        );
    }

    #[track_caller
    int bcp_offsetjava.lang.StringIndexOutOfBoundsException: Index 78 out of bounds for length 78
        mut parsed  Parsed:new(;
        assert!(parse(&mut parsed, sassert(sizeofConstantPoolCacheEntry == 4    java.lang.StringIndexOutOfBoundsException: Range [76, 75) out of bounds for length 78
    java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

    #[track_calleradd(,cache, in_bytes(ConstantPoolCache:base_offset()));
    fn checks:&tr :&[tem] : ParseResultParsed) java.lang.StringIndexOutOfBoundsException: Index 70 out of bounds for length 70
        let mut parsed = ->resolved_references(index)
       let   parse(mutparsed, s,items.iter();
        let.map(_ parsed;
        assert_eq!(parsed, expected  
    }

    #[test]
    fn test_rfc2822()void ::load_resolved_klass_at_offset
        let ymd_hmsn = |y, m, d, h, n, s, nano, off| {
            lhu(temp,)) // temp = resolved_klass_index
                .unwrap()
                y,,d h ,s
                (
                .with_nanosecond(nano)
                .unwrap()
        };

        // Test data - (input, Ok(expected result) or Err(error code))
        let testdates = [
            ("//      x10: superklass
            (Fri,  2Jan  :: -" (,1  17 35,20 0,
Label& java.lang.StringIndexOutOfBoundsException: Index 73 out of bounds for length 73
            ",20   :35:20-0800 UTC)",Ok(ymd_hmsn2015,1,, 17,35, ,0,-)), // trailing comment
            (
                rjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
                Ok(ymd_hmsn(2015,java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
            ), // complex trailing comment
            (r"Tue, addi(esp, esp wordSize)
            (
                "Tue}
                Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20  (esp ,2  :);
            , // multiple comments
            ("Tue, 20 Jan bler::(Register ) {
            ("20Jan 2015173520-",Ok(ymd_hmsn(, 1 , , 35 20,0,-)) // no day of week
            ("20 JAN 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35}
            (Tue 20Jan 17:35 0800, Okymd_hmsn(,1,20,17,35 0,0 -8)),// no second
            " Sep  :4500+000, Ok(ymd_hmsn(,9,11, 9 45 0 0,0))java.lang.StringIndexOutOfBoundsException: Index 86 out of bounds for length 86
            ("112001094500 EST" (ymd_hmsn(2001, 9, 11, 9, 45, 0 0-5))java.lang.StringIndexOutOfBoundsException: Index 85 out of bounds for length 85
            ("11 Sep 2001
            ("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month
            "ue 20Jan 2015, Err(TOO_SHORT
            ("Tue(esp, esp,-2*wordSize;
            ("      pop_ptr(;
            ("Tue, 20 Jan 2015 7    case ztos: // fall through
    case:  // fall through
            ("Tue, java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 12
            (Tue 20 Jan 2015:5: 0890, ErrOUT_OF_RANGE),  offset
            ("6 case :
            // named timezones that have specific timezone offsets
            // see https://www.rfc-editor.org/rfc/rfc2822#section-4.3java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 54
            ("Tue, 20 Jan 2015 17:35:20 GMT", Ok(ymd_hmsn(2015, 1, 20, 17, 35,    java.lang.StringIndexOutOfBoundsException: Range [9, 8) out of bounds for length 31
            ("Tue, 20 Janbreak;
            ("Tue, 20 Jan 2015 break;
            (
            ("Tue, java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
            ("ue 20 Jan  17:520CDT"Okymd_hmsn2015 1 ,17,35 20,,5)))
            ("Tue, 20 Jan 2015 17:35:20 CST", Ok(ymd_hmsn(2015, 1, 20,   (,Address,Interpreterexpr_offset_in_bytesn)java.lang.StringIndexOutOfBoundsException: Index 62 out of bounds for length 62
            ("Tue, 20 Jan 2015 17:35:20 MDT",java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
            ("Tue, 
            ("Tue, 20 Jan 2015 17:35:mv(x19_sender_sp, sp);
            ("Tue, 20 Jan 2015 17:35:20 PST", Ok(ymd_hmsn(  sd(esp,Address,frame::interpreter_frame_last_sp_offset * wordSize));
            ("Tue, 20 Jan 2015 17java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
/
            T, 20java.lang.StringIndexOutOfBoundsException: Range [26, 25) out of bounds for length 89
    // JVMTI events, such as single-stepping, are implemented partly by avoiding running
            // interp_only_mode if these events CAN be enabled.
                (Tue  2015 :35:20 " ((2015 ,20, 17 35 20,0 0),
(Tue, 20 201517:3520 ", ymd_hmsn , 20 17 ,20 ,0))
            // named single-letter timezone "J" is specifically not validbind();
            ("Tue, 20 Jan 2015 17:35:20jrt0)java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
            ("Tue, 20 Jan 2015 17:35:v InterpreterMacroAssembler:(TosState state, int step) {
        (Tue,20 2015173520Z, INVALID),           
            (INVALID)) // bad offset: zulu not allowed
            ("Tue, 20 Jan 2015 17:35:20 ZULU", ool verifyoop,
            ("Tue, 20 Jan 2015 17:35:20 −0800", Err(INVALID)), // bad offset: timezone offset using MINUS SIGN (U+2212), not specified for RFC 2822
            ("Tue, 20 Jan 2015  // Pay attention to the argument Rs, which is acquiesce in t0.
            ("Tue, 20   }
            (Tue,  Jan 2015�17:35:20 0800, Err(NVALID),// bad character!
        ];

        fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffsetjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
            let     ld(1,Address(, JavaThread::polling_word_offset()))
parsemut ,:FixedFixed:]java.lang.StringIndexOutOfBoundsException: Range [76, 71) out of bounds for length 76
            .)
        }

        // Test against test data above
        for &(date, checkdate) in testdates.
            #[cfg(feature = "std")]
            eprintln!("Test input: {date: ld(1 Addresst1));
            let }
            if dt != checkdate {
                // check for expected result
                panic!(
                    "Date conversion failed for {date}\nReceived: {dt:?}\nExpected: {checkdate:?}"
                );
            }
        }
    }

    #[test]
    add(,xbcp,step)java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
        static

        let   // load curr

        // Check that the format is what we expect
        #[cfg(// Unlock the receiver if this is a synchronized method.
        assert_eq!(dt.format(RFC850_FMT).to_string(), "Sunday//

        // Check that it parses correctly
        assert_eq!(
            NaiveDateTime::parse_from_strvoidInterpreterMacroAssembler:remove_activation
            bool throw_monitor_exceptionjava.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 61
        );

        // Check that the rest of the weekdays parse correctly (this test originally failed because unlocked unlock, no_unlock;
        // Sunday parsed incorrectly).
        let testdates = [
            (
                .with_ymd_and_hms(1994, 11, 7,8, 49, ).nwrap(),
                "Monday, 07-Nov-94 08:49:37 GMTsuper_call_VM_leaf((,InterpreterRuntime:t_unwind), xthread);
            ),
            (
                Utc.with_ymd_and_hmsconst do_not_unlock_if_synchronized(xthread,
                "Tuesday, 08-Nov-94 08:49:37 GMT",
            ),
            (
                .with_ymd_and_hms(1994 11 ,8,49,37).unwrap(,
                "Wednesday, 09-Nov-94 08:/java.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
            ),
            (
                Utc.with_ymd_and_hms(1994o check that  object has
                "ThursdayconstAddress monitor(fp, :interpreter_frame_initial_sp_offset *
            ),
            (
                Utc.with_ymd_and_hms(1994, 11, 11, 8, 49, 37).unwrap(),
                "Friday, 11-Nov-94 08:49:37 GMT",
            )
            (
                Utc.with_ymd_and_hms(1994, 11, 12, 8, 49, 37).unwrap(),call_VM(noreg CAST_FROM_FN_PTRaddress,
                "Saturday, 12-Nov-94 08:49:37 GMT",
            ),
        ];

        java.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 31
            assert_eq!(NaiveDateTime:parse_from_str(al1 ,Ok(val.0.aive_utc());
        }

        let test_dates_fail = [
            "Saturday, 12-Nov-94 08:49:37",
            "Saturday, 12-Nov-94 08:49:37 Z",
            "Saturday, 12-Nov-94 08:49:37 GMTTTT",restart
java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 47
            "Saturday, 12-Nov-94 l(, ;  
            "Caturday, 12-Nov-94 08:49:37 GMT",
            " 99Nov94 :937 "
            "java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 0
            "Saturday, 12-Mop-94 08:49CAST_FROM_FN_PTR(address,InterpreterRuntime::
            "Saturday, 12-Nov-94 28:49:37 GMT",
            "Saturday, 12-Nov-94 08:99:37 GMT",
            "Saturday, 12-Nov-94 08:49:99 GMT",
        ];

        for val in &test_dates_fail {
            assert!(java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        }
    }

    #[test]
    fn test_rfc3339() {
        let ymd_hmsnbindloop;
            FixedOffset:(   * 60java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
                .unwrap()
                .with_ymd_and_hms(y, m, d, h, nbind(ntry);
                .unwrap()
                .with_nanosecond(nano)
  
        };

        // Test data - (input, Ok(expected result) or Err(error code))
        let testdates = [
            ("2015-01-20T17:35:20-08:00", Ok(ymd_hmsn(2015,    (t1,t0 )java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
                                    InterpreterRuntime))java.lang.StringIndexOutOfBoundsException: Index 91 out of bounds for length 91
            ("1944-06-06T04:04:00Z"
            ("2001-09-11T09:45:00-08:00", Ok(java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 10
            ("2015-01-2035:20001-08:" ymd_hmsn2015, 1,20 ,35 ,1__000,-))java.lang.StringIndexOutOfBoundsException: Index 100 out of bounds for length 100
(,esp16)
            // Lock object
            ("2015-01-20T17:35:20.000000004-08//      c_rarg1: BasicObjectLock to be used for locking
            ("2015-01-20T17:35:20.000000004−08:00", Ok(ymd_hmsn(2015,// Kills:
            (
                "2015-01-20T17:35:20.000000000452-08:00",
                Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)),
            ), // too small
            
        "2015-01-20T17:20000000000452−:00,
                Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)),
            ),    const obj_offset =BasicObjectLock::bj_offset_in_bytes();
            ("2015-01-20 17:35:20-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // without 'T'
            ("2015/01/20T17:35:20.001-08:00", Err(INVALID)), // wrong separator char YMD
-08:00", Err(INVALID), // wrong separator char HMS
(0120T17::20-800" (NVALID)          missing 
            ("99-01-20T17:35:20-08:00", Err(INVALID)),      andit  JVM_ACC_IS_VALUE_BASED_CLASS
            (   // Load (object->mark() | 1) into swap_reg
            ("-2000-01ori(,t0,1)java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
            ("2015-02-30T17:35:20-08:00", java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
            ("2015-01-20T25 swap_reg,,obj_reg, t0,count, /*fallthrough*/NULL);
            ("2015-01= 0 
            ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second value
            ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset value
                  "-0120::20-8:00,Err(INVALID)       // bad year format
            ("15-01-20T17:35:20-08:00:00java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
            ("2015-01-20T17:35:2008:00", Err(INVALID)),      // missing offset sign
            ("2015    (,
            ("2015-01-20T17:35:20Zulu", Err(TOO_LONG)),      // bad offset format
            ("2015-01-20T17:35:20 Zulu", Err(INVALID)),      // bad offset format
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
            ("2015-01-20T17:35:20 GMT"// Unlocks an object. Used in monitorexit bytecode and
            ("2015-01-20T17:35:20+GMT", Err(INVALID)),       // bad offset format
            ("2015-01-20T17:35:20++08:00", Err(INVALID)),    // bad offset format
            //      t0, t1 (temp regs)
            ("2015-01-20 assert = ," only forlooksItberarg1
("2015-01-T:35200800, Err()),  // bad offset sign
            ("2015-01-20T17:35:20-08-00", Err(INVALID)),  // bad offset separator
java.lang.StringIndexOutOfBoundsException: Index 81 out of bounds for length 81
            ("2015-01
            ("2015-01-20T17:35:20-08:0", Err(TOO_SHORT)), // bad offset minutes
(2015--20T17:35:20-08:AA INVALID),  // bad offset minutes
            ("2015-01-20T17:35:20-08:ZZ", Err(INVALID)),call_VM_leaf(address InterpreterRuntime::, )
            ("2015-01-20T17:35:20.001-08 : 00", Err(INVALID)), // bad offset separator
            ("2015-01-20T17:35:20-08:00:00", int constant
            ("2015-01-20T17:35:20+08:", Err(TOO_SHORT)),  // bad offset format
            ("2015-01-20T17:35:20-08:}
            ("2015-01-20T17:35:20−08:", Err(TOO_SHORT)), // bad offset format with MINUS SIGN (U+2212)
            ("2015-01-20T17:35:20-08", Err(void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in,
            ("2015-01-20T"                                                      int,
            ("2015-01-20java.lang.StringIndexOutOfBoundsException: Range [59, 27) out of bounds for length 62
            ("2015-01-20T00:00:1-08:00, )java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
        ];

        // Test against test data above
        for  
            let dt = )
            if dt != checkdate {
                // check for expected result
                panic!(
"Date  for date\nReceived:{:}\nExpected:checkdate?"
                );
      }
        }
    }

    #[test]
    fn test_issue_1010() {
        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}",
            "uc\{c%\{}\{}0SUN\{}\{}u{SUNN\uc\{c\{}SUN\{}\{}!uc\u{b}uc}uc\u{c}u{c}A\c}\{}a,
        ;
        java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 0
    }
}

Messung V0.5 in Prozent
C=88 H=94 G=90

¤ Dauer der Verarbeitung: 0.114 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.