time/parsing/combinator/rfc/
iso8601.rs1use core::num::{NonZeroU16, NonZeroU8};
6
7use num_conv::prelude::*;
8
9use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign};
10use crate::parsing::ParsedItem;
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 pub(crate) const fn maybe_extended(self) -> bool {
28 matches!(self, Self::Extended | Self::Unknown)
29 }
30
31 pub(crate) const fn is_extended(self) -> bool {
33 matches!(self, Self::Extended)
34 }
35
36 pub(crate) fn coerce_basic(&mut self) -> Option<()> {
39 match self {
40 Self::Basic => Some(()),
41 Self::Extended => None,
42 Self::Unknown => {
43 *self = Self::Basic;
44 Some(())
45 }
46 }
47 }
48
49 pub(crate) fn coerce_extended(&mut self) -> Option<()> {
52 match self {
53 Self::Basic => None,
54 Self::Extended => Some(()),
55 Self::Unknown => {
56 *self = Self::Extended;
57 Some(())
58 }
59 }
60 }
61}
62
63pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
65 Some(match sign(input) {
66 Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| {
67 let val = val.cast_signed();
68 if sign == b'-' { -val } else { val }
69 }),
70 None => exactly_n_digits::<4, u32>(input)?.map(|val| val.cast_signed()),
71 })
72}
73
74pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
76 first_match(
77 [
78 (b"01".as_slice(), Month::January),
79 (b"02".as_slice(), Month::February),
80 (b"03".as_slice(), Month::March),
81 (b"04".as_slice(), Month::April),
82 (b"05".as_slice(), Month::May),
83 (b"06".as_slice(), Month::June),
84 (b"07".as_slice(), Month::July),
85 (b"08".as_slice(), Month::August),
86 (b"09".as_slice(), Month::September),
87 (b"10".as_slice(), Month::October),
88 (b"11".as_slice(), Month::November),
89 (b"12".as_slice(), Month::December),
90 ],
91 true,
92 )(input)
93}
94
95pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> {
97 exactly_n_digits::<2, _>(input)
98}
99
100pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> {
102 exactly_n_digits::<2, _>(input)
103}
104
105pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> {
107 first_match(
108 [
109 (b"1".as_slice(), Weekday::Monday),
110 (b"2".as_slice(), Weekday::Tuesday),
111 (b"3".as_slice(), Weekday::Wednesday),
112 (b"4".as_slice(), Weekday::Thursday),
113 (b"5".as_slice(), Weekday::Friday),
114 (b"6".as_slice(), Weekday::Saturday),
115 (b"7".as_slice(), Weekday::Sunday),
116 ],
117 true,
118 )(input)
119}
120
121pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU16>> {
123 exactly_n_digits::<3, _>(input)
124}
125
126pub(crate) fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
128 exactly_n_digits::<2, _>(input)
129}
130
131pub(crate) fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
133 exactly_n_digits::<2, _>(input)
134}
135
136pub(crate) fn float(input: &[u8]) -> Option<ParsedItem<'_, (u8, Option<f64>)>> {
143 let ParsedItem(input, integer_part) = match input {
145 [
146 first_digit @ b'0'..=b'9',
147 second_digit @ b'0'..=b'9',
148 input @ ..,
149 ] => ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0')),
150 _ => return None,
151 };
152
153 if let Some(ParsedItem(input, ())) = decimal_sign(input) {
154 let ParsedItem(mut input, mut fractional_part) =
156 any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.);
157
158 let mut divisor = 10.;
159 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
161 input = new_input;
162 divisor *= 10.;
163 fractional_part += (digit - b'0') as f64 / divisor;
164 }
165
166 Some(ParsedItem(input, (integer_part, Some(fractional_part))))
167 } else {
168 Some(ParsedItem(input, (integer_part, None)))
169 }
170}
171
172fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
174 ascii_char::<b'.'>(input).or_else(|| ascii_char::<b','>(input))
175}