time/parsing/combinator/rfc/
iso8601.rs1use core::num::NonZero;
6
7use num_conv::prelude::*;
8
9use crate::parsing::ParsedItem;
10use crate::parsing::combinator::{ExactlyNDigits, Sign, any_digit, sign};
11use crate::{Month, Weekday};
12
13#[derive(Debug, Clone, Copy)]
16pub(crate) enum ExtendedKind {
17 Basic,
19 Extended,
21 Unknown,
23}
24
25impl ExtendedKind {
26 #[inline]
28 pub(crate) const fn maybe_extended(self) -> bool {
29 matches!(self, Self::Extended | Self::Unknown)
30 }
31
32 #[inline]
34 pub(crate) const fn is_extended(self) -> bool {
35 matches!(self, Self::Extended)
36 }
37
38 #[inline]
41 pub(crate) const fn coerce_basic(&mut self) -> Option<()> {
42 match self {
43 Self::Basic => Some(()),
44 Self::Extended => None,
45 Self::Unknown => {
46 *self = Self::Basic;
47 Some(())
48 }
49 }
50 }
51
52 #[inline]
55 pub(crate) const fn coerce_extended(&mut self) -> Option<()> {
56 match self {
57 Self::Basic => None,
58 Self::Extended => Some(()),
59 Self::Unknown => {
60 *self = Self::Extended;
61 Some(())
62 }
63 }
64 }
65}
66
67#[inline]
69pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
70 Some(match sign(input) {
71 Some(ParsedItem(input, sign)) => ExactlyNDigits::<6>::parse(input)?.map(|val| {
72 let val = val.cast_signed();
73 match sign {
74 Sign::Negative => -val,
75 Sign::Positive => val,
76 }
77 }),
78 None => ExactlyNDigits::<4>::parse(input)?.map(|val| val.cast_signed().extend()),
79 })
80}
81
82#[inline]
84pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
85 match input {
86 [b'0', b'1', remaining @ ..] => Some(ParsedItem(remaining, Month::January)),
87 [b'0', b'2', remaining @ ..] => Some(ParsedItem(remaining, Month::February)),
88 [b'0', b'3', remaining @ ..] => Some(ParsedItem(remaining, Month::March)),
89 [b'0', b'4', remaining @ ..] => Some(ParsedItem(remaining, Month::April)),
90 [b'0', b'5', remaining @ ..] => Some(ParsedItem(remaining, Month::May)),
91 [b'0', b'6', remaining @ ..] => Some(ParsedItem(remaining, Month::June)),
92 [b'0', b'7', remaining @ ..] => Some(ParsedItem(remaining, Month::July)),
93 [b'0', b'8', remaining @ ..] => Some(ParsedItem(remaining, Month::August)),
94 [b'0', b'9', remaining @ ..] => Some(ParsedItem(remaining, Month::September)),
95 [b'1', b'0', remaining @ ..] => Some(ParsedItem(remaining, Month::October)),
96 [b'1', b'1', remaining @ ..] => Some(ParsedItem(remaining, Month::November)),
97 [b'1', b'2', remaining @ ..] => Some(ParsedItem(remaining, Month::December)),
98 _ => None,
99 }
100}
101
102#[inline]
104pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u8>>> {
105 ExactlyNDigits::<2>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new))
106}
107
108#[inline]
110pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u8>>> {
111 ExactlyNDigits::<2>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new))
112}
113
114#[inline]
116pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> {
117 match input {
118 [b'1', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Monday)),
119 [b'2', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Tuesday)),
120 [b'3', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Wednesday)),
121 [b'4', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Thursday)),
122 [b'5', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Friday)),
123 [b'6', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Saturday)),
124 [b'7', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Sunday)),
125 _ => None,
126 }
127}
128
129#[inline]
131pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u16>>> {
132 ExactlyNDigits::<3>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new))
133}
134
135#[inline]
137pub(crate) const fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
138 ExactlyNDigits::<2>::parse(input)
139}
140
141#[inline]
143pub(crate) const fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
144 ExactlyNDigits::<2>::parse(input)
145}
146
147#[inline]
154pub(crate) fn float(input: &[u8]) -> Option<ParsedItem<'_, (u8, Option<f64>)>> {
155 let ParsedItem(input, integer_part) = match input {
157 [
158 first_digit @ b'0'..=b'9',
159 second_digit @ b'0'..=b'9',
160 input @ ..,
161 ] => ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0')),
162 _ => return None,
163 };
164
165 if let Some(ParsedItem(input, ())) = decimal_sign(input) {
166 let ParsedItem(mut input, mut fractional_part) =
168 any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.);
169
170 let mut divisor = 10.;
171 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
173 input = new_input;
174 divisor *= 10.;
175 fractional_part += (digit - b'0') as f64 / divisor;
176 }
177
178 Some(ParsedItem(input, (integer_part, Some(fractional_part))))
179 } else {
180 Some(ParsedItem(input, (integer_part, None)))
181 }
182}
183
184#[inline]
186fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
187 match input {
188 [b'.' | b',', remaining @ ..] => Some(ParsedItem(remaining, ())),
189 _ => None,
190 }
191}