1use core::ops::Deref;
4
5use num_conv::prelude::*;
6
7use crate::error::TryFromParsed;
8use crate::format_description::well_known::iso8601::EncodedConfig;
9use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
10use crate::format_description::BorrowedFormatItem;
11#[cfg(feature = "alloc")]
12use crate::format_description::OwnedFormatItem;
13use crate::internal_macros::bug;
14use crate::parsing::{Parsed, ParsedItem};
15use crate::{error, Date, Month, OffsetDateTime, Time, UtcOffset, Weekday};
16
17#[cfg_attr(docsrs, doc(notable_trait))]
19#[doc(alias = "Parseable")]
20pub trait Parsable: sealed::Sealed {}
21impl Parsable for BorrowedFormatItem<'_> {}
22impl Parsable for [BorrowedFormatItem<'_>] {}
23#[cfg(feature = "alloc")]
24impl Parsable for OwnedFormatItem {}
25#[cfg(feature = "alloc")]
26impl Parsable for [OwnedFormatItem] {}
27impl Parsable for Rfc2822 {}
28impl Parsable for Rfc3339 {}
29impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {}
30impl<T: Deref> Parsable for T where T::Target: Parsable {}
31
32mod sealed {
35 use super::*;
36 use crate::{PrimitiveDateTime, UtcDateTime};
37
38 pub trait Sealed {
40 fn parse_into<'a>(
44 &self,
45 input: &'a [u8],
46 parsed: &mut Parsed,
47 ) -> Result<&'a [u8], error::Parse>;
48
49 #[inline]
54 fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
55 let mut parsed = Parsed::new();
56 if self.parse_into(input, &mut parsed)?.is_empty() {
57 Ok(parsed)
58 } else {
59 Err(error::Parse::ParseFromDescription(
60 error::ParseFromDescription::UnexpectedTrailingCharacters,
61 ))
62 }
63 }
64
65 #[inline]
67 fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
68 Ok(self.parse(input)?.try_into()?)
69 }
70
71 #[inline]
73 fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
74 Ok(self.parse(input)?.try_into()?)
75 }
76
77 #[inline]
79 fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
80 Ok(self.parse(input)?.try_into()?)
81 }
82
83 #[inline]
85 fn parse_primitive_date_time(
86 &self,
87 input: &[u8],
88 ) -> Result<PrimitiveDateTime, error::Parse> {
89 Ok(self.parse(input)?.try_into()?)
90 }
91
92 #[inline]
94 fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> {
95 Ok(self.parse(input)?.try_into()?)
96 }
97
98 #[inline]
100 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
101 Ok(self.parse(input)?.try_into()?)
102 }
103 }
104}
105
106impl sealed::Sealed for BorrowedFormatItem<'_> {
107 #[inline]
108 fn parse_into<'a>(
109 &self,
110 input: &'a [u8],
111 parsed: &mut Parsed,
112 ) -> Result<&'a [u8], error::Parse> {
113 Ok(parsed.parse_item(input, self)?)
114 }
115}
116
117impl sealed::Sealed for [BorrowedFormatItem<'_>] {
118 #[inline]
119 fn parse_into<'a>(
120 &self,
121 input: &'a [u8],
122 parsed: &mut Parsed,
123 ) -> Result<&'a [u8], error::Parse> {
124 Ok(parsed.parse_items(input, self)?)
125 }
126}
127
128#[cfg(feature = "alloc")]
129impl sealed::Sealed for OwnedFormatItem {
130 #[inline]
131 fn parse_into<'a>(
132 &self,
133 input: &'a [u8],
134 parsed: &mut Parsed,
135 ) -> Result<&'a [u8], error::Parse> {
136 Ok(parsed.parse_item(input, self)?)
137 }
138}
139
140#[cfg(feature = "alloc")]
141impl sealed::Sealed for [OwnedFormatItem] {
142 #[inline]
143 fn parse_into<'a>(
144 &self,
145 input: &'a [u8],
146 parsed: &mut Parsed,
147 ) -> Result<&'a [u8], error::Parse> {
148 Ok(parsed.parse_items(input, self)?)
149 }
150}
151
152impl<T> sealed::Sealed for T
153where
154 T: Deref<Target: sealed::Sealed>,
155{
156 #[inline]
157 fn parse_into<'a>(
158 &self,
159 input: &'a [u8],
160 parsed: &mut Parsed,
161 ) -> Result<&'a [u8], error::Parse> {
162 self.deref().parse_into(input, parsed)
163 }
164}
165
166impl sealed::Sealed for Rfc2822 {
167 fn parse_into<'a>(
168 &self,
169 input: &'a [u8],
170 parsed: &mut Parsed,
171 ) -> Result<&'a [u8], error::Parse> {
172 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
173 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
174 use crate::parsing::combinator::{
175 ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
176 };
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 = n_to_m_digits::<1, 2, _>(input)
204 .and_then(|item| item.consume_value(|value| parsed.set_day(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 exactly_n_digits::<4, u32>(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()))
233 })
234 .ok_or(InvalidComponent("year"))?;
235 fws(input).ok_or(InvalidLiteral)?.into_inner()
236 }
237 None => {
238 let input = exactly_n_digits::<2, u32>(input)
239 .and_then(|item| {
240 item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
241 .map(|year| year.cast_signed())
242 .consume_value(|value| parsed.set_year(value))
243 })
244 .ok_or(InvalidComponent("year"))?;
245 cfws(input).ok_or(InvalidLiteral)?.into_inner()
246 }
247 };
248
249 let input = exactly_n_digits::<2, _>(input)
250 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
251 .ok_or(InvalidComponent("hour"))?;
252 let input = opt(cfws)(input).into_inner();
253 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
254 let input = opt(cfws)(input).into_inner();
255 let input = exactly_n_digits::<2, _>(input)
256 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
257 .ok_or(InvalidComponent("minute"))?;
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 = exactly_n_digits::<2, _>(input)
263 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
264 .ok_or(InvalidComponent("second"))?;
265 cfws(input).ok_or(InvalidLiteral)?.into_inner()
266 } else {
267 cfws(input).ok_or(InvalidLiteral)?.into_inner()
268 };
269
270 parsed.leap_second_allowed = true;
272
273 #[expect(
274 clippy::unnecessary_lazy_evaluations,
275 reason = "rust-lang/rust-clippy#8522"
276 )]
277 let zone_literal = first_match(
278 [
279 (b"UT".as_slice(), 0),
280 (b"GMT".as_slice(), 0),
281 (b"EST".as_slice(), -5),
282 (b"EDT".as_slice(), -4),
283 (b"CST".as_slice(), -6),
284 (b"CDT".as_slice(), -5),
285 (b"MST".as_slice(), -7),
286 (b"MDT".as_slice(), -6),
287 (b"PST".as_slice(), -8),
288 (b"PDT".as_slice(), -7),
289 ],
290 false,
291 )(input)
292 .or_else(|| match input {
293 [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => {
294 Some(ParsedItem(rest, 0))
295 }
296 _ => None,
297 });
298 if let Some(zone_literal) = zone_literal {
299 let input = zone_literal
300 .consume_value(|value| parsed.set_offset_hour(value))
301 .ok_or(InvalidComponent("offset hour"))?;
302 parsed
303 .set_offset_minute_signed(0)
304 .ok_or(InvalidComponent("offset minute"))?;
305 parsed
306 .set_offset_second_signed(0)
307 .ok_or(InvalidComponent("offset second"))?;
308 return Ok(input);
309 }
310
311 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
312 let input = exactly_n_digits::<2, u8>(input)
313 .and_then(|item| {
314 item.map(|offset_hour| {
315 if offset_sign == b'-' {
316 -offset_hour.cast_signed()
317 } else {
318 offset_hour.cast_signed()
319 }
320 })
321 .consume_value(|value| parsed.set_offset_hour(value))
322 })
323 .ok_or(InvalidComponent("offset hour"))?;
324 let input = exactly_n_digits::<2, u8>(input)
325 .and_then(|item| {
326 item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
327 })
328 .ok_or(InvalidComponent("offset minute"))?;
329
330 let input = opt(cfws)(input).into_inner();
331
332 Ok(input)
333 }
334
335 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
336 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
337 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
338 use crate::parsing::combinator::{
339 ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
340 };
341
342 let colon = ascii_char::<b':'>;
343 let comma = ascii_char::<b','>;
344
345 let input = opt(cfws)(input).into_inner();
346 let weekday = first_match(
349 [
350 (b"Mon".as_slice(), ()),
351 (b"Tue".as_slice(), ()),
352 (b"Wed".as_slice(), ()),
353 (b"Thu".as_slice(), ()),
354 (b"Fri".as_slice(), ()),
355 (b"Sat".as_slice(), ()),
356 (b"Sun".as_slice(), ()),
357 ],
358 false,
359 )(input);
360 let input = if let Some(item) = weekday {
361 let input = item.into_inner();
362 let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
363 opt(cfws)(input).into_inner()
364 } else {
365 input
366 };
367 let ParsedItem(input, day) =
368 n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
369 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
370 let ParsedItem(input, month) = first_match(
371 [
372 (b"Jan".as_slice(), Month::January),
373 (b"Feb".as_slice(), Month::February),
374 (b"Mar".as_slice(), Month::March),
375 (b"Apr".as_slice(), Month::April),
376 (b"May".as_slice(), Month::May),
377 (b"Jun".as_slice(), Month::June),
378 (b"Jul".as_slice(), Month::July),
379 (b"Aug".as_slice(), Month::August),
380 (b"Sep".as_slice(), Month::September),
381 (b"Oct".as_slice(), Month::October),
382 (b"Nov".as_slice(), Month::November),
383 (b"Dec".as_slice(), Month::December),
384 ],
385 false,
386 )(input)
387 .ok_or(InvalidComponent("month"))?;
388 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
389 let (input, year) = match exactly_n_digits::<4, u32>(input) {
390 Some(item) => {
391 let ParsedItem(input, year) = item
392 .flat_map(|year| if year >= 1900 { Some(year) } else { None })
393 .ok_or(InvalidComponent("year"))?;
394 let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
395 (input, year)
396 }
397 None => {
398 let ParsedItem(input, year) = exactly_n_digits::<2, u32>(input)
399 .map(|item| item.map(|year| if year < 50 { year + 2000 } else { year + 1900 }))
400 .ok_or(InvalidComponent("year"))?;
401 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
402 (input, year)
403 }
404 };
405
406 let ParsedItem(input, hour) =
407 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
408 let input = opt(cfws)(input).into_inner();
409 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
410 let input = opt(cfws)(input).into_inner();
411 let ParsedItem(input, minute) =
412 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
413
414 let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
415 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
417 let ParsedItem(input, second) =
418 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
419 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
420 (input, second)
421 } else {
422 (cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0)
423 };
424
425 #[expect(
426 clippy::unnecessary_lazy_evaluations,
427 reason = "rust-lang/rust-clippy#8522"
428 )]
429 let zone_literal = first_match(
430 [
431 (b"UT".as_slice(), 0),
432 (b"GMT".as_slice(), 0),
433 (b"EST".as_slice(), -5),
434 (b"EDT".as_slice(), -4),
435 (b"CST".as_slice(), -6),
436 (b"CDT".as_slice(), -5),
437 (b"MST".as_slice(), -7),
438 (b"MDT".as_slice(), -6),
439 (b"PST".as_slice(), -8),
440 (b"PDT".as_slice(), -7),
441 ],
442 false,
443 )(input)
444 .or_else(|| match input {
445 [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => {
446 Some(ParsedItem(rest, 0))
447 }
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) = exactly_n_digits::<2, u8>(input)
458 .map(|item| {
459 item.map(|offset_hour| {
460 if offset_sign == b'-' {
461 -offset_hour.cast_signed()
462 } else {
463 offset_hour.cast_signed()
464 }
465 })
466 })
467 .ok_or(InvalidComponent("offset hour"))?;
468 let ParsedItem(input, offset_minute) =
469 exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
470 (input, offset_hour, offset_minute.cast_signed())
471 };
472
473 let input = opt(cfws)(input).into_inner();
474
475 if !input.is_empty() {
476 return Err(error::Parse::ParseFromDescription(
477 error::ParseFromDescription::UnexpectedTrailingCharacters,
478 ));
479 }
480
481 let mut nanosecond = 0;
482 let leap_second_input = if second == 60 {
483 second = 59;
484 nanosecond = 999_999_999;
485 true
486 } else {
487 false
488 };
489
490 let dt = (|| {
491 let date = Date::from_calendar_date(year.cast_signed(), month, day)?;
492 let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
493 let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
494 Ok(OffsetDateTime::new_in_offset(date, time, offset))
495 })()
496 .map_err(TryFromParsed::ComponentRange)?;
497
498 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
499 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
500 error::ComponentRange {
501 name: "second",
502 minimum: 0,
503 maximum: 59,
504 value: 60,
505 conditional_message: Some("because leap seconds are not supported"),
506 },
507 )));
508 }
509
510 Ok(dt)
511 }
512}
513
514impl sealed::Sealed for Rfc3339 {
515 fn parse_into<'a>(
516 &self,
517 input: &'a [u8],
518 parsed: &mut Parsed,
519 ) -> Result<&'a [u8], error::Parse> {
520 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
521 use crate::parsing::combinator::{
522 any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
523 };
524
525 let dash = ascii_char::<b'-'>;
526 let colon = ascii_char::<b':'>;
527
528 let input = exactly_n_digits::<4, u32>(input)
529 .and_then(|item| item.consume_value(|value| parsed.set_year(value.cast_signed())))
530 .ok_or(InvalidComponent("year"))?;
531 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
532 let input = exactly_n_digits::<2, _>(input)
533 .and_then(|item| item.flat_map(|value| Month::from_number(value).ok()))
534 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
535 .ok_or(InvalidComponent("month"))?;
536 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
537 let input = exactly_n_digits::<2, _>(input)
538 .and_then(|item| item.consume_value(|value| parsed.set_day(value)))
539 .ok_or(InvalidComponent("day"))?;
540
541 let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
549
550 let input = exactly_n_digits::<2, _>(input)
551 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
552 .ok_or(InvalidComponent("hour"))?;
553 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
554 let input = exactly_n_digits::<2, _>(input)
555 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
556 .ok_or(InvalidComponent("minute"))?;
557 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
558 let input = exactly_n_digits::<2, _>(input)
559 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
560 .ok_or(InvalidComponent("second"))?;
561 let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
562 let ParsedItem(mut input, mut value) = any_digit(input)
563 .ok_or(InvalidComponent("subsecond"))?
564 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
565
566 let mut multiplier = 10_000_000;
567 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
568 value += (digit - b'0').extend::<u32>() * multiplier;
569 input = new_input;
570 multiplier /= 10;
571 }
572
573 parsed
574 .set_subsecond(value)
575 .ok_or(InvalidComponent("subsecond"))?;
576 input
577 } else {
578 input
579 };
580
581 parsed.leap_second_allowed = true;
583
584 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
585 parsed
586 .set_offset_hour(0)
587 .ok_or(InvalidComponent("offset hour"))?;
588 parsed
589 .set_offset_minute_signed(0)
590 .ok_or(InvalidComponent("offset minute"))?;
591 parsed
592 .set_offset_second_signed(0)
593 .ok_or(InvalidComponent("offset second"))?;
594 return Ok(input);
595 }
596
597 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
598 let input = exactly_n_digits::<2, u8>(input)
599 .and_then(|item| {
600 item.filter(|&offset_hour| offset_hour <= 23)?
601 .map(|offset_hour| {
602 if offset_sign == b'-' {
603 -offset_hour.cast_signed()
604 } else {
605 offset_hour.cast_signed()
606 }
607 })
608 .consume_value(|value| parsed.set_offset_hour(value))
609 })
610 .ok_or(InvalidComponent("offset hour"))?;
611 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
612 let input = exactly_n_digits::<2, u8>(input)
613 .and_then(|item| {
614 item.map(|offset_minute| {
615 if offset_sign == b'-' {
616 -offset_minute.cast_signed()
617 } else {
618 offset_minute.cast_signed()
619 }
620 })
621 .consume_value(|value| parsed.set_offset_minute_signed(value))
622 })
623 .ok_or(InvalidComponent("offset minute"))?;
624
625 Ok(input)
626 }
627
628 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
629 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
630 use crate::parsing::combinator::{
631 any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
632 };
633
634 let dash = ascii_char::<b'-'>;
635 let colon = ascii_char::<b':'>;
636
637 let ParsedItem(input, year) =
638 exactly_n_digits::<4, u32>(input).ok_or(InvalidComponent("year"))?;
639 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
640 let ParsedItem(input, month) =
641 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("month"))?;
642 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
643 let ParsedItem(input, day) =
644 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("day"))?;
645
646 let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
654
655 let ParsedItem(input, hour) =
656 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
657 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
658 let ParsedItem(input, minute) =
659 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
660 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
661 let ParsedItem(input, mut second) =
662 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
663 let ParsedItem(input, mut nanosecond) =
664 if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
665 let ParsedItem(mut input, mut value) = any_digit(input)
666 .ok_or(InvalidComponent("subsecond"))?
667 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
668
669 let mut multiplier = 10_000_000;
670 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
671 value += (digit - b'0').extend::<u32>() * multiplier;
672 input = new_input;
673 multiplier /= 10;
674 }
675
676 ParsedItem(input, value)
677 } else {
678 ParsedItem(input, 0)
679 };
680 let ParsedItem(input, offset) = {
681 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
682 ParsedItem(input, UtcOffset::UTC)
683 } else {
684 let ParsedItem(input, offset_sign) =
685 sign(input).ok_or(InvalidComponent("offset hour"))?;
686 let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
687 .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
688 .ok_or(InvalidComponent("offset hour"))?;
689 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
690 let ParsedItem(input, offset_minute) =
691 exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
692 UtcOffset::from_hms(
693 if offset_sign == b'-' {
694 -offset_hour.cast_signed()
695 } else {
696 offset_hour.cast_signed()
697 },
698 if offset_sign == b'-' {
699 -offset_minute.cast_signed()
700 } else {
701 offset_minute.cast_signed()
702 },
703 0,
704 )
705 .map(|offset| ParsedItem(input, offset))
706 .map_err(|mut err| {
707 if err.name == "hours" {
709 err.name = "offset hour";
710 } else if err.name == "minutes" {
711 err.name = "offset minute";
712 }
713 err
714 })
715 .map_err(TryFromParsed::ComponentRange)?
716 }
717 };
718
719 if !input.is_empty() {
720 return Err(error::Parse::ParseFromDescription(
721 error::ParseFromDescription::UnexpectedTrailingCharacters,
722 ));
723 }
724
725 let leap_second_input = if second == 60 {
729 second = 59;
730 nanosecond = 999_999_999;
731 true
732 } else {
733 false
734 };
735
736 let date = Month::from_number(month)
737 .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day))
738 .map_err(TryFromParsed::ComponentRange)?;
739 let time = Time::from_hms_nano(hour, minute, second, nanosecond)
740 .map_err(TryFromParsed::ComponentRange)?;
741 let dt = OffsetDateTime::new_in_offset(date, time, offset);
742
743 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
744 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
745 error::ComponentRange {
746 name: "second",
747 minimum: 0,
748 maximum: 59,
749 value: 60,
750 conditional_message: Some("because leap seconds are not supported"),
751 },
752 )));
753 }
754
755 Ok(dt)
756 }
757}
758
759impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
760 #[inline]
761 fn parse_into<'a>(
762 &self,
763 mut input: &'a [u8],
764 parsed: &mut Parsed,
765 ) -> Result<&'a [u8], error::Parse> {
766 use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
767
768 let mut extended_kind = ExtendedKind::Unknown;
769 let mut date_is_present = false;
770 let mut time_is_present = false;
771 let mut offset_is_present = false;
772 let mut first_error = None;
773
774 parsed.leap_second_allowed = true;
775
776 match Self::parse_date(parsed, &mut extended_kind)(input) {
777 Ok(new_input) => {
778 input = new_input;
779 date_is_present = true;
780 }
781 Err(err) => {
782 first_error.get_or_insert(err);
783 }
784 }
785
786 match Self::parse_time(parsed, &mut extended_kind, date_is_present)(input) {
787 Ok(new_input) => {
788 input = new_input;
789 time_is_present = true;
790 }
791 Err(err) => {
792 first_error.get_or_insert(err);
793 }
794 }
795
796 if !date_is_present || time_is_present {
798 match Self::parse_offset(parsed, &mut extended_kind)(input) {
799 Ok(new_input) => {
800 input = new_input;
801 offset_is_present = true;
802 }
803 Err(err) => {
804 first_error.get_or_insert(err);
805 }
806 }
807 }
808
809 if !date_is_present && !time_is_present && !offset_is_present {
810 match first_error {
811 Some(err) => return Err(err),
812 None => bug!("an error should be present if no components were parsed"),
813 }
814 }
815
816 Ok(input)
817 }
818}