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 #[allow(clippy::wildcard_imports)]
36 use super::*;
37 use crate::PrimitiveDateTime;
38
39 pub trait Sealed {
41 fn parse_into<'a>(
45 &self,
46 input: &'a [u8],
47 parsed: &mut Parsed,
48 ) -> Result<&'a [u8], error::Parse>;
49
50 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 fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
67 Ok(self.parse(input)?.try_into()?)
68 }
69
70 fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
72 Ok(self.parse(input)?.try_into()?)
73 }
74
75 fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
77 Ok(self.parse(input)?.try_into()?)
78 }
79
80 fn parse_primitive_date_time(
82 &self,
83 input: &[u8],
84 ) -> Result<PrimitiveDateTime, error::Parse> {
85 Ok(self.parse(input)?.try_into()?)
86 }
87
88 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
90 Ok(self.parse(input)?.try_into()?)
91 }
92 }
93}
94
95impl sealed::Sealed for BorrowedFormatItem<'_> {
97 fn parse_into<'a>(
98 &self,
99 input: &'a [u8],
100 parsed: &mut Parsed,
101 ) -> Result<&'a [u8], error::Parse> {
102 Ok(parsed.parse_item(input, self)?)
103 }
104}
105
106impl sealed::Sealed for [BorrowedFormatItem<'_>] {
107 fn parse_into<'a>(
108 &self,
109 input: &'a [u8],
110 parsed: &mut Parsed,
111 ) -> Result<&'a [u8], error::Parse> {
112 Ok(parsed.parse_items(input, self)?)
113 }
114}
115
116#[cfg(feature = "alloc")]
117impl sealed::Sealed for OwnedFormatItem {
118 fn parse_into<'a>(
119 &self,
120 input: &'a [u8],
121 parsed: &mut Parsed,
122 ) -> Result<&'a [u8], error::Parse> {
123 Ok(parsed.parse_item(input, self)?)
124 }
125}
126
127#[cfg(feature = "alloc")]
128impl sealed::Sealed for [OwnedFormatItem] {
129 fn parse_into<'a>(
130 &self,
131 input: &'a [u8],
132 parsed: &mut Parsed,
133 ) -> Result<&'a [u8], error::Parse> {
134 Ok(parsed.parse_items(input, self)?)
135 }
136}
137
138impl<T: Deref> sealed::Sealed for T
139where
140 T::Target: sealed::Sealed,
141{
142 fn parse_into<'a>(
143 &self,
144 input: &'a [u8],
145 parsed: &mut Parsed,
146 ) -> Result<&'a [u8], error::Parse> {
147 self.deref().parse_into(input, parsed)
148 }
149}
150impl sealed::Sealed for Rfc2822 {
154 fn parse_into<'a>(
155 &self,
156 input: &'a [u8],
157 parsed: &mut Parsed,
158 ) -> Result<&'a [u8], error::Parse> {
159 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
160 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
161 use crate::parsing::combinator::{
162 ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
163 };
164
165 let colon = ascii_char::<b':'>;
166 let comma = ascii_char::<b','>;
167
168 let input = opt(cfws)(input).into_inner();
169 let weekday = first_match(
170 [
171 (b"Mon".as_slice(), Weekday::Monday),
172 (b"Tue".as_slice(), Weekday::Tuesday),
173 (b"Wed".as_slice(), Weekday::Wednesday),
174 (b"Thu".as_slice(), Weekday::Thursday),
175 (b"Fri".as_slice(), Weekday::Friday),
176 (b"Sat".as_slice(), Weekday::Saturday),
177 (b"Sun".as_slice(), Weekday::Sunday),
178 ],
179 false,
180 )(input);
181 let input = if let Some(item) = weekday {
182 let input = item
183 .consume_value(|value| parsed.set_weekday(value))
184 .ok_or(InvalidComponent("weekday"))?;
185 let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
186 opt(cfws)(input).into_inner()
187 } else {
188 input
189 };
190 let input = n_to_m_digits::<1, 2, _>(input)
191 .and_then(|item| item.consume_value(|value| parsed.set_day(value)))
192 .ok_or(InvalidComponent("day"))?;
193 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
194 let input = first_match(
195 [
196 (b"Jan".as_slice(), Month::January),
197 (b"Feb".as_slice(), Month::February),
198 (b"Mar".as_slice(), Month::March),
199 (b"Apr".as_slice(), Month::April),
200 (b"May".as_slice(), Month::May),
201 (b"Jun".as_slice(), Month::June),
202 (b"Jul".as_slice(), Month::July),
203 (b"Aug".as_slice(), Month::August),
204 (b"Sep".as_slice(), Month::September),
205 (b"Oct".as_slice(), Month::October),
206 (b"Nov".as_slice(), Month::November),
207 (b"Dec".as_slice(), Month::December),
208 ],
209 false,
210 )(input)
211 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
212 .ok_or(InvalidComponent("month"))?;
213 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
214 let input = match exactly_n_digits::<4, u32>(input) {
215 Some(item) => {
216 let input = item
217 .flat_map(|year| if year >= 1900 { Some(year) } else { None })
218 .and_then(|item| {
219 item.consume_value(|value| parsed.set_year(value.cast_signed()))
220 })
221 .ok_or(InvalidComponent("year"))?;
222 fws(input).ok_or(InvalidLiteral)?.into_inner()
223 }
224 None => {
225 let input = exactly_n_digits::<2, u32>(input)
226 .and_then(|item| {
227 item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
228 .map(|year| year.cast_signed())
229 .consume_value(|value| parsed.set_year(value))
230 })
231 .ok_or(InvalidComponent("year"))?;
232 cfws(input).ok_or(InvalidLiteral)?.into_inner()
233 }
234 };
235
236 let input = exactly_n_digits::<2, _>(input)
237 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
238 .ok_or(InvalidComponent("hour"))?;
239 let input = opt(cfws)(input).into_inner();
240 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
241 let input = opt(cfws)(input).into_inner();
242 let input = exactly_n_digits::<2, _>(input)
243 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
244 .ok_or(InvalidComponent("minute"))?;
245
246 let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
247 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
249 let input = exactly_n_digits::<2, _>(input)
250 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
251 .ok_or(InvalidComponent("second"))?;
252 cfws(input).ok_or(InvalidLiteral)?.into_inner()
253 } else {
254 cfws(input).ok_or(InvalidLiteral)?.into_inner()
255 };
256
257 parsed.leap_second_allowed = true;
259
260 #[allow(clippy::unnecessary_lazy_evaluations)] let zone_literal = first_match(
262 [
263 (b"UT".as_slice(), 0),
264 (b"GMT".as_slice(), 0),
265 (b"EST".as_slice(), -5),
266 (b"EDT".as_slice(), -4),
267 (b"CST".as_slice(), -6),
268 (b"CDT".as_slice(), -5),
269 (b"MST".as_slice(), -7),
270 (b"MDT".as_slice(), -6),
271 (b"PST".as_slice(), -8),
272 (b"PDT".as_slice(), -7),
273 ],
274 false,
275 )(input)
276 .or_else(|| match input {
277 [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => {
278 Some(ParsedItem(rest, 0))
279 }
280 _ => None,
281 });
282 if let Some(zone_literal) = zone_literal {
283 let input = zone_literal
284 .consume_value(|value| parsed.set_offset_hour(value))
285 .ok_or(InvalidComponent("offset hour"))?;
286 parsed
287 .set_offset_minute_signed(0)
288 .ok_or(InvalidComponent("offset minute"))?;
289 parsed
290 .set_offset_second_signed(0)
291 .ok_or(InvalidComponent("offset second"))?;
292 return Ok(input);
293 }
294
295 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
296 let input = exactly_n_digits::<2, u8>(input)
297 .and_then(|item| {
298 item.map(|offset_hour| {
299 if offset_sign == b'-' {
300 -offset_hour.cast_signed()
301 } else {
302 offset_hour.cast_signed()
303 }
304 })
305 .consume_value(|value| parsed.set_offset_hour(value))
306 })
307 .ok_or(InvalidComponent("offset hour"))?;
308 let input = exactly_n_digits::<2, u8>(input)
309 .and_then(|item| {
310 item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
311 })
312 .ok_or(InvalidComponent("offset minute"))?;
313
314 let input = opt(cfws)(input).into_inner();
315
316 Ok(input)
317 }
318
319 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
320 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
321 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
322 use crate::parsing::combinator::{
323 ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
324 };
325
326 let colon = ascii_char::<b':'>;
327 let comma = ascii_char::<b','>;
328
329 let input = opt(cfws)(input).into_inner();
330 let weekday = first_match(
333 [
334 (b"Mon".as_slice(), ()),
335 (b"Tue".as_slice(), ()),
336 (b"Wed".as_slice(), ()),
337 (b"Thu".as_slice(), ()),
338 (b"Fri".as_slice(), ()),
339 (b"Sat".as_slice(), ()),
340 (b"Sun".as_slice(), ()),
341 ],
342 false,
343 )(input);
344 let input = if let Some(item) = weekday {
345 let input = item.into_inner();
346 let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
347 opt(cfws)(input).into_inner()
348 } else {
349 input
350 };
351 let ParsedItem(input, day) =
352 n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
353 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
354 let ParsedItem(input, month) = first_match(
355 [
356 (b"Jan".as_slice(), Month::January),
357 (b"Feb".as_slice(), Month::February),
358 (b"Mar".as_slice(), Month::March),
359 (b"Apr".as_slice(), Month::April),
360 (b"May".as_slice(), Month::May),
361 (b"Jun".as_slice(), Month::June),
362 (b"Jul".as_slice(), Month::July),
363 (b"Aug".as_slice(), Month::August),
364 (b"Sep".as_slice(), Month::September),
365 (b"Oct".as_slice(), Month::October),
366 (b"Nov".as_slice(), Month::November),
367 (b"Dec".as_slice(), Month::December),
368 ],
369 false,
370 )(input)
371 .ok_or(InvalidComponent("month"))?;
372 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
373 let (input, year) = match exactly_n_digits::<4, u32>(input) {
374 Some(item) => {
375 let ParsedItem(input, year) = item
376 .flat_map(|year| if year >= 1900 { Some(year) } else { None })
377 .ok_or(InvalidComponent("year"))?;
378 let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
379 (input, year)
380 }
381 None => {
382 let ParsedItem(input, year) = exactly_n_digits::<2, u32>(input)
383 .map(|item| item.map(|year| if year < 50 { year + 2000 } else { year + 1900 }))
384 .ok_or(InvalidComponent("year"))?;
385 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
386 (input, year)
387 }
388 };
389
390 let ParsedItem(input, hour) =
391 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
392 let input = opt(cfws)(input).into_inner();
393 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
394 let input = opt(cfws)(input).into_inner();
395 let ParsedItem(input, minute) =
396 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
397
398 let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
399 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
401 let ParsedItem(input, second) =
402 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
403 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
404 (input, second)
405 } else {
406 (cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0)
407 };
408
409 #[allow(clippy::unnecessary_lazy_evaluations)] let zone_literal = first_match(
411 [
412 (b"UT".as_slice(), 0),
413 (b"GMT".as_slice(), 0),
414 (b"EST".as_slice(), -5),
415 (b"EDT".as_slice(), -4),
416 (b"CST".as_slice(), -6),
417 (b"CDT".as_slice(), -5),
418 (b"MST".as_slice(), -7),
419 (b"MDT".as_slice(), -6),
420 (b"PST".as_slice(), -8),
421 (b"PDT".as_slice(), -7),
422 ],
423 false,
424 )(input)
425 .or_else(|| match input {
426 [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => {
427 Some(ParsedItem(rest, 0))
428 }
429 _ => None,
430 });
431
432 let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal {
433 let ParsedItem(input, offset_hour) = zone_literal;
434 (input, offset_hour, 0)
435 } else {
436 let ParsedItem(input, offset_sign) =
437 sign(input).ok_or(InvalidComponent("offset hour"))?;
438 let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
439 .map(|item| {
440 item.map(|offset_hour| {
441 if offset_sign == b'-' {
442 -offset_hour.cast_signed()
443 } else {
444 offset_hour.cast_signed()
445 }
446 })
447 })
448 .ok_or(InvalidComponent("offset hour"))?;
449 let ParsedItem(input, offset_minute) =
450 exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
451 (input, offset_hour, offset_minute.cast_signed())
452 };
453
454 let input = opt(cfws)(input).into_inner();
455
456 if !input.is_empty() {
457 return Err(error::Parse::ParseFromDescription(
458 error::ParseFromDescription::UnexpectedTrailingCharacters,
459 ));
460 }
461
462 let mut nanosecond = 0;
463 let leap_second_input = if second == 60 {
464 second = 59;
465 nanosecond = 999_999_999;
466 true
467 } else {
468 false
469 };
470
471 let dt = (|| {
472 let date = Date::from_calendar_date(year.cast_signed(), month, day)?;
473 let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
474 let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
475 Ok(OffsetDateTime::new_in_offset(date, time, offset))
476 })()
477 .map_err(TryFromParsed::ComponentRange)?;
478
479 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
480 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
481 error::ComponentRange {
482 name: "second",
483 minimum: 0,
484 maximum: 59,
485 value: 60,
486 conditional_range: true,
487 },
488 )));
489 }
490
491 Ok(dt)
492 }
493}
494
495impl sealed::Sealed for Rfc3339 {
496 fn parse_into<'a>(
497 &self,
498 input: &'a [u8],
499 parsed: &mut Parsed,
500 ) -> Result<&'a [u8], error::Parse> {
501 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
502 use crate::parsing::combinator::{
503 any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
504 };
505
506 let dash = ascii_char::<b'-'>;
507 let colon = ascii_char::<b':'>;
508
509 let input = exactly_n_digits::<4, u32>(input)
510 .and_then(|item| item.consume_value(|value| parsed.set_year(value.cast_signed())))
511 .ok_or(InvalidComponent("year"))?;
512 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
513 let input = exactly_n_digits::<2, _>(input)
514 .and_then(|item| item.flat_map(|value| Month::from_number(value).ok()))
515 .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
516 .ok_or(InvalidComponent("month"))?;
517 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
518 let input = exactly_n_digits::<2, _>(input)
519 .and_then(|item| item.consume_value(|value| parsed.set_day(value)))
520 .ok_or(InvalidComponent("day"))?;
521
522 let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
530
531 let input = exactly_n_digits::<2, _>(input)
532 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
533 .ok_or(InvalidComponent("hour"))?;
534 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
535 let input = exactly_n_digits::<2, _>(input)
536 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
537 .ok_or(InvalidComponent("minute"))?;
538 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
539 let input = exactly_n_digits::<2, _>(input)
540 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
541 .ok_or(InvalidComponent("second"))?;
542 let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
543 let ParsedItem(mut input, mut value) = any_digit(input)
544 .ok_or(InvalidComponent("subsecond"))?
545 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
546
547 let mut multiplier = 10_000_000;
548 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
549 value += (digit - b'0').extend::<u32>() * multiplier;
550 input = new_input;
551 multiplier /= 10;
552 }
553
554 parsed
555 .set_subsecond(value)
556 .ok_or(InvalidComponent("subsecond"))?;
557 input
558 } else {
559 input
560 };
561
562 parsed.leap_second_allowed = true;
564
565 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
566 parsed
567 .set_offset_hour(0)
568 .ok_or(InvalidComponent("offset hour"))?;
569 parsed
570 .set_offset_minute_signed(0)
571 .ok_or(InvalidComponent("offset minute"))?;
572 parsed
573 .set_offset_second_signed(0)
574 .ok_or(InvalidComponent("offset second"))?;
575 return Ok(input);
576 }
577
578 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
579 let input = exactly_n_digits::<2, u8>(input)
580 .and_then(|item| {
581 item.filter(|&offset_hour| offset_hour <= 23)?
582 .map(|offset_hour| {
583 if offset_sign == b'-' {
584 -offset_hour.cast_signed()
585 } else {
586 offset_hour.cast_signed()
587 }
588 })
589 .consume_value(|value| parsed.set_offset_hour(value))
590 })
591 .ok_or(InvalidComponent("offset hour"))?;
592 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
593 let input = exactly_n_digits::<2, u8>(input)
594 .and_then(|item| {
595 item.map(|offset_minute| {
596 if offset_sign == b'-' {
597 -offset_minute.cast_signed()
598 } else {
599 offset_minute.cast_signed()
600 }
601 })
602 .consume_value(|value| parsed.set_offset_minute_signed(value))
603 })
604 .ok_or(InvalidComponent("offset minute"))?;
605
606 Ok(input)
607 }
608
609 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
610 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
611 use crate::parsing::combinator::{
612 any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
613 };
614
615 let dash = ascii_char::<b'-'>;
616 let colon = ascii_char::<b':'>;
617
618 let ParsedItem(input, year) =
619 exactly_n_digits::<4, u32>(input).ok_or(InvalidComponent("year"))?;
620 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
621 let ParsedItem(input, month) =
622 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("month"))?;
623 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
624 let ParsedItem(input, day) =
625 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("day"))?;
626
627 let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
635
636 let ParsedItem(input, hour) =
637 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
638 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
639 let ParsedItem(input, minute) =
640 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
641 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
642 let ParsedItem(input, mut second) =
643 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
644 let ParsedItem(input, mut nanosecond) =
645 if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
646 let ParsedItem(mut input, mut value) = any_digit(input)
647 .ok_or(InvalidComponent("subsecond"))?
648 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
649
650 let mut multiplier = 10_000_000;
651 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
652 value += (digit - b'0').extend::<u32>() * multiplier;
653 input = new_input;
654 multiplier /= 10;
655 }
656
657 ParsedItem(input, value)
658 } else {
659 ParsedItem(input, 0)
660 };
661 let ParsedItem(input, offset) = {
662 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
663 ParsedItem(input, UtcOffset::UTC)
664 } else {
665 let ParsedItem(input, offset_sign) =
666 sign(input).ok_or(InvalidComponent("offset hour"))?;
667 let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
668 .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
669 .ok_or(InvalidComponent("offset hour"))?;
670 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
671 let ParsedItem(input, offset_minute) =
672 exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
673 UtcOffset::from_hms(
674 if offset_sign == b'-' {
675 -offset_hour.cast_signed()
676 } else {
677 offset_hour.cast_signed()
678 },
679 if offset_sign == b'-' {
680 -offset_minute.cast_signed()
681 } else {
682 offset_minute.cast_signed()
683 },
684 0,
685 )
686 .map(|offset| ParsedItem(input, offset))
687 .map_err(|mut err| {
688 if err.name == "hours" {
690 err.name = "offset hour";
691 } else if err.name == "minutes" {
692 err.name = "offset minute";
693 }
694 err
695 })
696 .map_err(TryFromParsed::ComponentRange)?
697 }
698 };
699
700 if !input.is_empty() {
701 return Err(error::Parse::ParseFromDescription(
702 error::ParseFromDescription::UnexpectedTrailingCharacters,
703 ));
704 }
705
706 let leap_second_input = if second == 60 {
710 second = 59;
711 nanosecond = 999_999_999;
712 true
713 } else {
714 false
715 };
716
717 let date = Month::from_number(month)
718 .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day))
719 .map_err(TryFromParsed::ComponentRange)?;
720 let time = Time::from_hms_nano(hour, minute, second, nanosecond)
721 .map_err(TryFromParsed::ComponentRange)?;
722 let dt = OffsetDateTime::new_in_offset(date, time, offset);
723
724 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
725 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
726 error::ComponentRange {
727 name: "second",
728 minimum: 0,
729 maximum: 59,
730 value: 60,
731 conditional_range: true,
732 },
733 )));
734 }
735
736 Ok(dt)
737 }
738}
739
740impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
741 fn parse_into<'a>(
742 &self,
743 mut input: &'a [u8],
744 parsed: &mut Parsed,
745 ) -> Result<&'a [u8], error::Parse> {
746 use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
747
748 let mut extended_kind = ExtendedKind::Unknown;
749 let mut date_is_present = false;
750 let mut time_is_present = false;
751 let mut offset_is_present = false;
752 let mut first_error = None;
753
754 parsed.leap_second_allowed = true;
755
756 match Self::parse_date(parsed, &mut extended_kind)(input) {
757 Ok(new_input) => {
758 input = new_input;
759 date_is_present = true;
760 }
761 Err(err) => {
762 first_error.get_or_insert(err);
763 }
764 }
765
766 match Self::parse_time(parsed, &mut extended_kind, date_is_present)(input) {
767 Ok(new_input) => {
768 input = new_input;
769 time_is_present = true;
770 }
771 Err(err) => {
772 first_error.get_or_insert(err);
773 }
774 }
775
776 if !date_is_present || time_is_present {
778 match Self::parse_offset(parsed, &mut extended_kind)(input) {
779 Ok(new_input) => {
780 input = new_input;
781 offset_is_present = true;
782 }
783 Err(err) => {
784 first_error.get_or_insert(err);
785 }
786 }
787 }
788
789 if !date_is_present && !time_is_present && !offset_is_present {
790 match first_error {
791 Some(err) => return Err(err),
792 None => bug!("an error should be present if no components were parsed"),
793 }
794 }
795
796 Ok(input)
797 }
798}
799