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