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