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