From 64d98f8ee037282c35007b64c2649055c56af1db Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:03 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- vendor/time/src/parsing/combinator/rfc/iso8601.rs | 173 ++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 vendor/time/src/parsing/combinator/rfc/iso8601.rs (limited to 'vendor/time/src/parsing/combinator/rfc/iso8601.rs') diff --git a/vendor/time/src/parsing/combinator/rfc/iso8601.rs b/vendor/time/src/parsing/combinator/rfc/iso8601.rs new file mode 100644 index 000000000..613a9057f --- /dev/null +++ b/vendor/time/src/parsing/combinator/rfc/iso8601.rs @@ -0,0 +1,173 @@ +//! Rules defined in [ISO 8601]. +//! +//! [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html + +use core::num::{NonZeroU16, NonZeroU8}; + +use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign}; +use crate::parsing::ParsedItem; +use crate::{Month, Weekday}; + +/// What kind of format is being parsed. This is used to ensure each part of the format (date, time, +/// offset) is the same kind. +#[derive(Debug, Clone, Copy)] +pub(crate) enum ExtendedKind { + /// The basic format. + Basic, + /// The extended format. + Extended, + /// ¯\_(ツ)_/¯ + Unknown, +} + +impl ExtendedKind { + /// Is it possible that the format is extended? + pub(crate) const fn maybe_extended(self) -> bool { + matches!(self, Self::Extended | Self::Unknown) + } + + /// Is the format known for certain to be extended? + pub(crate) const fn is_extended(self) -> bool { + matches!(self, Self::Extended) + } + + /// If the kind is `Unknown`, make it `Basic`. Otherwise, do nothing. Returns `Some` if and only + /// if the kind is now `Basic`. + pub(crate) fn coerce_basic(&mut self) -> Option<()> { + match self { + Self::Basic => Some(()), + Self::Extended => None, + Self::Unknown => { + *self = Self::Basic; + Some(()) + } + } + } + + /// If the kind is `Unknown`, make it `Extended`. Otherwise, do nothing. Returns `Some` if and + /// only if the kind is now `Extended`. + pub(crate) fn coerce_extended(&mut self) -> Option<()> { + match self { + Self::Basic => None, + Self::Extended => Some(()), + Self::Unknown => { + *self = Self::Extended; + Some(()) + } + } + } +} + +/// Parse a possibly expanded year. +pub(crate) fn year(input: &[u8]) -> Option> { + Some(match sign(input) { + Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| { + let val = val as i32; + if sign == b'-' { -val } else { val } + }), + None => exactly_n_digits::<4, u32>(input)?.map(|val| val as _), + }) +} + +/// Parse a month. +pub(crate) fn month(input: &[u8]) -> Option> { + first_match( + [ + (b"01".as_slice(), Month::January), + (b"02".as_slice(), Month::February), + (b"03".as_slice(), Month::March), + (b"04".as_slice(), Month::April), + (b"05".as_slice(), Month::May), + (b"06".as_slice(), Month::June), + (b"07".as_slice(), Month::July), + (b"08".as_slice(), Month::August), + (b"09".as_slice(), Month::September), + (b"10".as_slice(), Month::October), + (b"11".as_slice(), Month::November), + (b"12".as_slice(), Month::December), + ], + true, + )(input) +} + +/// Parse a week number. +pub(crate) fn week(input: &[u8]) -> Option> { + exactly_n_digits::<2, _>(input) +} + +/// Parse a day of the month. +pub(crate) fn day(input: &[u8]) -> Option> { + exactly_n_digits::<2, _>(input) +} + +/// Parse a day of the week. +pub(crate) fn dayk(input: &[u8]) -> Option> { + first_match( + [ + (b"1".as_slice(), Weekday::Monday), + (b"2".as_slice(), Weekday::Tuesday), + (b"3".as_slice(), Weekday::Wednesday), + (b"4".as_slice(), Weekday::Thursday), + (b"5".as_slice(), Weekday::Friday), + (b"6".as_slice(), Weekday::Saturday), + (b"7".as_slice(), Weekday::Sunday), + ], + true, + )(input) +} + +/// Parse a day of the year. +pub(crate) fn dayo(input: &[u8]) -> Option> { + exactly_n_digits::<3, _>(input) +} + +/// Parse the hour. +pub(crate) fn hour(input: &[u8]) -> Option> { + exactly_n_digits::<2, _>(input) +} + +/// Parse the minute. +pub(crate) fn min(input: &[u8]) -> Option> { + exactly_n_digits::<2, _>(input) +} + +/// Parse a floating point number as its integer and optional fractional parts. +/// +/// The number must have two digits before the decimal point. If a decimal point is present, at +/// least one digit must follow. +/// +/// The return type is a tuple of the integer part and optional fraction part. +pub(crate) fn float(input: &[u8]) -> Option)>> { + // Two digits before the decimal. + let ParsedItem(input, integer_part) = match input { + [ + first_digit @ b'0'..=b'9', + second_digit @ b'0'..=b'9', + input @ .., + ] => ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0')), + _ => return None, + }; + + if let Some(ParsedItem(input, ())) = decimal_sign(input) { + // Mandatory post-decimal digit. + let ParsedItem(mut input, mut fractional_part) = + any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.); + + let mut divisor = 10.; + // Any number of subsequent digits. + while let Some(ParsedItem(new_input, digit)) = any_digit(input) { + input = new_input; + divisor *= 10.; + fractional_part += (digit - b'0') as f64 / divisor; + } + + Some(ParsedItem(input, (integer_part, Some(fractional_part)))) + } else { + Some(ParsedItem(input, (integer_part, None))) + } +} + +/// Parse a "decimal sign", which is either a comma or a period. +fn decimal_sign(input: &[u8]) -> Option> { + ascii_char::(input).or_else(|| ascii_char::(input)) +} -- cgit v1.2.3