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 _)
133 .ok_or(InvalidComponent("minute"))?
134 .with_second(
135 (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64)
136 as _,
137 )
138 .ok_or(InvalidComponent("second"))?
139 .with_subsecond(
140 (fractional_part * Nanosecond::per(Hour) as f64
141 % Nanosecond::per(Second) as f64) as _,
142 )
143 .ok_or(InvalidComponent("subsecond"))?;
144 return Ok(input);
145 }
146 };
147
148 if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
149 extended_kind
150 .coerce_extended()
151 .ok_or(InvalidComponent("minute"))?;
152 input = new_input;
153 };
154
155 let mut input = match float(input) {
156 Some(ParsedItem(input, (minute, None))) => {
157 extended_kind.coerce_basic();
158 parsed
159 .set_minute(minute)
160 .ok_or(InvalidComponent("minute"))?;
161 input
162 }
163 Some(ParsedItem(input, (minute, Some(fractional_part)))) => {
164 extended_kind.coerce_basic();
166 *parsed = parsed
167 .with_minute(minute)
168 .ok_or(InvalidComponent("minute"))?
169 .with_second((fractional_part * Second::per(Minute) as f64) as _)
170 .ok_or(InvalidComponent("second"))?
171 .with_subsecond(
172 (fractional_part * Nanosecond::per(Minute) as f64
173 % Nanosecond::per(Second) as f64) as _,
174 )
175 .ok_or(InvalidComponent("subsecond"))?;
176 return Ok(input);
177 }
178 None if extended_kind.is_extended() => {
180 return Err(error::Parse::ParseFromDescription(InvalidComponent(
181 "minute",
182 )));
183 }
184 None => {
185 *parsed = parsed
187 .with_minute(0)
188 .ok_or(InvalidComponent("minute"))?
189 .with_second(0)
190 .ok_or(InvalidComponent("second"))?
191 .with_subsecond(0)
192 .ok_or(InvalidComponent("subsecond"))?;
193 return Ok(input);
194 }
195 };
196
197 if extended_kind.is_extended() {
198 match ascii_char::<b':'>(input) {
199 Some(ParsedItem(new_input, ())) => input = new_input,
200 None => {
201 *parsed = parsed
202 .with_second(0)
203 .ok_or(InvalidComponent("second"))?
204 .with_subsecond(0)
205 .ok_or(InvalidComponent("subsecond"))?;
206 return Ok(input);
207 }
208 }
209 }
210
211 let (input, second, subsecond) = match float(input) {
212 Some(ParsedItem(input, (second, None))) => (input, second, 0),
213 Some(ParsedItem(input, (second, Some(fractional_part)))) => (
214 input,
215 second,
216 round(fractional_part * Nanosecond::per(Second) as f64) as _,
217 ),
218 None if extended_kind.is_extended() => {
219 return Err(error::Parse::ParseFromDescription(InvalidComponent(
220 "second",
221 )));
222 }
223 None => (input, 0, 0),
225 };
226 *parsed = parsed
227 .with_second(second)
228 .ok_or(InvalidComponent("second"))?
229 .with_subsecond(subsecond)
230 .ok_or(InvalidComponent("subsecond"))?;
231
232 Ok(input)
233 }
234 }
235
236 pub(crate) fn parse_offset<'a>(
241 parsed: &'a mut Parsed,
242 extended_kind: &'a mut ExtendedKind,
243 ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
244 move |input| {
245 if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) {
246 *parsed = parsed
247 .with_offset_hour(0)
248 .ok_or(InvalidComponent("offset hour"))?
249 .with_offset_minute_signed(0)
250 .ok_or(InvalidComponent("offset minute"))?
251 .with_offset_second_signed(0)
252 .ok_or(InvalidComponent("offset second"))?;
253 return Ok(input);
254 }
255
256 let ParsedItem(input, sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
257 let mut input = hour(input)
258 .and_then(|parsed_item| {
259 parsed_item.consume_value(|hour| {
260 parsed.set_offset_hour(if sign == b'-' {
261 -hour.cast_signed()
262 } else {
263 hour.cast_signed()
264 })
265 })
266 })
267 .ok_or(InvalidComponent("offset hour"))?;
268
269 if extended_kind.maybe_extended() {
270 if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
271 extended_kind
272 .coerce_extended()
273 .ok_or(InvalidComponent("offset minute"))?;
274 input = new_input;
275 };
276 }
277
278 match min(input) {
279 Some(ParsedItem(new_input, min)) => {
280 input = new_input;
281 parsed
282 .set_offset_minute_signed(if sign == b'-' {
283 -min.cast_signed()
284 } else {
285 min.cast_signed()
286 })
287 .ok_or(InvalidComponent("offset minute"))?;
288 }
289 None => {
290 parsed.set_offset_minute_signed(0);
292 }
293 }
294
295 extended_kind.coerce_basic();
300
301 Ok(input)
302 }
303 }
304}
305
306fn round(value: f64) -> f64 {
309 #[cfg(feature = "std")]
310 {
311 value.round()
312 }
313 #[cfg(not(feature = "std"))]
314 {
315 round_impl(value)
316 }
317}
318
319#[cfg(not(feature = "std"))]
320#[allow(clippy::missing_docs_in_private_items)]
321fn round_impl(value: f64) -> f64 {
322 debug_assert!(value.is_sign_positive() && !value.is_nan());
323
324 let f = value % 1.;
325 if f < 0.5 {
326 value - f
327 } else {
328 value - f + 1.
329 }
330}