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