time/parsing/
iso8601.rs
1use num_conv::prelude::*;
4
5use crate::convert::*;
6use crate::error;
7use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
8use crate::format_description::well_known::iso8601::EncodedConfig;
9use crate::format_description::well_known::Iso8601;
10use crate::parsing::combinator::rfc::iso8601::{
11 day, dayk, dayo, float, hour, min, month, week, year, ExtendedKind,
12};
13use crate::parsing::combinator::{ascii_char, sign};
14use crate::parsing::{Parsed, ParsedItem};
15
16impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
17 pub(crate) fn parse_date<'a>(
25 parsed: &'a mut Parsed,
26 extended_kind: &'a mut ExtendedKind,
27 ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
28 move |input| {
29 let ParsedItem(mut input, year) = year(input).ok_or(InvalidComponent("year"))?;
31 *extended_kind = match ascii_char::<b'-'>(input) {
32 Some(ParsedItem(new_input, ())) => {
33 input = new_input;
34 ExtendedKind::Extended
35 }
36 None => ExtendedKind::Basic, };
38
39 let parsed_month_day = (|| {
40 let ParsedItem(mut input, month) = month(input).ok_or(InvalidComponent("month"))?;
41 if extended_kind.is_extended() {
42 input = ascii_char::<b'-'>(input)
43 .ok_or(InvalidLiteral)?
44 .into_inner();
45 }
46 let ParsedItem(input, day) = day(input).ok_or(InvalidComponent("day"))?;
47 Ok(ParsedItem(input, (month, day)))
48 })();
49 let mut ret_error = match parsed_month_day {
50 Ok(ParsedItem(input, (month, day))) => {
51 *parsed = parsed
52 .with_year(year)
53 .ok_or(InvalidComponent("year"))?
54 .with_month(month)
55 .ok_or(InvalidComponent("month"))?
56 .with_day(day)
57 .ok_or(InvalidComponent("day"))?;
58 return Ok(input);
59 }
60 Err(err) => err,
61 };
62
63 if let Some(ParsedItem(input, ordinal)) = dayo(input) {
65 *parsed = parsed
66 .with_year(year)
67 .ok_or(InvalidComponent("year"))?
68 .with_ordinal(ordinal)
69 .ok_or(InvalidComponent("ordinal"))?;
70 return Ok(input);
71 }
72
73 let parsed_week_weekday = (|| {
74 let input = ascii_char::<b'W'>(input)
75 .ok_or((false, InvalidLiteral))?
76 .into_inner();
77 let ParsedItem(mut input, week) =
78 week(input).ok_or((true, InvalidComponent("week")))?;
79 if extended_kind.is_extended() {
80 input = ascii_char::<b'-'>(input)
81 .ok_or((true, InvalidLiteral))?
82 .into_inner();
83 }
84 let ParsedItem(input, weekday) =
85 dayk(input).ok_or((true, InvalidComponent("weekday")))?;
86 Ok(ParsedItem(input, (week, weekday)))
87 })();
88 match parsed_week_weekday {
89 Ok(ParsedItem(input, (week, weekday))) => {
90 *parsed = parsed
91 .with_iso_year(year)
92 .ok_or(InvalidComponent("year"))?
93 .with_iso_week_number(week)
94 .ok_or(InvalidComponent("week"))?
95 .with_weekday(weekday)
96 .ok_or(InvalidComponent("weekday"))?;
97 return Ok(input);
98 }
99 Err((false, _err)) => {}
100 Err((true, err)) => ret_error = err,
102 }
103
104 Err(ret_error.into())
105 }
106 }
107
108 pub(crate) fn parse_time<'a>(
114 parsed: &'a mut Parsed,
115 extended_kind: &'a mut ExtendedKind,
116 date_is_present: bool,
117 ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
118 move |mut input| {
119 if date_is_present {
120 input = ascii_char::<b'T'>(input)
121 .ok_or(InvalidLiteral)?
122 .into_inner();
123 }
124
125 let ParsedItem(mut input, hour) = float(input).ok_or(InvalidComponent("hour"))?;
126 match hour {
127 (hour, None) => parsed.set_hour_24(hour).ok_or(InvalidComponent("hour"))?,
128 (hour, Some(fractional_part)) => {
129 *parsed = parsed
130 .with_hour_24(hour)
131 .ok_or(InvalidComponent("hour"))?
132 .with_minute((fractional_part * Second::per(Minute) as f64) as u8)
133 .ok_or(InvalidComponent("minute"))?
134 .with_second(
135 (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64)
136 as u8,
137 )
138 .ok_or(InvalidComponent("second"))?
139 .with_subsecond(
140 (fractional_part * Nanosecond::per(Hour) as f64
141 % Nanosecond::per(Second) as f64)
142 as u32,
143 )
144 .ok_or(InvalidComponent("subsecond"))?;
145 return Ok(input);
146 }
147 };
148
149 if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
150 extended_kind
151 .coerce_extended()
152 .ok_or(InvalidComponent("minute"))?;
153 input = new_input;
154 };
155
156 let mut input = match float(input) {
157 Some(ParsedItem(input, (minute, None))) => {
158 extended_kind.coerce_basic();
159 parsed
160 .set_minute(minute)
161 .ok_or(InvalidComponent("minute"))?;
162 input
163 }
164 Some(ParsedItem(input, (minute, Some(fractional_part)))) => {
165 extended_kind.coerce_basic();
167 *parsed = parsed
168 .with_minute(minute)
169 .ok_or(InvalidComponent("minute"))?
170 .with_second((fractional_part * Second::per(Minute) as f64) as u8)
171 .ok_or(InvalidComponent("second"))?
172 .with_subsecond(
173 (fractional_part * Nanosecond::per(Minute) as f64
174 % Nanosecond::per(Second) as f64)
175 as u32,
176 )
177 .ok_or(InvalidComponent("subsecond"))?;
178 return Ok(input);
179 }
180 None if extended_kind.is_extended() => {
182 return Err(error::Parse::ParseFromDescription(InvalidComponent(
183 "minute",
184 )));
185 }
186 None => {
187 *parsed = parsed
189 .with_minute(0)
190 .ok_or(InvalidComponent("minute"))?
191 .with_second(0)
192 .ok_or(InvalidComponent("second"))?
193 .with_subsecond(0)
194 .ok_or(InvalidComponent("subsecond"))?;
195 return Ok(input);
196 }
197 };
198
199 if extended_kind.is_extended() {
200 match ascii_char::<b':'>(input) {
201 Some(ParsedItem(new_input, ())) => input = new_input,
202 None => {
203 *parsed = parsed
204 .with_second(0)
205 .ok_or(InvalidComponent("second"))?
206 .with_subsecond(0)
207 .ok_or(InvalidComponent("subsecond"))?;
208 return Ok(input);
209 }
210 }
211 }
212
213 let (input, second, subsecond) = match float(input) {
214 Some(ParsedItem(input, (second, None))) => (input, second, 0),
215 Some(ParsedItem(input, (second, Some(fractional_part)))) => (
216 input,
217 second,
218 round(fractional_part * Nanosecond::per(Second) as f64) as u32,
219 ),
220 None if extended_kind.is_extended() => {
221 return Err(error::Parse::ParseFromDescription(InvalidComponent(
222 "second",
223 )));
224 }
225 None => (input, 0, 0),
227 };
228 *parsed = parsed
229 .with_second(second)
230 .ok_or(InvalidComponent("second"))?
231 .with_subsecond(subsecond)
232 .ok_or(InvalidComponent("subsecond"))?;
233
234 Ok(input)
235 }
236 }
237
238 pub(crate) fn parse_offset<'a>(
243 parsed: &'a mut Parsed,
244 extended_kind: &'a mut ExtendedKind,
245 ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
246 move |input| {
247 if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) {
248 *parsed = parsed
249 .with_offset_hour(0)
250 .ok_or(InvalidComponent("offset hour"))?
251 .with_offset_minute_signed(0)
252 .ok_or(InvalidComponent("offset minute"))?
253 .with_offset_second_signed(0)
254 .ok_or(InvalidComponent("offset second"))?;
255 return Ok(input);
256 }
257
258 let ParsedItem(input, sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
259 let mut input = hour(input)
260 .and_then(|parsed_item| {
261 parsed_item.consume_value(|hour| {
262 parsed.set_offset_hour(if sign == b'-' {
263 -hour.cast_signed()
264 } else {
265 hour.cast_signed()
266 })
267 })
268 })
269 .ok_or(InvalidComponent("offset hour"))?;
270
271 if extended_kind.maybe_extended() {
272 if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
273 extended_kind
274 .coerce_extended()
275 .ok_or(InvalidComponent("offset minute"))?;
276 input = new_input;
277 };
278 }
279
280 match min(input) {
281 Some(ParsedItem(new_input, min)) => {
282 input = new_input;
283 parsed
284 .set_offset_minute_signed(if sign == b'-' {
285 -min.cast_signed()
286 } else {
287 min.cast_signed()
288 })
289 .ok_or(InvalidComponent("offset minute"))?;
290 }
291 None => {
292 parsed.set_offset_minute_signed(0);
294 }
295 }
296
297 extended_kind.coerce_basic();
302
303 Ok(input)
304 }
305 }
306}
307
308fn round(value: f64) -> f64 {
311 #[cfg(feature = "std")]
312 {
313 value.round()
314 }
315 #[cfg(not(feature = "std"))]
316 {
317 round_impl(value)
318 }
319}
320
321#[cfg(not(feature = "std"))]
322#[allow(clippy::missing_docs_in_private_items)]
323fn round_impl(value: f64) -> f64 {
324 debug_assert!(value.is_sign_positive() && !value.is_nan());
325
326 let f = value % 1.;
327 if f < 0.5 {
328 value - f
329 } else {
330 value - f + 1.
331 }
332}