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(__time_03_docs, 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 [
278 b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
279 rest @ ..,
280 ] => Some(ParsedItem(rest, 0)),
281 _ => None,
282 });
283 if let Some(zone_literal) = zone_literal {
284 let input = zone_literal
285 .consume_value(|value| parsed.set_offset_hour(value))
286 .ok_or(InvalidComponent("offset hour"))?;
287 parsed
288 .set_offset_minute_signed(0)
289 .ok_or(InvalidComponent("offset minute"))?;
290 parsed
291 .set_offset_second_signed(0)
292 .ok_or(InvalidComponent("offset second"))?;
293 return Ok(input);
294 }
295
296 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
297 let input = exactly_n_digits::<2, u8>(input)
298 .and_then(|item| {
299 item.map(|offset_hour| {
300 if offset_sign == b'-' {
301 -offset_hour.cast_signed()
302 } else {
303 offset_hour.cast_signed()
304 }
305 })
306 .consume_value(|value| parsed.set_offset_hour(value))
307 })
308 .ok_or(InvalidComponent("offset hour"))?;
309 let input = exactly_n_digits::<2, u8>(input)
310 .and_then(|item| {
311 item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
312 })
313 .ok_or(InvalidComponent("offset minute"))?;
314
315 let input = opt(cfws)(input).into_inner();
316
317 Ok(input)
318 }
319
320 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
321 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
322 use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
323 use crate::parsing::combinator::{
324 ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
325 };
326
327 let colon = ascii_char::<b':'>;
328 let comma = ascii_char::<b','>;
329
330 let input = opt(cfws)(input).into_inner();
331 let weekday = first_match(
334 [
335 (b"Mon".as_slice(), ()),
336 (b"Tue".as_slice(), ()),
337 (b"Wed".as_slice(), ()),
338 (b"Thu".as_slice(), ()),
339 (b"Fri".as_slice(), ()),
340 (b"Sat".as_slice(), ()),
341 (b"Sun".as_slice(), ()),
342 ],
343 false,
344 )(input);
345 let input = if let Some(item) = weekday {
346 let input = item.into_inner();
347 let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
348 opt(cfws)(input).into_inner()
349 } else {
350 input
351 };
352 let ParsedItem(input, day) =
353 n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
354 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
355 let ParsedItem(input, month) = first_match(
356 [
357 (b"Jan".as_slice(), Month::January),
358 (b"Feb".as_slice(), Month::February),
359 (b"Mar".as_slice(), Month::March),
360 (b"Apr".as_slice(), Month::April),
361 (b"May".as_slice(), Month::May),
362 (b"Jun".as_slice(), Month::June),
363 (b"Jul".as_slice(), Month::July),
364 (b"Aug".as_slice(), Month::August),
365 (b"Sep".as_slice(), Month::September),
366 (b"Oct".as_slice(), Month::October),
367 (b"Nov".as_slice(), Month::November),
368 (b"Dec".as_slice(), Month::December),
369 ],
370 false,
371 )(input)
372 .ok_or(InvalidComponent("month"))?;
373 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
374 let (input, year) = match exactly_n_digits::<4, u32>(input) {
375 Some(item) => {
376 let ParsedItem(input, year) = item
377 .flat_map(|year| if year >= 1900 { Some(year) } else { None })
378 .ok_or(InvalidComponent("year"))?;
379 let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
380 (input, year)
381 }
382 None => {
383 let ParsedItem(input, year) = exactly_n_digits::<2, u32>(input)
384 .map(|item| item.map(|year| if year < 50 { year + 2000 } else { year + 1900 }))
385 .ok_or(InvalidComponent("year"))?;
386 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
387 (input, year)
388 }
389 };
390
391 let ParsedItem(input, hour) =
392 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
393 let input = opt(cfws)(input).into_inner();
394 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
395 let input = opt(cfws)(input).into_inner();
396 let ParsedItem(input, minute) =
397 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
398
399 let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
400 let input = input.into_inner(); let input = opt(cfws)(input).into_inner();
402 let ParsedItem(input, second) =
403 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
404 let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
405 (input, second)
406 } else {
407 (cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0)
408 };
409
410 #[allow(clippy::unnecessary_lazy_evaluations)] let zone_literal = first_match(
412 [
413 (b"UT".as_slice(), 0),
414 (b"GMT".as_slice(), 0),
415 (b"EST".as_slice(), -5),
416 (b"EDT".as_slice(), -4),
417 (b"CST".as_slice(), -6),
418 (b"CDT".as_slice(), -5),
419 (b"MST".as_slice(), -7),
420 (b"MDT".as_slice(), -6),
421 (b"PST".as_slice(), -8),
422 (b"PDT".as_slice(), -7),
423 ],
424 false,
425 )(input)
426 .or_else(|| match input {
427 [
428 b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
429 rest @ ..,
430 ] => Some(ParsedItem(rest, 0)),
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_range: true,
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 let input = ascii_char_ignore_case::<b'T'>(input)
524 .ok_or(InvalidLiteral)?
525 .into_inner();
526 let input = exactly_n_digits::<2, _>(input)
527 .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
528 .ok_or(InvalidComponent("hour"))?;
529 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
530 let input = exactly_n_digits::<2, _>(input)
531 .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
532 .ok_or(InvalidComponent("minute"))?;
533 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
534 let input = exactly_n_digits::<2, _>(input)
535 .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
536 .ok_or(InvalidComponent("second"))?;
537 let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
538 let ParsedItem(mut input, mut value) = any_digit(input)
539 .ok_or(InvalidComponent("subsecond"))?
540 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
541
542 let mut multiplier = 10_000_000;
543 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
544 value += (digit - b'0').extend::<u32>() * multiplier;
545 input = new_input;
546 multiplier /= 10;
547 }
548
549 parsed
550 .set_subsecond(value)
551 .ok_or(InvalidComponent("subsecond"))?;
552 input
553 } else {
554 input
555 };
556
557 parsed.leap_second_allowed = true;
559
560 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
561 parsed
562 .set_offset_hour(0)
563 .ok_or(InvalidComponent("offset hour"))?;
564 parsed
565 .set_offset_minute_signed(0)
566 .ok_or(InvalidComponent("offset minute"))?;
567 parsed
568 .set_offset_second_signed(0)
569 .ok_or(InvalidComponent("offset second"))?;
570 return Ok(input);
571 }
572
573 let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
574 let input = exactly_n_digits::<2, u8>(input)
575 .and_then(|item| {
576 item.filter(|&offset_hour| offset_hour <= 23)?
577 .map(|offset_hour| {
578 if offset_sign == b'-' {
579 -offset_hour.cast_signed()
580 } else {
581 offset_hour.cast_signed()
582 }
583 })
584 .consume_value(|value| parsed.set_offset_hour(value))
585 })
586 .ok_or(InvalidComponent("offset hour"))?;
587 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
588 let input = exactly_n_digits::<2, u8>(input)
589 .and_then(|item| {
590 item.map(|offset_minute| {
591 if offset_sign == b'-' {
592 -offset_minute.cast_signed()
593 } else {
594 offset_minute.cast_signed()
595 }
596 })
597 .consume_value(|value| parsed.set_offset_minute_signed(value))
598 })
599 .ok_or(InvalidComponent("offset minute"))?;
600
601 Ok(input)
602 }
603
604 fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
605 use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
606 use crate::parsing::combinator::{
607 any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
608 };
609
610 let dash = ascii_char::<b'-'>;
611 let colon = ascii_char::<b':'>;
612
613 let ParsedItem(input, year) =
614 exactly_n_digits::<4, u32>(input).ok_or(InvalidComponent("year"))?;
615 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
616 let ParsedItem(input, month) =
617 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("month"))?;
618 let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
619 let ParsedItem(input, day) =
620 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("day"))?;
621 let input = ascii_char_ignore_case::<b'T'>(input)
622 .ok_or(InvalidLiteral)?
623 .into_inner();
624 let ParsedItem(input, hour) =
625 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
626 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
627 let ParsedItem(input, minute) =
628 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
629 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
630 let ParsedItem(input, mut second) =
631 exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
632 let ParsedItem(input, mut nanosecond) =
633 if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
634 let ParsedItem(mut input, mut value) = any_digit(input)
635 .ok_or(InvalidComponent("subsecond"))?
636 .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
637
638 let mut multiplier = 10_000_000;
639 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
640 value += (digit - b'0').extend::<u32>() * multiplier;
641 input = new_input;
642 multiplier /= 10;
643 }
644
645 ParsedItem(input, value)
646 } else {
647 ParsedItem(input, 0)
648 };
649 let ParsedItem(input, offset) = {
650 if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
651 ParsedItem(input, UtcOffset::UTC)
652 } else {
653 let ParsedItem(input, offset_sign) =
654 sign(input).ok_or(InvalidComponent("offset hour"))?;
655 let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
656 .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
657 .ok_or(InvalidComponent("offset hour"))?;
658 let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
659 let ParsedItem(input, offset_minute) =
660 exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
661 UtcOffset::from_hms(
662 if offset_sign == b'-' {
663 -offset_hour.cast_signed()
664 } else {
665 offset_hour.cast_signed()
666 },
667 if offset_sign == b'-' {
668 -offset_minute.cast_signed()
669 } else {
670 offset_minute.cast_signed()
671 },
672 0,
673 )
674 .map(|offset| ParsedItem(input, offset))
675 .map_err(|mut err| {
676 if err.name == "hours" {
678 err.name = "offset hour";
679 } else if err.name == "minutes" {
680 err.name = "offset minute";
681 }
682 err
683 })
684 .map_err(TryFromParsed::ComponentRange)?
685 }
686 };
687
688 if !input.is_empty() {
689 return Err(error::Parse::ParseFromDescription(
690 error::ParseFromDescription::UnexpectedTrailingCharacters,
691 ));
692 }
693
694 let leap_second_input = if second == 60 {
698 second = 59;
699 nanosecond = 999_999_999;
700 true
701 } else {
702 false
703 };
704
705 let date = Month::from_number(month)
706 .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day))
707 .map_err(TryFromParsed::ComponentRange)?;
708 let time = Time::from_hms_nano(hour, minute, second, nanosecond)
709 .map_err(TryFromParsed::ComponentRange)?;
710 let dt = OffsetDateTime::new_in_offset(date, time, offset);
711
712 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
713 return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
714 error::ComponentRange {
715 name: "second",
716 minimum: 0,
717 maximum: 59,
718 value: 60,
719 conditional_range: true,
720 },
721 )));
722 }
723
724 Ok(dt)
725 }
726}
727
728impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
729 fn parse_into<'a>(
730 &self,
731 mut input: &'a [u8],
732 parsed: &mut Parsed,
733 ) -> Result<&'a [u8], error::Parse> {
734 use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
735
736 let mut extended_kind = ExtendedKind::Unknown;
737 let mut date_is_present = false;
738 let mut time_is_present = false;
739 let mut offset_is_present = false;
740 let mut first_error = None;
741
742 parsed.leap_second_allowed = true;
743
744 match Self::parse_date(parsed, &mut extended_kind)(input) {
745 Ok(new_input) => {
746 input = new_input;
747 date_is_present = true;
748 }
749 Err(err) => {
750 first_error.get_or_insert(err);
751 }
752 }
753
754 match Self::parse_time(parsed, &mut extended_kind, date_is_present)(input) {
755 Ok(new_input) => {
756 input = new_input;
757 time_is_present = true;
758 }
759 Err(err) => {
760 first_error.get_or_insert(err);
761 }
762 }
763
764 if !date_is_present || time_is_present {
766 match Self::parse_offset(parsed, &mut extended_kind)(input) {
767 Ok(new_input) => {
768 input = new_input;
769 offset_is_present = true;
770 }
771 Err(err) => {
772 first_error.get_or_insert(err);
773 }
774 }
775 }
776
777 if !date_is_present && !time_is_present && !offset_is_present {
778 match first_error {
779 Some(err) => return Err(err),
780 None => bug!("an error should be present if no components were parsed"),
781 }
782 }
783
784 Ok(input)
785 }
786}
787