1use crate::convert::*;
4use crate::error;
5use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
6use crate::format_description::well_known::Iso8601;
7use crate::format_description::well_known::iso8601::EncodedConfig;
8use crate::internal_macros::try_likely_ok;
9use crate::parsing::combinator::rfc::iso8601::{
10 ExtendedKind, day, dayk, dayo, float, hour, min, month, week, year,
11};
12use crate::parsing::combinator::{Sign, ascii_char, sign};
13use crate::parsing::{Parsed, ParsedItem};
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 if date_is_present {
126 input =
127 try_likely_ok!(ascii_char::<b'T'>(input).ok_or(InvalidLiteral)).into_inner();
128 }
129
130 let ParsedItem(mut input, hour) =
131 try_likely_ok!(float(input).ok_or(InvalidComponent("hour")));
132 match hour {
133 (hour, None) => {
134 try_likely_ok!(parsed.set_hour_24(hour).ok_or(InvalidComponent("hour")))
135 }
136 (hour, Some(fractional_part)) => {
137 *parsed = try_likely_ok!(
138 try_likely_ok!(
139 try_likely_ok!(
140 try_likely_ok!(
141 parsed.with_hour_24(hour).ok_or(InvalidComponent("hour"))
142 )
143 .with_minute((fractional_part * Second::per_t::<f64>(Minute)) as u8)
144 .ok_or(InvalidComponent("minute"))
145 )
146 .with_second(
147 (fractional_part * Second::per_t::<f64>(Hour)
148 % Minute::per_t::<f64>(Hour))
149 as u8,
150 )
151 .ok_or(InvalidComponent("second"))
152 )
153 .with_subsecond(
154 (fractional_part * Nanosecond::per_t::<f64>(Hour)
155 % Nanosecond::per_t::<f64>(Second))
156 as u32,
157 )
158 .ok_or(InvalidComponent("subsecond"))
159 );
160 return Ok(input);
161 }
162 };
163
164 if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
165 try_likely_ok!(
166 extended_kind
167 .coerce_extended()
168 .ok_or(InvalidComponent("minute"))
169 );
170 input = new_input;
171 };
172
173 let mut input = match float(input) {
174 Some(ParsedItem(input, (minute, None))) => {
175 extended_kind.coerce_basic();
176 try_likely_ok!(parsed.set_minute(minute).ok_or(InvalidComponent("minute")));
177 input
178 }
179 Some(ParsedItem(input, (minute, Some(fractional_part)))) => {
180 extended_kind.coerce_basic();
182 *parsed = try_likely_ok!(
183 try_likely_ok!(
184 try_likely_ok!(
185 parsed.with_minute(minute).ok_or(InvalidComponent("minute"))
186 )
187 .with_second((fractional_part * Second::per_t::<f64>(Minute)) as u8)
188 .ok_or(InvalidComponent("second"))
189 )
190 .with_subsecond(
191 (fractional_part * Nanosecond::per_t::<f64>(Minute)
192 % Nanosecond::per_t::<f64>(Second))
193 as u32,
194 )
195 .ok_or(InvalidComponent("subsecond"))
196 );
197 return Ok(input);
198 }
199 None if extended_kind.is_extended() => {
201 return Err(error::Parse::ParseFromDescription(InvalidComponent(
202 "minute",
203 )));
204 }
205 None => {
206 *parsed = try_likely_ok!(
208 try_likely_ok!(
209 try_likely_ok!(parsed.with_minute(0).ok_or(InvalidComponent("minute")))
210 .with_second(0)
211 .ok_or(InvalidComponent("second"))
212 )
213 .with_subsecond(0)
214 .ok_or(InvalidComponent("subsecond"))
215 );
216 return Ok(input);
217 }
218 };
219
220 if extended_kind.is_extended() {
221 match ascii_char::<b':'>(input) {
222 Some(ParsedItem(new_input, ())) => input = new_input,
223 None => {
224 *parsed = try_likely_ok!(
225 try_likely_ok!(parsed.with_second(0).ok_or(InvalidComponent("second")))
226 .with_subsecond(0)
227 .ok_or(InvalidComponent("subsecond"))
228 );
229 return Ok(input);
230 }
231 }
232 }
233
234 let (input, second, subsecond) = match float(input) {
235 Some(ParsedItem(input, (second, None))) => (input, second, 0),
236 Some(ParsedItem(input, (second, Some(fractional_part)))) => (
237 input,
238 second,
239 round(fractional_part * Nanosecond::per_t::<f64>(Second)) as u32,
240 ),
241 None if extended_kind.is_extended() => {
242 return Err(error::Parse::ParseFromDescription(InvalidComponent(
243 "second",
244 )));
245 }
246 None => (input, 0, 0),
248 };
249 *parsed = try_likely_ok!(
250 try_likely_ok!(parsed.with_second(second).ok_or(InvalidComponent("second")))
251 .with_subsecond(subsecond)
252 .ok_or(InvalidComponent("subsecond"))
253 );
254
255 Ok(input)
256 }
257 }
258
259 pub(crate) fn parse_offset<'a>(
264 parsed: &'a mut Parsed,
265 extended_kind: &'a mut ExtendedKind,
266 ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + use<'a, CONFIG> {
267 move |input| {
268 if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) {
269 *parsed = try_likely_ok!(
270 try_likely_ok!(
271 try_likely_ok!(
272 parsed
273 .with_offset_hour(0)
274 .ok_or(InvalidComponent("offset hour"))
275 )
276 .with_offset_minute_signed(0)
277 .ok_or(InvalidComponent("offset minute"))
278 )
279 .with_offset_second_signed(0)
280 .ok_or(InvalidComponent("offset second"))
281 );
282 return Ok(input);
283 }
284
285 let ParsedItem(input, sign) =
286 try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
287 let mut input = try_likely_ok!(
288 hour(input)
289 .and_then(|parsed_item| {
290 parsed_item.consume_value(|hour| {
291 parsed.set_offset_hour(match sign {
292 Sign::Negative => -hour.cast_signed(),
293 Sign::Positive => hour.cast_signed(),
294 })
295 })
296 })
297 .ok_or(InvalidComponent("offset hour"))
298 );
299
300 if extended_kind.maybe_extended()
301 && let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input)
302 {
303 try_likely_ok!(
304 extended_kind
305 .coerce_extended()
306 .ok_or(InvalidComponent("offset minute"))
307 );
308 input = new_input;
309 };
310
311 match min(input) {
312 Some(ParsedItem(new_input, min)) => {
313 input = new_input;
314 try_likely_ok!(
315 parsed
316 .set_offset_minute_signed(match sign {
317 Sign::Negative => -min.cast_signed(),
318 Sign::Positive => min.cast_signed(),
319 })
320 .ok_or(InvalidComponent("offset minute"))
321 );
322 }
323 None => {
324 parsed.set_offset_minute_signed(0);
326 }
327 }
328
329 extended_kind.coerce_basic();
334
335 Ok(input)
336 }
337 }
338}
339
340#[inline]
343fn round(value: f64) -> f64 {
344 #[cfg(feature = "std")]
345 {
346 value.round()
347 }
348 #[cfg(not(feature = "std"))]
349 {
350 debug_assert!(value.is_sign_positive() && !value.is_nan());
351
352 let f = value % 1.;
353 if f < 0.5 { value - f } else { value - f + 1. }
354 }
355}