Skip to main content

time/parsing/
parsable.rs

1//! A trait that can be used to parse an item from an input.
2
3use 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/// A type that can be parsed.
24#[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
38/// Seal the trait to prevent downstream users from implementing it, while still allowing it to
39/// exist in generic bounds.
40mod sealed {
41    use super::*;
42    use crate::{PrimitiveDateTime, UtcDateTime};
43
44    /// Parse the item using a format description and an input.
45    pub trait Sealed {
46        /// Parse the item into the provided [`Parsed`] struct.
47        ///
48        /// This method can be used to parse a single component without parsing the full value.
49        fn parse_into<'a>(
50            &self,
51            input: &'a [u8],
52            parsed: &mut Parsed,
53        ) -> Result<&'a [u8], error::Parse>;
54
55        /// Parse the item into a new [`Parsed`] struct.
56        ///
57        /// This method can only be used to parse a complete value of a type. If any characters
58        /// remain after parsing, an error will be returned.
59        #[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        /// Parse a [`Date`] from the format description.
72        #[inline]
73        fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
74            Ok(self.parse(input)?.try_into()?)
75        }
76
77        /// Parse a [`Time`] from the format description.
78        #[inline]
79        fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
80            Ok(self.parse(input)?.try_into()?)
81        }
82
83        /// Parse a [`UtcOffset`] from the format description.
84        #[inline]
85        fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
86            Ok(self.parse(input)?.try_into()?)
87        }
88
89        /// Parse a [`PrimitiveDateTime`] from the format description.
90        #[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        /// Parse a [`UtcDateTime`] from the format description.
99        #[inline]
100        fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> {
101            Ok(self.parse(input)?.try_into()?)
102        }
103
104        /// Parse a [`OffsetDateTime`] from the format description.
105        #[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(); // discard the colon
261            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        // The RFC explicitly allows leap seconds.
273        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(); // discard the colon
387            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        // RFC3339 allows any separator, not just `T`, not just `space`.
498        // cf. Section 5.6: Internet Date/Time Format:
499        //   NOTE: ISO 8601 defines date and time separated by "T".
500        //   Applications using this syntax may choose, for the sake of
501        //   readability, to specify a full-date and full-time separated by
502        //   (say) a space character.
503        // Specifically, rusqlite uses space separators.
504        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        // The RFC explicitly allows leap seconds.
546        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        // RFC3339 allows any separator, not just `T`, not just `space`.
614        // cf. Section 5.6: Internet Date/Time Format:
615        //   NOTE: ISO 8601 defines date and time separated by "T".
616        //   Applications using this syntax may choose, for the sake of
617        //   readability, to specify a full-date and full-time separated by
618        //   (say) a space character.
619        // Specifically, rusqlite uses space separators.
620        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        // The RFC explicitly permits leap seconds. We don't currently support them, so treat it as
688        // the preceding nanosecond. However, leap seconds can only occur as the last second of the
689        // month UTC.
690        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 a date and offset are present, a time must be as well.
757        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}