time/parsing/combinator/rfc/
iso8601.rs1use core::num::NonZero;
6
7#[allow(unused_imports, reason = "MSRV of 1.87")]
8use num_conv::prelude::*;
9
10use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign};
11use crate::parsing::ParsedItem;
12use crate::{Month, Weekday};
13
14#[derive(Debug, Clone, Copy)]
17pub(crate) enum ExtendedKind {
18 Basic,
20 Extended,
22 Unknown,
24}
25
26impl ExtendedKind {
27 #[inline]
29 pub(crate) const fn maybe_extended(self) -> bool {
30 matches!(self, Self::Extended | Self::Unknown)
31 }
32
33 #[inline]
35 pub(crate) const fn is_extended(self) -> bool {
36 matches!(self, Self::Extended)
37 }
38
39 #[inline]
42 pub(crate) fn coerce_basic(&mut self) -> Option<()> {
43 match self {
44 Self::Basic => Some(()),
45 Self::Extended => None,
46 Self::Unknown => {
47 *self = Self::Basic;
48 Some(())
49 }
50 }
51 }
52
53 #[inline]
56 pub(crate) fn coerce_extended(&mut self) -> Option<()> {
57 match self {
58 Self::Basic => None,
59 Self::Extended => Some(()),
60 Self::Unknown => {
61 *self = Self::Extended;
62 Some(())
63 }
64 }
65 }
66}
67
68#[inline]
70pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
71 Some(match sign(input) {
72 Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| {
73 let val = val.cast_signed();
74 if sign == b'-' {
75 -val
76 } else {
77 val
78 }
79 }),
80 None => exactly_n_digits::<4, u32>(input)?.map(|val| val.cast_signed()),
81 })
82}
83
84#[inline]
86pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
87 first_match(
88 [
89 (b"01".as_slice(), Month::January),
90 (b"02".as_slice(), Month::February),
91 (b"03".as_slice(), Month::March),
92 (b"04".as_slice(), Month::April),
93 (b"05".as_slice(), Month::May),
94 (b"06".as_slice(), Month::June),
95 (b"07".as_slice(), Month::July),
96 (b"08".as_slice(), Month::August),
97 (b"09".as_slice(), Month::September),
98 (b"10".as_slice(), Month::October),
99 (b"11".as_slice(), Month::November),
100 (b"12".as_slice(), Month::December),
101 ],
102 true,
103 )(input)
104}
105
106#[inline]
108pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u8>>> {
109 exactly_n_digits::<2, _>(input)
110}
111
112#[inline]
114pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u8>>> {
115 exactly_n_digits::<2, _>(input)
116}
117
118#[inline]
120pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> {
121 first_match(
122 [
123 (b"1".as_slice(), Weekday::Monday),
124 (b"2".as_slice(), Weekday::Tuesday),
125 (b"3".as_slice(), Weekday::Wednesday),
126 (b"4".as_slice(), Weekday::Thursday),
127 (b"5".as_slice(), Weekday::Friday),
128 (b"6".as_slice(), Weekday::Saturday),
129 (b"7".as_slice(), Weekday::Sunday),
130 ],
131 true,
132 )(input)
133}
134
135#[inline]
137pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u16>>> {
138 exactly_n_digits::<3, _>(input)
139}
140
141#[inline]
143pub(crate) fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
144 exactly_n_digits::<2, _>(input)
145}
146
147#[inline]
149pub(crate) fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
150 exactly_n_digits::<2, _>(input)
151}
152
153#[inline]
160pub(crate) fn float(input: &[u8]) -> Option<ParsedItem<'_, (u8, Option<f64>)>> {
161 let ParsedItem(input, integer_part) = match input {
163 [first_digit @ b'0'..=b'9', second_digit @ b'0'..=b'9', input @ ..] => {
164 ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0'))
165 }
166 _ => return None,
167 };
168
169 if let Some(ParsedItem(input, ())) = decimal_sign(input) {
170 let ParsedItem(mut input, mut fractional_part) =
172 any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.);
173
174 let mut divisor = 10.;
175 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
177 input = new_input;
178 divisor *= 10.;
179 fractional_part += (digit - b'0') as f64 / divisor;
180 }
181
182 Some(ParsedItem(input, (integer_part, Some(fractional_part))))
183 } else {
184 Some(ParsedItem(input, (integer_part, None)))
185 }
186}
187
188#[inline]
190fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
191 ascii_char::<b'.'>(input).or_else(|| ascii_char::<b','>(input))
192}