1use core::num::NonZero;
4use core::ops::Deref;
5
6use num_conv::prelude::*;
7
8use crate::error::TryFromParsed;
9use crate::format_description::BorrowedFormatItem;
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::internal_macros::bug;
15use crate::parsing::combinator::{Sign, one_or_two_digits};
16use crate::parsing::{Parsed, ParsedItem};
17use crate::{Date, Month, OffsetDateTime, Time, UtcOffset, Weekday, error};
18
19#[cfg_attr(docsrs, doc(notable_trait))]
21#[doc(alias = "Parseable")]
22pub trait Parsable: sealed::Sealed {}
23impl Parsable for BorrowedFormatItem<'_> {}
24impl Parsable for [BorrowedFormatItem<'_>] {}
25#[cfg(feature = "alloc")]
26impl Parsable for OwnedFormatItem {}
27#[cfg(feature = "alloc")]
28impl Parsable for [OwnedFormatItem] {}
29impl Parsable for Rfc2822 {}
30impl Parsable for Rfc3339 {}
31impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {}
32impl<T> Parsable for T where T: Deref<Target: Parsable> {}
33
34mod sealed {
37 use super::*;
38 use crate::{PrimitiveDateTime, UtcDateTime};
39
40 pub trait Sealed {
42 fn parse_into<'a>(
46 &self,
47 input: &'a [u8],
48 parsed: &mut Parsed,
49 ) -> Result<&'a [u8], error::Parse>;
50
51 #[inline]
56 fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
57 let mut parsed = Parsed::new();
58 if self.parse_into(input, &mut parsed)?.is_empty() {
59 Ok(parsed)
60 } else {
61 Err(error::Parse::ParseFromDescription(
62 error::ParseFromDescription::UnexpectedTrailingCharacters,
63 ))
64 }
65 }
66
67 #[inline]
69 fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
70 Ok(self.parse(input)?.try_into()?)
71 }
72
73 #[inline]
75 fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
76 Ok(self.parse(input)?.try_into()?)
77 }
78
79 #[inline]
81 fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
82 Ok(self.parse(input)?.try_into()?)
83 }
84
85 #[inline]
87 fn parse_primitive_date_time(
88 &self,
89 input: &[u8],
90 ) -> Result<PrimitiveDateTime, error::Parse> {
91 Ok(self.parse(input)?.try_into()?)
92 }
93
94 #[inline]
96 fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> {
97 Ok(self.parse(input)?.try_into()?)
98 }
99
100 #[inline]
102 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
103 Ok(self.parse(input)?.try_into()?)
104 }
105 }
106}
107
108impl sealed::Sealed for BorrowedFormatItem<'_> {
109 #[inline]
110 fn parse_into<'a>(
111 &self,
112 input: &'a [u8],
113 parsed: &mut Parsed,
114 ) -> Result<&'a [u8], error::Parse> {
115 Ok(parsed.parse_item(input, self)?)
116 }
117}
118
119impl sealed::Sealed for [BorrowedFormatItem<'_>] {
120 #[inline]
121 fn parse_into<'a>(
122 &self,
123 input: &'a [u8],
124 parsed: &mut Parsed,
125 ) -> Result<&'a [u8], error::Parse> {
126 Ok(parsed.parse_items(input, self)?)
127 }
128}
129
130#[cfg(feature = "alloc")]
131impl sealed::Sealed for OwnedFormatItem {
132 #[inline]
133 fn parse_into<'a>(
134 &self,
135 input: &'a [u8],
136 parsed: &mut Parsed,
137 ) -> Result<&'a [u8], error::Parse> {
138 Ok(parsed.parse_item(input, self)?)
139 }
140}
141
142#[cfg(feature = "alloc")]
143impl sealed::Sealed for [OwnedFormatItem] {
144 #[inline]
145 fn parse_into<'a>(
146 &self,
147 input: &'a [u8],
148 parsed: &mut Parsed,
149 ) -> Result<&'a [u8], error::Parse> {
150 Ok(parsed.parse_items(input, self)?)
151 }
152}
153
154impl<T> sealed::Sealed for T
155where
156 T: Deref<Target: sealed::Sealed>,
157{
158 #[inline]
159 fn parse_into<'a>(
160 &self,
161 input: &'a [u8],
162 parsed: &mut Parsed,
163 ) -> Result<&'a [u8], error::Parse> {
164 self.deref().parse_into(input, parsed)
165 }
166}
167
168impl sealed::Sealed for Rfc2822 {
169 fn parse_into<'a>(
170 &self,
171 input: &'a [u8],
172 parsed: &mut Parsed,
173 ) -> Result<&'a [u8], error::Parse> {
174 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
175 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
176 use crate::parsing::combinator::{ExactlyNDigits, ascii_char, first_match, opt, sign};
177
178 let colon = ascii_char::<b':'>;
179 let comma = ascii_char::<b','>;
180
181 let input = opt(cfws)(input).into_inner();
182 let weekday = first_match(
183 [
184 (b"Mon".as_slice(), Weekday::Monday),
185 (b"Tue".as_slice(), Weekday::Tuesday),
186 (b"Wed".as_slice(), Weekday::Wednesday),
187 (b"Thu".as_slice(), Weekday::Thursday),
188 (b"Fri".as_slice(), Weekday::Friday),
189 (b"Sat".as_slice(), Weekday::Saturday),
190 (b"Sun".as_slice(), Weekday::Sunday),
191 ],
192 false,
193 )(input);
194 let input = if let Some(item) = weekday {
195 let input = item
196 .consume_value(|value| parsed.set_weekday(value))
197 .ok_or(InvalidComponent("weekday"))?;
198 let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
199 opt(cfws)(input).into_inner()
200 } else {
201 input
202 };
203 let input = 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 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
207 let input = first_match(
208 [
209 (b"Jan".as_slice(), Month::January),
210 (b"Feb".as_slice(), Month::February),
211 (b"Mar".as_slice(), Month::March),
212 (b"Apr".as_slice(), Month::April),
213 (b"May".as_slice(), Month::May),
214 (b"Jun".as_slice(), Month::June),
215 (b"Jul".as_slice(), Month::July),
216 (b"Aug".as_slice(), Month::August),
217 (b"Sep".as_slice(), Month::September),
218 (b"Oct".as_slice(), Month::October),
219 (b"Nov".as_slice(), Month::November),
220 (b"Dec".as_slice(), Month::December),
221 ],
222 false,
223 )(input)
224 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
225 .ok_or(InvalidComponent("month"))?;
226 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
227 let input = match ExactlyNDigits::<4>::parse(input) {
228 Some(item) => {
229 let input = item
230 .flat_map(|year| if year >= 1900 { Some(year) } else { None })
231 .and_then(|item| {
232 item.consume_value(|value| parsed.set_year(value.cast_signed().extend()))
233 })
234 .ok_or(InvalidComponent("year"))?;
235 fws(input).ok_or(InvalidLiteral)?.into_inner()
236 }
237 None => {
238 let input = ExactlyNDigits::<2>::parse(input)
239 .and_then(|item| {
240 item.map(|year| year.extend::<u32>())
241 .map(|year| if year < 50 { year + 2000 } else { year + 1900 })
242 .map(|year| year.cast_signed())
243 .consume_value(|value| parsed.set_year(value))
244 })
245 .ok_or(InvalidComponent("year"))?;
246 cfws(input).ok_or(InvalidLiteral)?.into_inner()
247 }
248 };
249
250 let input = ExactlyNDigits::<2>::parse(input)
251 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
252 .ok_or(InvalidComponent("hour"))?;
253 let input = opt(cfws)(input).into_inner();
254 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
255 let input = opt(cfws)(input).into_inner();
256 let input = ExactlyNDigits::<2>::parse(input)
257 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
258 .ok_or(InvalidComponent("minute"))?;
259
260 let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
261 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
263 let input = ExactlyNDigits::<2>::parse(input)
264 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
265 .ok_or(InvalidComponent("second"))?;
266 cfws(input).ok_or(InvalidLiteral)?.into_inner()
267 } else {
268 cfws(input).ok_or(InvalidLiteral)?.into_inner()
269 };
270
271 parsed.leap_second_allowed = true;
273
274 #[expect(
275 clippy::unnecessary_lazy_evaluations,
276 reason = "rust-lang/rust-clippy#8522"
277 )]
278 let zone_literal = first_match(
279 [
280 (b"UT".as_slice(), 0),
281 (b"GMT".as_slice(), 0),
282 (b"EST".as_slice(), -5),
283 (b"EDT".as_slice(), -4),
284 (b"CST".as_slice(), -6),
285 (b"CDT".as_slice(), -5),
286 (b"MST".as_slice(), -7),
287 (b"MDT".as_slice(), -6),
288 (b"PST".as_slice(), -8),
289 (b"PDT".as_slice(), -7),
290 ],
291 false,
292 )(input)
293 .or_else(|| match input {
294 [
295 b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
296 rest @ ..,
297 ] => Some(ParsedItem(rest, 0)),
298 _ => None,
299 });
300 if let Some(zone_literal) = zone_literal {
301 let input = zone_literal
302 .consume_value(|value| parsed.set_offset_hour(value))
303 .ok_or(InvalidComponent("offset hour"))?;
304 parsed
305 .set_offset_minute_signed(0)
306 .ok_or(InvalidComponent("offset minute"))?;
307 parsed
308 .set_offset_second_signed(0)
309 .ok_or(InvalidComponent("offset second"))?;
310 return Ok(input);
311 }
312
313 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
314 let input = ExactlyNDigits::<2>::parse(input)
315 .and_then(|item| {
316 item.map(|offset_hour| match offset_sign {
317 Sign::Negative => -offset_hour.cast_signed(),
318 Sign::Positive => offset_hour.cast_signed(),
319 })
320 .consume_value(|value| parsed.set_offset_hour(value))
321 })
322 .ok_or(InvalidComponent("offset hour"))?;
323 let input = ExactlyNDigits::<2>::parse(input)
324 .and_then(|item| {
325 item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
326 })
327 .ok_or(InvalidComponent("offset minute"))?;
328
329 let input = opt(cfws)(input).into_inner();
330
331 Ok(input)
332 }
333
334 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
335 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
336 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
337 use crate::parsing::combinator::{ExactlyNDigits, ascii_char, first_match, opt, sign};
338
339 let colon = ascii_char::<b':'>;
340 let comma = ascii_char::<b','>;
341
342 let input = opt(cfws)(input).into_inner();
343 let weekday = first_match(
346 [
347 (b"Mon".as_slice(), ()),
348 (b"Tue".as_slice(), ()),
349 (b"Wed".as_slice(), ()),
350 (b"Thu".as_slice(), ()),
351 (b"Fri".as_slice(), ()),
352 (b"Sat".as_slice(), ()),
353 (b"Sun".as_slice(), ()),
354 ],
355 false,
356 )(input);
357 let input = if let Some(item) = weekday {
358 let input = item.into_inner();
359 let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
360 opt(cfws)(input).into_inner()
361 } else {
362 input
363 };
364 let ParsedItem(input, day) = one_or_two_digits(input).ok_or(InvalidComponent("day"))?;
365 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
366 let ParsedItem(input, month) = first_match(
367 [
368 (b"Jan".as_slice(), Month::January),
369 (b"Feb".as_slice(), Month::February),
370 (b"Mar".as_slice(), Month::March),
371 (b"Apr".as_slice(), Month::April),
372 (b"May".as_slice(), Month::May),
373 (b"Jun".as_slice(), Month::June),
374 (b"Jul".as_slice(), Month::July),
375 (b"Aug".as_slice(), Month::August),
376 (b"Sep".as_slice(), Month::September),
377 (b"Oct".as_slice(), Month::October),
378 (b"Nov".as_slice(), Month::November),
379 (b"Dec".as_slice(), Month::December),
380 ],
381 false,
382 )(input)
383 .ok_or(InvalidComponent("month"))?;
384 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
385 let (input, year) = match ExactlyNDigits::<4>::parse(input) {
386 Some(item) => {
387 let ParsedItem(input, year) = item
388 .flat_map(|year| if year >= 1900 { Some(year) } else { None })
389 .ok_or(InvalidComponent("year"))?;
390 let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
391 (input, year)
392 }
393 None => {
394 let ParsedItem(input, year) = ExactlyNDigits::<2>::parse(input)
395 .map(|item| {
396 item.map(|year| year.extend::<u16>())
397 .map(|year| if year < 50 { year + 2000 } else { year + 1900 })
398 })
399 .ok_or(InvalidComponent("year"))?;
400 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
401 (input, year)
402 }
403 };
404
405 let ParsedItem(input, hour) =
406 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("hour"))?;
407 let input = opt(cfws)(input).into_inner();
408 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
409 let input = opt(cfws)(input).into_inner();
410 let ParsedItem(input, minute) =
411 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("minute"))?;
412
413 let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
414 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
416 let ParsedItem(input, second) =
417 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("second"))?;
418 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
419 (input, second)
420 } else {
421 (cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0)
422 };
423
424 #[expect(
425 clippy::unnecessary_lazy_evaluations,
426 reason = "rust-lang/rust-clippy#8522"
427 )]
428 let zone_literal = first_match(
429 [
430 (b"UT".as_slice(), 0),
431 (b"GMT".as_slice(), 0),
432 (b"EST".as_slice(), -5),
433 (b"EDT".as_slice(), -4),
434 (b"CST".as_slice(), -6),
435 (b"CDT".as_slice(), -5),
436 (b"MST".as_slice(), -7),
437 (b"MDT".as_slice(), -6),
438 (b"PST".as_slice(), -8),
439 (b"PDT".as_slice(), -7),
440 ],
441 false,
442 )(input)
443 .or_else(|| match input {
444 [
445 b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
446 rest @ ..,
447 ] => Some(ParsedItem(rest, 0)),
448 _ => None,
449 });
450
451 let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal {
452 let ParsedItem(input, offset_hour) = zone_literal;
453 (input, offset_hour, 0)
454 } else {
455 let ParsedItem(input, offset_sign) =
456 sign(input).ok_or(InvalidComponent("offset hour"))?;
457 let ParsedItem(input, offset_hour) = ExactlyNDigits::<2>::parse(input)
458 .map(|item| {
459 item.map(|offset_hour| match offset_sign {
460 Sign::Negative => -offset_hour.cast_signed(),
461 Sign::Positive => offset_hour.cast_signed(),
462 })
463 })
464 .ok_or(InvalidComponent("offset hour"))?;
465 let ParsedItem(input, offset_minute) =
466 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("offset minute"))?;
467 (input, offset_hour, offset_minute.cast_signed())
468 };
469
470 let input = opt(cfws)(input).into_inner();
471
472 if !input.is_empty() {
473 return Err(error::Parse::ParseFromDescription(
474 error::ParseFromDescription::UnexpectedTrailingCharacters,
475 ));
476 }
477
478 let mut nanosecond = 0;
479 let leap_second_input = if second == 60 {
480 second = 59;
481 nanosecond = 999_999_999;
482 true
483 } else {
484 false
485 };
486
487 let dt = (|| {
488 let date = Date::from_calendar_date(year.cast_signed().extend(), month, day)?;
489 let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
490 let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
491 Ok(OffsetDateTime::new_in_offset(date, time, offset))
492 })()
493 .map_err(TryFromParsed::ComponentRange)?;
494
495 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
496 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
497 error::ComponentRange::conditional("second"),
498 )));
499 }
500
501 Ok(dt)
502 }
503}
504
505impl sealed::Sealed for Rfc3339 {
506 fn parse_into<'a>(
507 &self,
508 input: &'a [u8],
509 parsed: &mut Parsed,
510 ) -> Result<&'a [u8], error::Parse> {
511 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
512 use crate::parsing::combinator::{
513 ExactlyNDigits, any_digit, ascii_char, ascii_char_ignore_case, sign,
514 };
515
516 let dash = ascii_char::<b'-'>;
517 let colon = ascii_char::<b':'>;
518
519 let input = ExactlyNDigits::<4>::parse(input)
520 .and_then(|item| {
521 item.consume_value(|value| parsed.set_year(value.cast_signed().extend()))
522 })
523 .ok_or(InvalidComponent("year"))?;
524 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
525 let input = ExactlyNDigits::<2>::parse(input)
526 .and_then(|item| item.flat_map(|value| Month::from_number(NonZero::new(value)?).ok()))
527 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
528 .ok_or(InvalidComponent("month"))?;
529 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
530 let input = ExactlyNDigits::<2>::parse(input)
531 .and_then(|item| item.consume_value(|value| parsed.set_day(NonZero::new(value)?)))
532 .ok_or(InvalidComponent("day"))?;
533
534 let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
542
543 let input = ExactlyNDigits::<2>::parse(input)
544 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
545 .ok_or(InvalidComponent("hour"))?;
546 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
547 let input = ExactlyNDigits::<2>::parse(input)
548 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
549 .ok_or(InvalidComponent("minute"))?;
550 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
551 let input = ExactlyNDigits::<2>::parse(input)
552 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
553 .ok_or(InvalidComponent("second"))?;
554 let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
555 let ParsedItem(mut input, mut value) = any_digit(input)
556 .ok_or(InvalidComponent("subsecond"))?
557 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
558
559 let mut multiplier = 10_000_000;
560 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
561 value += (digit - b'0').extend::<u32>() * multiplier;
562 input = new_input;
563 multiplier /= 10;
564 }
565
566 parsed
567 .set_subsecond(value)
568 .ok_or(InvalidComponent("subsecond"))?;
569 input
570 } else {
571 input
572 };
573
574 parsed.leap_second_allowed = true;
576
577 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
578 parsed
579 .set_offset_hour(0)
580 .ok_or(InvalidComponent("offset hour"))?;
581 parsed
582 .set_offset_minute_signed(0)
583 .ok_or(InvalidComponent("offset minute"))?;
584 parsed
585 .set_offset_second_signed(0)
586 .ok_or(InvalidComponent("offset second"))?;
587 return Ok(input);
588 }
589
590 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
591 let input = ExactlyNDigits::<2>::parse(input)
592 .and_then(|item| {
593 item.filter(|&offset_hour| offset_hour <= 23)?
594 .map(|offset_hour| match offset_sign {
595 Sign::Negative => -offset_hour.cast_signed(),
596 Sign::Positive => offset_hour.cast_signed(),
597 })
598 .consume_value(|value| parsed.set_offset_hour(value))
599 })
600 .ok_or(InvalidComponent("offset hour"))?;
601 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
602 let input = ExactlyNDigits::<2>::parse(input)
603 .and_then(|item| {
604 item.map(|offset_minute| match offset_sign {
605 Sign::Negative => -offset_minute.cast_signed(),
606 Sign::Positive => offset_minute.cast_signed(),
607 })
608 .consume_value(|value| parsed.set_offset_minute_signed(value))
609 })
610 .ok_or(InvalidComponent("offset minute"))?;
611
612 Ok(input)
613 }
614
615 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
616 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
617 use crate::parsing::combinator::{
618 ExactlyNDigits, any_digit, ascii_char, ascii_char_ignore_case, sign,
619 };
620
621 let dash = ascii_char::<b'-'>;
622 let colon = ascii_char::<b':'>;
623
624 let ParsedItem(input, year) =
625 ExactlyNDigits::<4>::parse(input).ok_or(InvalidComponent("year"))?;
626 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
627 let ParsedItem(input, month) = ExactlyNDigits::<2>::parse(input)
628 .and_then(|parsed| parsed.flat_map(NonZero::new))
629 .ok_or(InvalidComponent("month"))?;
630 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
631 let ParsedItem(input, day) =
632 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("day"))?;
633
634 let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
642
643 let ParsedItem(input, hour) =
644 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("hour"))?;
645 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
646 let ParsedItem(input, minute) =
647 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("minute"))?;
648 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
649 let ParsedItem(input, mut second) =
650 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("second"))?;
651 let ParsedItem(input, mut nanosecond) =
652 if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
653 let ParsedItem(mut input, mut value) = any_digit(input)
654 .ok_or(InvalidComponent("subsecond"))?
655 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
656
657 let mut multiplier = 10_000_000;
658 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
659 value += (digit - b'0').extend::<u32>() * multiplier;
660 input = new_input;
661 multiplier /= 10;
662 }
663
664 ParsedItem(input, value)
665 } else {
666 ParsedItem(input, 0)
667 };
668 let ParsedItem(input, offset) = {
669 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
670 ParsedItem(input, UtcOffset::UTC)
671 } else {
672 let ParsedItem(input, offset_sign) =
673 sign(input).ok_or(InvalidComponent("offset hour"))?;
674 let ParsedItem(input, offset_hour) = ExactlyNDigits::<2>::parse(input)
675 .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
676 .ok_or(InvalidComponent("offset hour"))?;
677 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
678 let ParsedItem(input, offset_minute) =
679 ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("offset minute"))?;
680 match offset_sign {
681 Sign::Negative => UtcOffset::from_hms(
682 -offset_hour.cast_signed(),
683 -offset_minute.cast_signed(),
684 0,
685 ),
686 Sign::Positive => UtcOffset::from_hms(
687 offset_hour.cast_signed(),
688 offset_minute.cast_signed(),
689 0,
690 ),
691 }
692 .map(|offset| ParsedItem(input, offset))
693 .map_err(TryFromParsed::ComponentRange)?
694 }
695 };
696
697 if !input.is_empty() {
698 return Err(error::Parse::ParseFromDescription(
699 error::ParseFromDescription::UnexpectedTrailingCharacters,
700 ));
701 }
702
703 let leap_second_input = if second == 60 {
707 second = 59;
708 nanosecond = 999_999_999;
709 true
710 } else {
711 false
712 };
713
714 let date = Month::from_number(month)
715 .and_then(|month| Date::from_calendar_date(year.cast_signed().extend(), month, day))
716 .map_err(TryFromParsed::ComponentRange)?;
717 let time = Time::from_hms_nano(hour, minute, second, nanosecond)
718 .map_err(TryFromParsed::ComponentRange)?;
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}