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