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'-' {
69 -val
70 } else {
71 val
72 }
73 }),
74 None => exactly_n_digits::<4, u32>(input)?.map(|val| val.cast_signed()),
75 })
76}
77
78pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
80 first_match(
81 [
82 (b"01".as_slice(), Month::January),
83 (b"02".as_slice(), Month::February),
84 (b"03".as_slice(), Month::March),
85 (b"04".as_slice(), Month::April),
86 (b"05".as_slice(), Month::May),
87 (b"06".as_slice(), Month::June),
88 (b"07".as_slice(), Month::July),
89 (b"08".as_slice(), Month::August),
90 (b"09".as_slice(), Month::September),
91 (b"10".as_slice(), Month::October),
92 (b"11".as_slice(), Month::November),
93 (b"12".as_slice(), Month::December),
94 ],
95 true,
96 )(input)
97}
98
99pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> {
101 exactly_n_digits::<2, _>(input)
102}
103
104pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> {
106 exactly_n_digits::<2, _>(input)
107}
108
109pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> {
111 first_match(
112 [
113 (b"1".as_slice(), Weekday::Monday),
114 (b"2".as_slice(), Weekday::Tuesday),
115 (b"3".as_slice(), Weekday::Wednesday),
116 (b"4".as_slice(), Weekday::Thursday),
117 (b"5".as_slice(), Weekday::Friday),
118 (b"6".as_slice(), Weekday::Saturday),
119 (b"7".as_slice(), Weekday::Sunday),
120 ],
121 true,
122 )(input)
123}
124
125pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU16>> {
127 exactly_n_digits::<3, _>(input)
128}
129
130pub(crate) fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
132 exactly_n_digits::<2, _>(input)
133}
134
135pub(crate) fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
137 exactly_n_digits::<2, _>(input)
138}
139
140pub(crate) fn float(input: &[u8]) -> Option<ParsedItem<'_, (u8, Option<f64>)>> {
147 let ParsedItem(input, integer_part) = match input {
149 [first_digit @ b'0'..=b'9', second_digit @ b'0'..=b'9', input @ ..] => {
150 ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0'))
151 }
152 _ => return None,
153 };
154
155 if let Some(ParsedItem(input, ())) = decimal_sign(input) {
156 let ParsedItem(mut input, mut fractional_part) =
158 any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.);
159
160 let mut divisor = 10.;
161 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
163 input = new_input;
164 divisor *= 10.;
165 fractional_part += (digit - b'0') as f64 / divisor;
166 }
167
168 Some(ParsedItem(input, (integer_part, Some(fractional_part))))
169 } else {
170 Some(ParsedItem(input, (integer_part, None)))
171 }
172}
173
174fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
176 ascii_char::<b'.'>(input).or_else(|| ascii_char::<b','>(input))
177}