1use core::num::NonZero;
4use core::ops::Deref;
5
6use num_conv::prelude::*;
7
8use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
9use crate::error::TryFromParsed;
10#[cfg(feature = "alloc")]
11use crate::format_description::OwnedFormatItem;
12use crate::format_description::well_known::iso8601::EncodedConfig;
13use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
14use crate::format_description::{BorrowedFormatItem, FormatDescriptionV3, modifier};
15use crate::internal_macros::{bug, try_likely_ok};
16use crate::parsing::combinator::{
17 ExactlyNDigits, Sign, any_digit, ascii_char, ascii_char_ignore_case, one_or_two_digits, opt,
18 sign,
19};
20use crate::parsing::{Parsed, ParsedItem, component};
21use crate::{Date, Month, OffsetDateTime, Time, UtcOffset, error};
22
23#[cfg_attr(docsrs, doc(notable_trait))]
25#[doc(alias = "Parseable")]
26pub trait Parsable: sealed::Sealed {}
27impl Parsable for FormatDescriptionV3<'_> {}
28impl Parsable for BorrowedFormatItem<'_> {}
29impl Parsable for [BorrowedFormatItem<'_>] {}
30#[cfg(feature = "alloc")]
31impl Parsable for OwnedFormatItem {}
32#[cfg(feature = "alloc")]
33impl Parsable for [OwnedFormatItem] {}
34impl Parsable for Rfc2822 {}
35impl Parsable for Rfc3339 {}
36impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {}
37impl<T> Parsable for T where T: Deref<Target: Parsable> {}
38
39mod sealed {
42 use super::*;
43 use crate::{PrimitiveDateTime, Timestamp, UtcDateTime};
44
45 pub trait Sealed {
47 fn parse_into<'a>(
51 &self,
52 input: &'a [u8],
53 parsed: &mut Parsed,
54 ) -> Result<&'a [u8], error::Parse>;
55
56 #[inline]
61 fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
62 let mut parsed = Parsed::new();
63 if self.parse_into(input, &mut parsed)?.is_empty() {
64 Ok(parsed)
65 } else {
66 Err(error::Parse::ParseFromDescription(
67 error::ParseFromDescription::UnexpectedTrailingCharacters,
68 ))
69 }
70 }
71
72 #[inline]
74 fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
75 Ok(self.parse(input)?.try_into()?)
76 }
77
78 #[inline]
80 fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
81 Ok(self.parse(input)?.try_into()?)
82 }
83
84 #[inline]
86 fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
87 Ok(self.parse(input)?.try_into()?)
88 }
89
90 #[inline]
92 fn parse_primitive_date_time(
93 &self,
94 input: &[u8],
95 ) -> Result<PrimitiveDateTime, error::Parse> {
96 Ok(self.parse(input)?.try_into()?)
97 }
98
99 #[inline]
101 fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> {
102 Ok(self.parse(input)?.try_into()?)
103 }
104
105 #[inline]
107 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
108 Ok(self.parse(input)?.try_into()?)
109 }
110
111 #[inline]
113 fn parse_timestamp(&self, input: &[u8]) -> Result<Timestamp, error::Parse> {
114 Ok(self.parse(input)?.try_into()?)
115 }
116 }
117}
118
119impl sealed::Sealed for FormatDescriptionV3<'_> {
120 #[inline]
121 fn parse_into<'a>(
122 &self,
123 input: &'a [u8],
124 parsed: &mut Parsed,
125 ) -> Result<&'a [u8], error::Parse> {
126 Ok(parsed.parse_v3_inner(input, &self.inner)?)
127 }
128}
129
130impl sealed::Sealed for BorrowedFormatItem<'_> {
131 #[inline]
132 fn parse_into<'a>(
133 &self,
134 input: &'a [u8],
135 parsed: &mut Parsed,
136 ) -> Result<&'a [u8], error::Parse> {
137 Ok(parsed.parse_item(input, self)?)
138 }
139}
140
141impl sealed::Sealed for [BorrowedFormatItem<'_>] {
142 #[inline]
143 fn parse_into<'a>(
144 &self,
145 input: &'a [u8],
146 parsed: &mut Parsed,
147 ) -> Result<&'a [u8], error::Parse> {
148 Ok(parsed.parse_items(input, self)?)
149 }
150}
151
152#[cfg(feature = "alloc")]
153impl sealed::Sealed for OwnedFormatItem {
154 #[inline]
155 fn parse_into<'a>(
156 &self,
157 input: &'a [u8],
158 parsed: &mut Parsed,
159 ) -> Result<&'a [u8], error::Parse> {
160 Ok(parsed.parse_item(input, self)?)
161 }
162}
163
164#[cfg(feature = "alloc")]
165impl sealed::Sealed for [OwnedFormatItem] {
166 #[inline]
167 fn parse_into<'a>(
168 &self,
169 input: &'a [u8],
170 parsed: &mut Parsed,
171 ) -> Result<&'a [u8], error::Parse> {
172 Ok(parsed.parse_items(input, self)?)
173 }
174}
175
176impl<T> sealed::Sealed for T
177where
178 T: Deref<Target: sealed::Sealed>,
179{
180 #[inline]
181 fn parse_into<'a>(
182 &self,
183 input: &'a [u8],
184 parsed: &mut Parsed,
185 ) -> Result<&'a [u8], error::Parse> {
186 self.deref().parse_into(input, parsed)
187 }
188}
189
190impl sealed::Sealed for Rfc2822 {
191 fn parse_into<'a>(
192 &self,
193 input: &'a [u8],
194 parsed: &mut Parsed,
195 ) -> Result<&'a [u8], error::Parse> {
196 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws, zone_literal};
197
198 let colon = ascii_char::<b':'>;
199 let comma = ascii_char::<b','>;
200
201 let input = opt(cfws)(input).into_inner();
202 let weekday = component::parse_weekday_short(
203 input,
204 modifier::WeekdayShort {
205 case_sensitive: false,
206 },
207 );
208 let input = if let Some(item) = weekday {
209 let input = try_likely_ok!(
210 item.consume_value(|value| parsed.set_weekday(value))
211 .ok_or(InvalidComponent("weekday"))
212 );
213 let input = try_likely_ok!(comma(input).ok_or(InvalidLiteral)).into_inner();
214 opt(cfws)(input).into_inner()
215 } else {
216 input
217 };
218 let input = try_likely_ok!(
219 one_or_two_digits(input)
220 .and_then(|item| item.consume_value(|value| parsed.set_day(NonZero::new(value)?)))
221 .ok_or(InvalidComponent("day"))
222 );
223 let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
224 let input = try_likely_ok!(
225 component::parse_month_short(
226 input,
227 modifier::MonthShort {
228 case_sensitive: false,
229 },
230 )
231 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
232 .ok_or(InvalidComponent("month"))
233 );
234 let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
235 let input = match ExactlyNDigits::<4>::parse(input) {
236 Some(item) => {
237 let input = try_likely_ok!(
238 item.flat_map(|year| if year >= 1900 { Some(year) } else { None })
239 .and_then(|item| {
240 item.consume_value(|value| parsed.set_year(value.cast_signed().widen()))
241 })
242 .ok_or(InvalidComponent("year"))
243 );
244 try_likely_ok!(fws(input).ok_or(InvalidLiteral)).into_inner()
245 }
246 None => {
247 let input = try_likely_ok!(
248 ExactlyNDigits::<2>::parse(input)
249 .and_then(|item| {
250 item.map(|year| year.widen::<u32>())
251 .map(|year| if year < 50 { year + 2000 } else { year + 1900 })
252 .map(|year| year.cast_signed())
253 .consume_value(|value| parsed.set_year(value))
254 })
255 .ok_or(InvalidComponent("year"))
256 );
257 try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner()
258 }
259 };
260
261 let input = try_likely_ok!(
262 ExactlyNDigits::<2>::parse(input)
263 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
264 .ok_or(InvalidComponent("hour"))
265 );
266 let input = opt(cfws)(input).into_inner();
267 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
268 let input = opt(cfws)(input).into_inner();
269 let input = try_likely_ok!(
270 ExactlyNDigits::<2>::parse(input)
271 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
272 .ok_or(InvalidComponent("minute"))
273 );
274
275 let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
276 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
278 let input = try_likely_ok!(
279 ExactlyNDigits::<2>::parse(input)
280 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
281 .ok_or(InvalidComponent("second"))
282 );
283 try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner()
284 } else {
285 try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner()
286 };
287
288 parsed.leap_second_allowed = true;
290
291 if let Some(zone_literal) = zone_literal(input) {
292 crate::hint::cold_path();
293 let input = try_likely_ok!(
294 zone_literal
295 .consume_value(|value| parsed.set_offset_hour(value))
296 .ok_or(InvalidComponent("offset hour"))
297 );
298 try_likely_ok!(
299 parsed
300 .set_offset_minute_signed(0)
301 .ok_or(InvalidComponent("offset minute"))
302 );
303 try_likely_ok!(
304 parsed
305 .set_offset_second_signed(0)
306 .ok_or(InvalidComponent("offset second"))
307 );
308 return Ok(input);
309 }
310
311 let ParsedItem(input, offset_sign) =
312 try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
313 let input = try_likely_ok!(
314 ExactlyNDigits::<2>::parse(input)
315 .and_then(|item| {
316 item.map(|offset_hour| match offset_sign {
317 Sign::Negative => -offset_hour.cast_signed(),
318 Sign::Positive => offset_hour.cast_signed(),
319 })
320 .consume_value(|value| parsed.set_offset_hour(value))
321 })
322 .ok_or(InvalidComponent("offset hour"))
323 );
324 let input = try_likely_ok!(
325 ExactlyNDigits::<2>::parse(input)
326 .and_then(|item| {
327 item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
328 })
329 .ok_or(InvalidComponent("offset minute"))
330 );
331
332 let input = opt(cfws)(input).into_inner();
333
334 Ok(input)
335 }
336
337 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
338 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws, zone_literal};
339
340 let colon = ascii_char::<b':'>;
341 let comma = ascii_char::<b','>;
342
343 let input = opt(cfws)(input).into_inner();
344 let weekday = component::parse_weekday_short(
345 input,
346 modifier::WeekdayShort {
347 case_sensitive: false,
348 },
349 );
350 let input = if let Some(item) = weekday {
351 let input = item.discard_value();
352 let input = try_likely_ok!(comma(input).ok_or(InvalidLiteral)).into_inner();
353 opt(cfws)(input).into_inner()
354 } else {
355 input
356 };
357 let ParsedItem(input, day) =
358 try_likely_ok!(one_or_two_digits(input).ok_or(InvalidComponent("day")));
359 let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
360 let ParsedItem(input, month) = try_likely_ok!(
361 component::parse_month_short(
362 input,
363 modifier::MonthShort {
364 case_sensitive: false,
365 },
366 )
367 .ok_or(InvalidComponent("month"))
368 );
369 let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
370 let (input, year) = match ExactlyNDigits::<4>::parse(input) {
371 Some(item) => {
372 let ParsedItem(input, year) = try_likely_ok!(
373 item.flat_map(|year| if year >= 1900 { Some(year) } else { None })
374 .ok_or(InvalidComponent("year"))
375 );
376 let input = try_likely_ok!(fws(input).ok_or(InvalidLiteral)).into_inner();
377 (input, year)
378 }
379 None => {
380 let ParsedItem(input, year) = try_likely_ok!(
381 ExactlyNDigits::<2>::parse(input)
382 .map(|item| {
383 item.map(|year| year.widen::<u16>())
384 .map(|year| if year < 50 { year + 2000 } else { year + 1900 })
385 })
386 .ok_or(InvalidComponent("year"))
387 );
388 let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
389 (input, year)
390 }
391 };
392
393 let ParsedItem(input, hour) =
394 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("hour")));
395 let input = opt(cfws)(input).into_inner();
396 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
397 let input = opt(cfws)(input).into_inner();
398 let ParsedItem(input, minute) =
399 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("minute")));
400
401 let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
402 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
404 let ParsedItem(input, second) =
405 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("second")));
406 let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
407 (input, second)
408 } else {
409 (
410 try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner(),
411 0,
412 )
413 };
414
415 let sign = sign(input);
416 let (input, offset_hour, offset_minute) = match sign {
417 None => {
418 let ParsedItem(input, offset_hour) =
419 zone_literal(input).ok_or(InvalidComponent("offset hour"))?;
420 (input, offset_hour, 0)
421 }
422 Some(ParsedItem(input, offset_sign)) => {
423 let ParsedItem(input, offset_hour) = try_likely_ok!(
424 ExactlyNDigits::<2>::parse(input)
425 .map(|item| {
426 item.map(|offset_hour| match offset_sign {
427 Sign::Negative => -offset_hour.cast_signed(),
428 Sign::Positive => offset_hour.cast_signed(),
429 })
430 })
431 .ok_or(InvalidComponent("offset hour"))
432 );
433 let ParsedItem(input, offset_minute) = try_likely_ok!(
434 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("offset minute"))
435 );
436 (input, offset_hour, offset_minute.cast_signed())
437 }
438 };
439
440 let input = opt(cfws)(input).into_inner();
441
442 if !input.is_empty() {
443 return Err(error::Parse::ParseFromDescription(
444 error::ParseFromDescription::UnexpectedTrailingCharacters,
445 ));
446 }
447
448 let mut nanosecond = 0;
449 let leap_second_input = if second == 60 {
450 second = 59;
451 nanosecond = 999_999_999;
452 true
453 } else {
454 false
455 };
456
457 let dt = try_likely_ok!(
458 (|| {
459 let date = try_likely_ok!(Date::from_calendar_date(
460 year.cast_signed().widen(),
461 month,
462 day
463 ));
464 let time = try_likely_ok!(Time::from_hms_nano(hour, minute, second, nanosecond));
465 let offset = try_likely_ok!(UtcOffset::from_hms(offset_hour, offset_minute, 0));
466 Ok(OffsetDateTime::new_in_offset(date, time, offset))
467 })()
468 .map_err(TryFromParsed::ComponentRange)
469 );
470
471 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
472 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
473 error::ComponentRange::conditional("second"),
474 )));
475 }
476
477 Ok(dt)
478 }
479}
480
481impl sealed::Sealed for Rfc3339 {
482 fn parse_into<'a>(
483 &self,
484 input: &'a [u8],
485 parsed: &mut Parsed,
486 ) -> Result<&'a [u8], error::Parse> {
487 let dash = ascii_char::<b'-'>;
488 let colon = ascii_char::<b':'>;
489
490 let input = try_likely_ok!(
491 ExactlyNDigits::<4>::parse(input)
492 .and_then(|item| {
493 item.consume_value(|value| parsed.set_year(value.cast_signed().widen()))
494 })
495 .ok_or(InvalidComponent("year"))
496 );
497 let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
498 let input = try_likely_ok!(
499 ExactlyNDigits::<2>::parse(input)
500 .and_then(
501 |item| item.flat_map(|value| Month::from_number(NonZero::new(value)?).ok())
502 )
503 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
504 .ok_or(InvalidComponent("month"))
505 );
506 let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
507 let input = try_likely_ok!(
508 ExactlyNDigits::<2>::parse(input)
509 .and_then(|item| item.consume_value(|value| parsed.set_day(NonZero::new(value)?)))
510 .ok_or(InvalidComponent("day"))
511 );
512
513 let input = try_likely_ok!(input.get(1..).ok_or(InvalidComponent("separator")));
521
522 let input = try_likely_ok!(
523 ExactlyNDigits::<2>::parse(input)
524 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
525 .ok_or(InvalidComponent("hour"))
526 );
527 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
528 let input = try_likely_ok!(
529 ExactlyNDigits::<2>::parse(input)
530 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
531 .ok_or(InvalidComponent("minute"))
532 );
533 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
534 let input = try_likely_ok!(
535 ExactlyNDigits::<2>::parse(input)
536 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
537 .ok_or(InvalidComponent("second"))
538 );
539 let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
540 let ParsedItem(mut input, mut value) =
541 try_likely_ok!(any_digit(input).ok_or(InvalidComponent("subsecond")))
542 .map(|v| (v - b'0').widen::<u32>() * 100_000_000);
543
544 let mut multiplier = 10_000_000;
545 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
546 value += (digit - b'0').widen::<u32>() * multiplier;
547 input = new_input;
548 multiplier /= 10;
549 }
550
551 try_likely_ok!(
552 parsed
553 .set_subsecond(value)
554 .ok_or(InvalidComponent("subsecond"))
555 );
556 input
557 } else {
558 input
559 };
560
561 parsed.leap_second_allowed = true;
563
564 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
565 try_likely_ok!(
566 parsed
567 .set_offset_hour(0)
568 .ok_or(InvalidComponent("offset hour"))
569 );
570 try_likely_ok!(
571 parsed
572 .set_offset_minute_signed(0)
573 .ok_or(InvalidComponent("offset minute"))
574 );
575 try_likely_ok!(
576 parsed
577 .set_offset_second_signed(0)
578 .ok_or(InvalidComponent("offset second"))
579 );
580 return Ok(input);
581 }
582
583 let ParsedItem(input, offset_sign) =
584 try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
585 let input = try_likely_ok!(
586 ExactlyNDigits::<2>::parse(input)
587 .and_then(|item| {
588 item.filter(|&offset_hour| offset_hour <= 23)?
589 .map(|offset_hour| match offset_sign {
590 Sign::Negative => -offset_hour.cast_signed(),
591 Sign::Positive => offset_hour.cast_signed(),
592 })
593 .consume_value(|value| parsed.set_offset_hour(value))
594 })
595 .ok_or(InvalidComponent("offset hour"))
596 );
597 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
598 let input = try_likely_ok!(
599 ExactlyNDigits::<2>::parse(input)
600 .and_then(|item| {
601 item.map(|offset_minute| match offset_sign {
602 Sign::Negative => -offset_minute.cast_signed(),
603 Sign::Positive => offset_minute.cast_signed(),
604 })
605 .consume_value(|value| parsed.set_offset_minute_signed(value))
606 })
607 .ok_or(InvalidComponent("offset minute"))
608 );
609
610 Ok(input)
611 }
612
613 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
614 let dash = ascii_char::<b'-'>;
615 let colon = ascii_char::<b':'>;
616
617 let ParsedItem(input, year) =
618 try_likely_ok!(ExactlyNDigits::<4>::parse(input).ok_or(InvalidComponent("year")));
619 let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
620 let ParsedItem(input, month) = try_likely_ok!(
621 ExactlyNDigits::<2>::parse(input)
622 .and_then(|parsed| parsed.flat_map(NonZero::new))
623 .ok_or(InvalidComponent("month"))
624 );
625 let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
626 let ParsedItem(input, day) =
627 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("day")));
628
629 let input = try_likely_ok!(input.get(1..).ok_or(InvalidComponent("separator")));
637
638 let ParsedItem(input, hour) =
639 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("hour")));
640 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
641 let ParsedItem(input, minute) =
642 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("minute")));
643 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
644 let ParsedItem(input, mut second) =
645 try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("second")));
646 let ParsedItem(input, mut nanosecond) =
647 if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
648 let ParsedItem(mut input, mut value) =
649 try_likely_ok!(any_digit(input).ok_or(InvalidComponent("subsecond")))
650 .map(|v| (v - b'0').widen::<u32>() * 100_000_000);
651
652 let mut multiplier = 10_000_000;
653 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
654 value += (digit - b'0').widen::<u32>() * multiplier;
655 input = new_input;
656 multiplier /= 10;
657 }
658
659 ParsedItem(input, value)
660 } else {
661 ParsedItem(input, 0)
662 };
663 let ParsedItem(input, offset) = {
664 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
665 ParsedItem(input, UtcOffset::UTC)
666 } else {
667 let ParsedItem(input, offset_sign) =
668 try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
669 let ParsedItem(input, offset_hour) = try_likely_ok!(
670 ExactlyNDigits::<2>::parse(input)
671 .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
672 .ok_or(InvalidComponent("offset hour"))
673 );
674 let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
675 let ParsedItem(input, offset_minute) = try_likely_ok!(
676 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("offset minute"))
677 );
678 try_likely_ok!(
679 match offset_sign {
680 Sign::Negative => UtcOffset::from_hms(
681 -offset_hour.cast_signed(),
682 -offset_minute.cast_signed(),
683 0,
684 ),
685 Sign::Positive => UtcOffset::from_hms(
686 offset_hour.cast_signed(),
687 offset_minute.cast_signed(),
688 0,
689 ),
690 }
691 .map(|offset| ParsedItem(input, offset))
692 .map_err(TryFromParsed::ComponentRange)
693 )
694 }
695 };
696
697 if !input.is_empty() {
698 return Err(error::Parse::ParseFromDescription(
699 error::ParseFromDescription::UnexpectedTrailingCharacters,
700 ));
701 }
702
703 let leap_second_input = if second == 60 {
707 second = 59;
708 nanosecond = 999_999_999;
709 true
710 } else {
711 false
712 };
713
714 let date = try_likely_ok!(
715 Month::from_number(month)
716 .and_then(|month| Date::from_calendar_date(year.cast_signed().widen(), month, day))
717 .map_err(TryFromParsed::ComponentRange)
718 );
719 let time = try_likely_ok!(
720 Time::from_hms_nano(hour, minute, second, nanosecond)
721 .map_err(TryFromParsed::ComponentRange)
722 );
723 let dt = OffsetDateTime::new_in_offset(date, time, offset);
724
725 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
726 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
727 error::ComponentRange::conditional("second"),
728 )));
729 }
730
731 Ok(dt)
732 }
733}
734
735impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
736 #[inline]
737 fn parse_into<'a>(
738 &self,
739 mut input: &'a [u8],
740 parsed: &mut Parsed,
741 ) -> Result<&'a [u8], error::Parse> {
742 use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
743
744 let mut extended_kind = ExtendedKind::Unknown;
745 let mut date_is_present = false;
746 let mut time_is_present = false;
747 let mut offset_is_present = false;
748 let mut first_error = None;
749
750 parsed.leap_second_allowed = true;
751
752 match Self::parse_date(parsed, &mut extended_kind)(input) {
753 Ok(new_input) => {
754 input = new_input;
755 date_is_present = true;
756 }
757 Err(err) => {
758 first_error.get_or_insert(err);
759 }
760 }
761
762 match Self::parse_time(parsed, &mut extended_kind, date_is_present)(input) {
763 Ok(new_input) => {
764 input = new_input;
765 time_is_present = true;
766 }
767 Err(err) => {
768 first_error.get_or_insert(err);
769 }
770 }
771
772 if !date_is_present || time_is_present {
774 match Self::parse_offset(parsed, &mut extended_kind)(input) {
775 Ok(new_input) => {
776 input = new_input;
777 offset_is_present = true;
778 }
779 Err(err) => {
780 first_error.get_or_insert(err);
781 }
782 }
783 }
784
785 if !date_is_present && !time_is_present && !offset_is_present {
786 match first_error {
787 Some(err) => return Err(err),
788 None => bug!("an error should be present if no components were parsed"),
789 }
790 }
791
792 Ok(input)
793 }
794}