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