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