1use core::num::NonZero;
4
5use deranged::{
6 Option_ri8, Option_ri16, Option_ri32, Option_ri128, Option_ru8, Option_ru16, Option_ru32, ri8,
7 ri16, ri32, ri128, ru8, ru16, ru32,
8};
9use num_conv::prelude::*;
10
11use crate::date::{MAX_YEAR, MIN_YEAR};
12use crate::error::TryFromParsed::InsufficientInformation;
13#[cfg(feature = "alloc")]
14use crate::format_description::OwnedFormatItem;
15use crate::format_description::{BorrowedFormatItem, Component, Period, modifier};
16use crate::internal_macros::{bug, const_try_opt};
17use crate::parsing::ParsedItem;
18use crate::parsing::component::{
19 parse_calendar_year_century_extended_range, parse_calendar_year_century_standard_range,
20 parse_calendar_year_full_extended_range, parse_calendar_year_full_standard_range,
21 parse_calendar_year_last_two, parse_day, parse_end, parse_hour_12, parse_hour_24, parse_ignore,
22 parse_iso_year_century_extended_range, parse_iso_year_century_standard_range,
23 parse_iso_year_full_extended_range, parse_iso_year_full_standard_range,
24 parse_iso_year_last_two, parse_minute, parse_month_long, parse_month_numerical,
25 parse_month_short, parse_offset_hour, parse_offset_minute, parse_offset_second, parse_ordinal,
26 parse_period, parse_second, parse_subsecond, parse_unix_timestamp_microsecond,
27 parse_unix_timestamp_millisecond, parse_unix_timestamp_nanosecond, parse_unix_timestamp_second,
28 parse_week_number_iso, parse_week_number_monday, parse_week_number_sunday, parse_weekday_long,
29 parse_weekday_monday, parse_weekday_short, parse_weekday_sunday, parse_year,
30};
31use crate::unit::{Day, Hour, Minute, Nanosecond, Second};
32use crate::{
33 Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, error,
34};
35
36mod sealed {
38 use super::*;
39
40 pub trait AnyFormatItem {
42 fn parse_item<'a>(
44 &self,
45 parsed: &mut Parsed,
46 input: &'a [u8],
47 ) -> Result<&'a [u8], error::ParseFromDescription>;
48 }
49}
50
51impl sealed::AnyFormatItem for BorrowedFormatItem<'_> {
52 #[inline(always)]
53 fn parse_item<'a>(
54 &self,
55 parsed: &mut Parsed,
56 input: &'a [u8],
57 ) -> Result<&'a [u8], error::ParseFromDescription> {
58 match self {
59 #[expect(deprecated)]
60 Self::Literal(literal) => Parsed::parse_literal(input, literal),
61 Self::StringLiteral(literal) => Parsed::parse_literal(input, literal.as_bytes()),
62 Self::Component(component) => parsed.parse_component(input, *component),
63 Self::Compound(compound) => parsed.parse_items(input, compound),
64 Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
65 Self::First(items) => {
66 let mut first_err = None;
67
68 for item in items.iter() {
69 match parsed.parse_item(input, item) {
70 Ok(remaining_input) => return Ok(remaining_input),
71 Err(err) if first_err.is_none() => first_err = Some(err),
72 Err(_) => {}
73 }
74 }
75
76 match first_err {
77 Some(err) => Err(err),
78 None => Ok(input),
81 }
82 }
83 }
84 }
85}
86
87#[cfg(feature = "alloc")]
88impl sealed::AnyFormatItem for OwnedFormatItem {
89 #[inline]
90 fn parse_item<'a>(
91 &self,
92 parsed: &mut Parsed,
93 input: &'a [u8],
94 ) -> Result<&'a [u8], error::ParseFromDescription> {
95 match self {
96 #[expect(deprecated)]
97 Self::Literal(literal) => Parsed::parse_literal(input, literal),
98 Self::StringLiteral(literal) => Parsed::parse_literal(input, literal.as_bytes()),
99 Self::Component(component) => parsed.parse_component(input, *component),
100 Self::Compound(compound) => parsed.parse_items(input, compound),
101 Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
102 Self::First(items) => {
103 let mut first_err = None;
104
105 for item in items.iter() {
106 match parsed.parse_item(input, item) {
107 Ok(remaining_input) => return Ok(remaining_input),
108 Err(err) if first_err.is_none() => first_err = Some(err),
109 Err(_) => {}
110 }
111 }
112
113 match first_err {
114 Some(err) => Err(err),
115 None => Ok(input),
118 }
119 }
120 }
121 }
122}
123
124#[derive(Debug, Clone, Copy)]
131pub struct Parsed {
132 year: Option_ri32<{ MIN_YEAR }, { MAX_YEAR }>,
134 year_century: Option_ri16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
136 year_last_two: Option_ru8<0, 99>,
138 iso_year: Option_ri32<{ MIN_YEAR }, { MAX_YEAR }>,
140 iso_year_century: Option_ri16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
142 iso_year_last_two: Option_ru8<0, 99>,
144 month: Option<Month>,
146 sunday_week_number: Option_ru8<0, 53>,
148 monday_week_number: Option_ru8<0, 53>,
150 iso_week_number: Option_ru8<1, 53>,
152 weekday: Option<Weekday>,
154 ordinal: Option_ru16<1, 366>,
156 day: Option_ru8<1, 31>,
158 hour_24: Option_ru8<0, { Hour::per_t::<u8>(Day) - 1 }>,
160 hour_12: Option_ru8<1, 12>,
163 hour_12_is_pm: Option<bool>,
165 minute: Option_ru8<0, { Minute::per_t::<u8>(Hour) - 1 }>,
167 second: Option_ru8<0, { Second::per_t::<u8>(Minute) }>,
170 subsecond: Option_ru32<0, { Nanosecond::per_t::<u32>(Second) - 1 }>,
172 offset_hour: Option_ri8<-23, 23>,
174 offset_minute:
176 Option_ri8<{ -Minute::per_t::<i8>(Hour) + 1 }, { Minute::per_t::<i8>(Hour) - 1 }>,
177 offset_second:
179 Option_ri8<{ -Second::per_t::<i8>(Minute) + 1 }, { Second::per_t::<i8>(Minute) - 1 }>,
180 unix_timestamp_nanos: Option_ri128<
182 {
183 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
184 .unix_timestamp_nanos()
185 },
186 {
187 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
188 .unix_timestamp_nanos()
189 },
190 >,
191 offset_is_negative: bool,
194 year_century_is_negative: bool,
197 iso_year_century_is_negative: bool,
200 pub(super) leap_second_allowed: bool,
203}
204
205impl Default for Parsed {
206 #[inline]
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212impl Parsed {
213 #[inline]
215 pub const fn new() -> Self {
216 Self {
217 year: Option_ri32::None,
218 year_century: Option_ri16::None,
219 year_last_two: Option_ru8::None,
220 iso_year: Option_ri32::None,
221 iso_year_century: Option_ri16::None,
222 iso_year_last_two: Option_ru8::None,
223 month: None,
224 sunday_week_number: Option_ru8::None,
225 monday_week_number: Option_ru8::None,
226 iso_week_number: Option_ru8::None,
227 weekday: None,
228 ordinal: Option_ru16::None,
229 day: Option_ru8::None,
230 hour_24: Option_ru8::None,
231 hour_12: Option_ru8::None,
232 hour_12_is_pm: None,
233 minute: Option_ru8::None,
234 second: Option_ru8::None,
235 subsecond: Option_ru32::None,
236 offset_hour: Option_ri8::None,
237 offset_minute: Option_ri8::None,
238 offset_second: Option_ri8::None,
239 unix_timestamp_nanos: Option_ri128::None,
240 offset_is_negative: false,
241 year_century_is_negative: false,
242 iso_year_century_is_negative: false,
243 leap_second_allowed: false,
244 }
245 }
246
247 #[inline]
253 pub fn parse_item<'a>(
254 &mut self,
255 input: &'a [u8],
256 item: &impl sealed::AnyFormatItem,
257 ) -> Result<&'a [u8], error::ParseFromDescription> {
258 item.parse_item(self, input)
259 }
260
261 #[inline]
267 pub fn parse_items<'a>(
268 &mut self,
269 mut input: &'a [u8],
270 items: &[impl sealed::AnyFormatItem],
271 ) -> Result<&'a [u8], error::ParseFromDescription> {
272 let mut this = *self;
275 for item in items {
276 input = this.parse_item(input, item)?;
277 }
278 *self = this;
279 Ok(input)
280 }
281
282 #[inline]
284 pub fn parse_literal<'a>(
285 input: &'a [u8],
286 literal: &[u8],
287 ) -> Result<&'a [u8], error::ParseFromDescription> {
288 input
289 .strip_prefix(literal)
290 .ok_or(error::ParseFromDescription::InvalidLiteral)
291 }
292
293 #[inline]
296 pub fn parse_component<'a>(
297 &mut self,
298 input: &'a [u8],
299 component: Component,
300 ) -> Result<&'a [u8], error::ParseFromDescription> {
301 use error::ParseFromDescription::InvalidComponent;
302
303 match component {
304 Component::Day(modifiers) => parse_day(input, modifiers)
305 .and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
306 .ok_or(InvalidComponent("day")),
307 Component::MonthShort(modifiers) => parse_month_short(input, modifiers)
308 .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
309 .ok_or(InvalidComponent("month")),
310 Component::MonthLong(modifiers) => parse_month_long(input, modifiers)
311 .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
312 .ok_or(InvalidComponent("month")),
313 Component::MonthNumerical(modifiers) => parse_month_numerical(input, modifiers)
314 .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
315 .ok_or(InvalidComponent("month")),
316 Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
317 .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
318 .ok_or(InvalidComponent("ordinal")),
319 Component::WeekdayShort(modifiers) => parse_weekday_short(input, modifiers)
320 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
321 .ok_or(InvalidComponent("weekday")),
322 Component::WeekdayLong(modifiers) => parse_weekday_long(input, modifiers)
323 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
324 .ok_or(InvalidComponent("weekday")),
325 Component::WeekdaySunday(modifiers) => parse_weekday_sunday(input, modifiers)
326 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
327 .ok_or(InvalidComponent("weekday")),
328 Component::WeekdayMonday(modifiers) => parse_weekday_monday(input, modifiers)
329 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
330 .ok_or(InvalidComponent("weekday")),
331 Component::WeekNumberIso(modifiers) => parse_week_number_iso(input, modifiers)
332 .and_then(|parsed| {
333 parsed.consume_value(|value| self.set_iso_week_number(NonZero::new(value)?))
334 })
335 .ok_or(InvalidComponent("week number")),
336 Component::WeekNumberSunday(modifiers) => parse_week_number_sunday(input, modifiers)
337 .and_then(|parsed| parsed.consume_value(|value| self.set_sunday_week_number(value)))
338 .ok_or(InvalidComponent("week number")),
339 Component::WeekNumberMonday(modifiers) => parse_week_number_monday(input, modifiers)
340 .and_then(|parsed| parsed.consume_value(|value| self.set_monday_week_number(value)))
341 .ok_or(InvalidComponent("week number")),
342 Component::CalendarYearFullExtendedRange(modifiers) => {
343 parse_calendar_year_full_extended_range(input, modifiers)
344 .and_then(|parsed| parsed.consume_value(|value| self.set_year(value)))
345 .ok_or(InvalidComponent("year"))
346 }
347 Component::CalendarYearFullStandardRange(modifiers) => {
348 parse_calendar_year_full_standard_range(input, modifiers)
349 .and_then(|parsed| parsed.consume_value(|value| self.set_year(value)))
350 .ok_or(InvalidComponent("year"))
351 }
352 Component::IsoYearFullExtendedRange(modifiers) => {
353 parse_iso_year_full_extended_range(input, modifiers)
354 .and_then(|parsed| parsed.consume_value(|value| self.set_iso_year(value)))
355 .ok_or(InvalidComponent("year"))
356 }
357 Component::IsoYearFullStandardRange(modifiers) => {
358 parse_iso_year_full_standard_range(input, modifiers)
359 .and_then(|parsed| parsed.consume_value(|value| self.set_iso_year(value)))
360 .ok_or(InvalidComponent("year"))
361 }
362 Component::CalendarYearCenturyExtendedRange(modifiers) => {
363 parse_calendar_year_century_extended_range(input, modifiers)
364 .and_then(|parsed| {
365 parsed.consume_value(|(value, is_negative)| {
366 self.set_year_century(value, is_negative)
367 })
368 })
369 .ok_or(InvalidComponent("year"))
370 }
371 Component::CalendarYearCenturyStandardRange(modifiers) => {
372 parse_calendar_year_century_standard_range(input, modifiers)
373 .and_then(|parsed| {
374 parsed.consume_value(|(value, is_negative)| {
375 self.set_year_century(value, is_negative)
376 })
377 })
378 .ok_or(InvalidComponent("year"))
379 }
380 Component::IsoYearCenturyExtendedRange(modifiers) => {
381 parse_iso_year_century_extended_range(input, modifiers)
382 .and_then(|parsed| {
383 parsed.consume_value(|(value, is_negative)| {
384 self.set_iso_year_century(value, is_negative)
385 })
386 })
387 .ok_or(InvalidComponent("year"))
388 }
389 Component::IsoYearCenturyStandardRange(modifiers) => {
390 parse_iso_year_century_standard_range(input, modifiers)
391 .and_then(|parsed| {
392 parsed.consume_value(|(value, is_negative)| {
393 self.set_iso_year_century(value, is_negative)
394 })
395 })
396 .ok_or(InvalidComponent("year"))
397 }
398 Component::CalendarYearLastTwo(modifiers) => {
399 parse_calendar_year_last_two(input, modifiers)
400 .and_then(|parsed| parsed.consume_value(|value| self.set_year_last_two(value)))
401 .ok_or(InvalidComponent("year"))
402 }
403 Component::IsoYearLastTwo(modifiers) => parse_iso_year_last_two(input, modifiers)
404 .and_then(|parsed| parsed.consume_value(|value| self.set_iso_year_last_two(value)))
405 .ok_or(InvalidComponent("year")),
406 Component::Hour12(modifiers) => parse_hour_12(input, modifiers)
407 .and_then(|parsed| {
408 parsed.consume_value(|value| self.set_hour_12(NonZero::new(value)?))
409 })
410 .ok_or(InvalidComponent("hour")),
411 Component::Hour24(modifiers) => parse_hour_24(input, modifiers)
412 .and_then(|parsed| parsed.consume_value(|value| self.set_hour_24(value)))
413 .ok_or(InvalidComponent("hour")),
414 Component::Minute(modifiers) => parse_minute(input, modifiers)
415 .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
416 .ok_or(InvalidComponent("minute")),
417 Component::Period(modifiers) => parse_period(input, modifiers)
418 .and_then(|parsed| {
419 parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
420 })
421 .ok_or(InvalidComponent("period")),
422 Component::Second(modifiers) => parse_second(input, modifiers)
423 .and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
424 .ok_or(InvalidComponent("second")),
425 Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
426 .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
427 .ok_or(InvalidComponent("subsecond")),
428 Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
429 .and_then(|parsed| {
430 parsed.consume_value(|(value, is_negative)| {
431 self.set_offset_hour(value)?;
432 self.offset_is_negative = is_negative;
433 Some(())
434 })
435 })
436 .ok_or(InvalidComponent("offset hour")),
437 Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
438 .and_then(|parsed| {
439 parsed.consume_value(|value| self.set_offset_minute_signed(value))
440 })
441 .ok_or(InvalidComponent("offset minute")),
442 Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
443 .and_then(|parsed| {
444 parsed.consume_value(|value| self.set_offset_second_signed(value))
445 })
446 .ok_or(InvalidComponent("offset second")),
447 Component::Ignore(modifiers) => parse_ignore(input, modifiers)
448 .map(ParsedItem::<()>::into_inner)
449 .ok_or(InvalidComponent("ignore")),
450 Component::UnixTimestampSecond(modifiers) => {
451 parse_unix_timestamp_second(input, modifiers)
452 .and_then(|parsed| {
453 parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
454 })
455 .ok_or(InvalidComponent("unix_timestamp"))
456 }
457 Component::UnixTimestampMillisecond(modifiers) => {
458 parse_unix_timestamp_millisecond(input, modifiers)
459 .and_then(|parsed| {
460 parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
461 })
462 .ok_or(InvalidComponent("unix_timestamp"))
463 }
464 Component::UnixTimestampMicrosecond(modifiers) => {
465 parse_unix_timestamp_microsecond(input, modifiers)
466 .and_then(|parsed| {
467 parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
468 })
469 .ok_or(InvalidComponent("unix_timestamp"))
470 }
471 Component::UnixTimestampNanosecond(modifiers) => {
472 parse_unix_timestamp_nanosecond(input, modifiers)
473 .and_then(|parsed| {
474 parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
475 })
476 .ok_or(InvalidComponent("unix_timestamp"))
477 }
478 Component::End(modifiers) => parse_end(input, modifiers)
479 .map(ParsedItem::<()>::into_inner)
480 .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
481
482 #[expect(deprecated)]
485 Component::Month(modifiers) => match modifiers.repr {
486 modifier::MonthRepr::Short => parse_month_short(
487 input,
488 modifier::MonthShort {
489 case_sensitive: modifiers.case_sensitive,
490 },
491 ),
492 modifier::MonthRepr::Long => parse_month_long(
493 input,
494 modifier::MonthLong {
495 case_sensitive: modifiers.case_sensitive,
496 },
497 ),
498 modifier::MonthRepr::Numerical => parse_month_numerical(
499 input,
500 modifier::MonthNumerical {
501 padding: modifiers.padding,
502 },
503 ),
504 }
505 .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
506 .ok_or(InvalidComponent("month")),
507 #[expect(deprecated)]
508 Component::Hour(modifiers) => if modifiers.is_12_hour_clock {
509 parse_hour_12(
510 input,
511 modifier::Hour12 {
512 padding: modifiers.padding,
513 },
514 )
515 .and_then(|parsed| {
516 parsed.consume_value(|value| self.set_hour_12(NonZero::new(value)?))
517 })
518 } else {
519 parse_hour_24(
520 input,
521 modifier::Hour24 {
522 padding: modifiers.padding,
523 },
524 )
525 .and_then(|parsed| parsed.consume_value(|value| self.set_hour_24(value)))
526 }
527 .ok_or(InvalidComponent("hour")),
528 #[expect(deprecated)]
529 Component::WeekNumber(modifiers) => match modifiers.repr {
530 modifier::WeekNumberRepr::Iso => parse_week_number_iso(
531 input,
532 modifier::WeekNumberIso {
533 padding: modifiers.padding,
534 },
535 )
536 .and_then(|parsed| {
537 parsed.consume_value(|value| self.set_iso_week_number(NonZero::new(value)?))
538 }),
539 modifier::WeekNumberRepr::Sunday => parse_week_number_sunday(
540 input,
541 modifier::WeekNumberSunday {
542 padding: modifiers.padding,
543 },
544 )
545 .and_then(|parsed| {
546 parsed.consume_value(|value| self.set_sunday_week_number(value))
547 }),
548 modifier::WeekNumberRepr::Monday => parse_week_number_monday(
549 input,
550 modifier::WeekNumberMonday {
551 padding: modifiers.padding,
552 },
553 )
554 .and_then(|parsed| {
555 parsed.consume_value(|value| self.set_monday_week_number(value))
556 }),
557 }
558 .ok_or(InvalidComponent("week number")),
559 #[expect(deprecated)]
560 Component::Weekday(modifiers) => match modifiers.repr {
561 modifier::WeekdayRepr::Short => parse_weekday_short(
562 input,
563 modifier::WeekdayShort {
564 case_sensitive: modifiers.case_sensitive,
565 },
566 ),
567 modifier::WeekdayRepr::Long => parse_weekday_long(
568 input,
569 modifier::WeekdayLong {
570 case_sensitive: modifiers.case_sensitive,
571 },
572 ),
573 modifier::WeekdayRepr::Sunday => parse_weekday_sunday(
574 input,
575 modifier::WeekdaySunday {
576 one_indexed: modifiers.one_indexed,
577 },
578 ),
579 modifier::WeekdayRepr::Monday => parse_weekday_monday(
580 input,
581 modifier::WeekdayMonday {
582 one_indexed: modifiers.one_indexed,
583 },
584 ),
585 }
586 .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
587 .ok_or(InvalidComponent("weekday")),
588 #[expect(deprecated)]
589 Component::UnixTimestamp(modifiers) => match modifiers.precision {
590 modifier::UnixTimestampPrecision::Second => parse_unix_timestamp_second(
591 input,
592 modifier::UnixTimestampSecond {
593 sign_is_mandatory: modifiers.sign_is_mandatory,
594 },
595 ),
596 modifier::UnixTimestampPrecision::Millisecond => parse_unix_timestamp_millisecond(
597 input,
598 modifier::UnixTimestampMillisecond {
599 sign_is_mandatory: modifiers.sign_is_mandatory,
600 },
601 ),
602 modifier::UnixTimestampPrecision::Microsecond => parse_unix_timestamp_microsecond(
603 input,
604 modifier::UnixTimestampMicrosecond {
605 sign_is_mandatory: modifiers.sign_is_mandatory,
606 },
607 ),
608 modifier::UnixTimestampPrecision::Nanosecond => parse_unix_timestamp_nanosecond(
609 input,
610 modifier::UnixTimestampNanosecond {
611 sign_is_mandatory: modifiers.sign_is_mandatory,
612 },
613 ),
614 }
615 .and_then(|parsed| parsed.consume_value(|value| self.set_unix_timestamp_nanos(value)))
616 .ok_or(InvalidComponent("unix_timestamp")),
617 #[expect(deprecated)]
618 Component::Year(modifiers) => {
619 let ParsedItem(remaining, (value, is_negative)) =
620 parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
621 match (modifiers.iso_week_based, modifiers.repr) {
622 (false, modifier::YearRepr::Full) => self.set_year(value),
623 (false, modifier::YearRepr::Century) => {
624 self.set_year_century(value.truncate(), is_negative)
625 }
626 (false, modifier::YearRepr::LastTwo) => {
627 self.set_year_last_two(value.cast_unsigned().truncate())
628 }
629 (true, modifier::YearRepr::Full) => self.set_iso_year(value),
630 (true, modifier::YearRepr::Century) => {
631 self.set_iso_year_century(value.truncate(), is_negative)
632 }
633 (true, modifier::YearRepr::LastTwo) => {
634 self.set_iso_year_last_two(value.cast_unsigned().truncate())
635 }
636 }
637 .ok_or(InvalidComponent("year"))?;
638 Ok(remaining)
639 }
640 }
641 }
642}
643
644impl Parsed {
646 #[inline]
648 pub const fn year(&self) -> Option<i32> {
649 self.year.get_primitive()
650 }
651
652 #[inline]
657 pub const fn year_century(&self) -> Option<i16> {
658 self.year_century.get_primitive()
659 }
660
661 #[inline]
666 pub const fn year_century_is_negative(&self) -> Option<bool> {
667 match self.year_century() {
668 Some(_) => Some(self.year_century_is_negative),
669 None => None,
670 }
671 }
672
673 #[inline]
675 pub const fn year_last_two(&self) -> Option<u8> {
676 self.year_last_two.get_primitive()
677 }
678
679 #[inline]
681 pub const fn iso_year(&self) -> Option<i32> {
682 self.iso_year.get_primitive()
683 }
684
685 #[inline]
690 pub const fn iso_year_century(&self) -> Option<i16> {
691 self.iso_year_century.get_primitive()
692 }
693
694 #[inline]
699 pub const fn iso_year_century_is_negative(&self) -> Option<bool> {
700 match self.iso_year_century() {
701 Some(_) => Some(self.iso_year_century_is_negative),
702 None => None,
703 }
704 }
705
706 #[inline]
708 pub const fn iso_year_last_two(&self) -> Option<u8> {
709 self.iso_year_last_two.get_primitive()
710 }
711
712 #[inline]
714 pub const fn month(&self) -> Option<Month> {
715 self.month
716 }
717
718 #[inline]
720 pub const fn sunday_week_number(&self) -> Option<u8> {
721 self.sunday_week_number.get_primitive()
722 }
723
724 #[inline]
726 pub const fn monday_week_number(&self) -> Option<u8> {
727 self.monday_week_number.get_primitive()
728 }
729
730 #[inline]
732 pub const fn iso_week_number(&self) -> Option<NonZero<u8>> {
733 NonZero::new(const_try_opt!(self.iso_week_number.get_primitive()))
734 }
735
736 #[inline]
738 pub const fn weekday(&self) -> Option<Weekday> {
739 self.weekday
740 }
741
742 #[inline]
744 pub const fn ordinal(&self) -> Option<NonZero<u16>> {
745 NonZero::new(const_try_opt!(self.ordinal.get_primitive()))
746 }
747
748 #[inline]
750 pub const fn day(&self) -> Option<NonZero<u8>> {
751 NonZero::new(const_try_opt!(self.day.get_primitive()))
752 }
753
754 #[inline]
756 pub const fn hour_24(&self) -> Option<u8> {
757 self.hour_24.get_primitive()
758 }
759
760 #[inline]
762 pub const fn hour_12(&self) -> Option<NonZero<u8>> {
763 NonZero::new(const_try_opt!(self.hour_12.get_primitive()))
764 }
765
766 #[inline]
768 pub const fn hour_12_is_pm(&self) -> Option<bool> {
769 self.hour_12_is_pm
770 }
771
772 #[inline]
774 pub const fn minute(&self) -> Option<u8> {
775 self.minute.get_primitive()
776 }
777
778 #[inline]
780 pub const fn second(&self) -> Option<u8> {
781 self.second.get_primitive()
782 }
783
784 #[inline]
786 pub const fn subsecond(&self) -> Option<u32> {
787 self.subsecond.get_primitive()
788 }
789
790 #[inline]
792 pub const fn offset_hour(&self) -> Option<i8> {
793 self.offset_hour.get_primitive()
794 }
795
796 #[doc(hidden)]
798 #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
799 #[inline]
800 pub const fn offset_minute(&self) -> Option<u8> {
801 Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
802 }
803
804 #[inline]
806 pub const fn offset_minute_signed(&self) -> Option<i8> {
807 match (self.offset_minute.get_primitive(), self.offset_is_negative) {
808 (Some(offset_minute), true) => Some(-offset_minute),
809 (Some(offset_minute), _) => Some(offset_minute),
810 (None, _) => None,
811 }
812 }
813
814 #[doc(hidden)]
816 #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
817 #[inline]
818 pub const fn offset_second(&self) -> Option<u8> {
819 Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
820 }
821
822 #[inline]
824 pub const fn offset_second_signed(&self) -> Option<i8> {
825 match (self.offset_second.get_primitive(), self.offset_is_negative) {
826 (Some(offset_second), true) => Some(-offset_second),
827 (Some(offset_second), _) => Some(offset_second),
828 (None, _) => None,
829 }
830 }
831
832 #[inline]
834 pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
835 self.unix_timestamp_nanos.get_primitive()
836 }
837}
838
839macro_rules! setters {
841 ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
842 #[doc = concat!("Set the `", stringify!($name), "` component.")]
843 #[inline]
844 pub const fn $setter(&mut self, value: $type) -> Option<()> {
845 match self.$builder(value) {
846 Some(value) => {
847 *self = value;
848 Some(())
849 },
850 None => None,
851 }
852 }
853 )*};
854}
855
856impl Parsed {
861 setters! {
862 year set_year with_year i32;
863 }
864
865 #[inline]
870 pub const fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
871 self.year_century = Option_ri16::Some(const_try_opt!(ri16::new(value)));
872 if value != 0 {
873 self.year_century_is_negative = value.is_negative();
874 } else {
875 self.year_century_is_negative = is_negative;
876 }
877 Some(())
878 }
879
880 setters! {
881 year_last_two set_year_last_two with_year_last_two u8;
882 iso_year set_iso_year with_iso_year i32;
883 iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
884 }
885
886 #[inline]
891 pub const fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
892 self.iso_year_century = Option_ri16::Some(const_try_opt!(ri16::new(value)));
893 if value != 0 {
894 self.iso_year_century_is_negative = value.is_negative();
895 } else {
896 self.iso_year_century_is_negative = is_negative;
897 }
898 Some(())
899 }
900
901 setters! {
902 month set_month with_month Month;
903 sunday_week_number set_sunday_week_number with_sunday_week_number u8;
904 monday_week_number set_monday_week_number with_monday_week_number u8;
905 iso_week_number set_iso_week_number with_iso_week_number NonZero<u8>;
906 weekday set_weekday with_weekday Weekday;
907 ordinal set_ordinal with_ordinal NonZero<u16>;
908 day set_day with_day NonZero<u8>;
909 hour_24 set_hour_24 with_hour_24 u8;
910 hour_12 set_hour_12 with_hour_12 NonZero<u8>;
911 hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
912 minute set_minute with_minute u8;
913 second set_second with_second u8;
914 subsecond set_subsecond with_subsecond u32;
915 offset_hour set_offset_hour with_offset_hour i8;
916 offset_minute set_offset_minute_signed with_offset_minute_signed i8;
917 offset_second set_offset_second_signed with_offset_second_signed i8;
918 unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
919 }
920
921 #[doc(hidden)]
923 #[deprecated(
924 since = "0.3.8",
925 note = "use `parsed.set_offset_minute_signed()` instead"
926 )]
927 #[inline]
928 pub const fn set_offset_minute(&mut self, value: u8) -> Option<()> {
929 if value > i8::MAX.cast_unsigned() {
930 None
931 } else {
932 self.set_offset_minute_signed(value.cast_signed())
933 }
934 }
935
936 #[doc(hidden)]
938 #[deprecated(
939 since = "0.3.8",
940 note = "use `parsed.set_offset_second_signed()` instead"
941 )]
942 #[inline]
943 pub const fn set_offset_second(&mut self, value: u8) -> Option<()> {
944 if value > i8::MAX.cast_unsigned() {
945 None
946 } else {
947 self.set_offset_second_signed(value.cast_signed())
948 }
949 }
950}
951
952impl Parsed {
957 #[inline]
959 pub const fn with_year(mut self, value: i32) -> Option<Self> {
960 self.year = Option_ri32::Some(const_try_opt!(ri32::new(value)));
961 Some(self)
962 }
963
964 #[inline]
969 pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
970 self.year_century = Option_ri16::Some(const_try_opt!(ri16::new(value)));
971 if value != 0 {
972 self.year_century_is_negative = value.is_negative();
973 } else {
974 self.year_century_is_negative = is_negative;
975 }
976 Some(self)
977 }
978
979 #[inline]
981 pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
982 self.year_last_two = Option_ru8::Some(const_try_opt!(ru8::new(value)));
983 Some(self)
984 }
985
986 #[inline]
988 pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
989 self.iso_year = Option_ri32::Some(const_try_opt!(ri32::new(value)));
990 Some(self)
991 }
992
993 #[inline]
998 pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
999 self.iso_year_century = Option_ri16::Some(const_try_opt!(ri16::new(value)));
1000 if value != 0 {
1001 self.iso_year_century_is_negative = value.is_negative();
1002 } else {
1003 self.iso_year_century_is_negative = is_negative;
1004 }
1005 Some(self)
1006 }
1007
1008 #[inline]
1010 pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
1011 self.iso_year_last_two = Option_ru8::Some(const_try_opt!(ru8::new(value)));
1012 Some(self)
1013 }
1014
1015 #[inline]
1017 pub const fn with_month(mut self, value: Month) -> Option<Self> {
1018 self.month = Some(value);
1019 Some(self)
1020 }
1021
1022 #[inline]
1024 pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
1025 self.sunday_week_number = Option_ru8::Some(const_try_opt!(ru8::new(value)));
1026 Some(self)
1027 }
1028
1029 #[inline]
1031 pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
1032 self.monday_week_number = Option_ru8::Some(const_try_opt!(ru8::new(value)));
1033 Some(self)
1034 }
1035
1036 #[inline]
1038 pub const fn with_iso_week_number(mut self, value: NonZero<u8>) -> Option<Self> {
1039 self.iso_week_number = Option_ru8::Some(const_try_opt!(ru8::new(value.get())));
1040 Some(self)
1041 }
1042
1043 #[inline]
1045 pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
1046 self.weekday = Some(value);
1047 Some(self)
1048 }
1049
1050 #[inline]
1052 pub const fn with_ordinal(mut self, value: NonZero<u16>) -> Option<Self> {
1053 self.ordinal = Option_ru16::Some(const_try_opt!(ru16::new(value.get())));
1054 Some(self)
1055 }
1056
1057 #[inline]
1059 pub const fn with_day(mut self, value: NonZero<u8>) -> Option<Self> {
1060 self.day = Option_ru8::Some(const_try_opt!(ru8::new(value.get())));
1061 Some(self)
1062 }
1063
1064 #[inline]
1066 pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
1067 self.hour_24 = Option_ru8::Some(const_try_opt!(ru8::new(value)));
1068 Some(self)
1069 }
1070
1071 #[inline]
1073 pub const fn with_hour_12(mut self, value: NonZero<u8>) -> Option<Self> {
1074 self.hour_12 = Option_ru8::Some(const_try_opt!(ru8::new(value.get())));
1075 Some(self)
1076 }
1077
1078 #[inline]
1080 pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
1081 self.hour_12_is_pm = Some(value);
1082 Some(self)
1083 }
1084
1085 #[inline]
1087 pub const fn with_minute(mut self, value: u8) -> Option<Self> {
1088 self.minute = Option_ru8::Some(const_try_opt!(ru8::new(value)));
1089 Some(self)
1090 }
1091
1092 #[inline]
1094 pub const fn with_second(mut self, value: u8) -> Option<Self> {
1095 self.second = Option_ru8::Some(const_try_opt!(ru8::new(value)));
1096 Some(self)
1097 }
1098
1099 #[inline]
1101 pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
1102 self.subsecond = Option_ru32::Some(const_try_opt!(ru32::new(value)));
1103 Some(self)
1104 }
1105
1106 #[inline]
1108 pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
1109 self.offset_hour = Option_ri8::Some(const_try_opt!(ri8::new(value)));
1110 Some(self)
1111 }
1112
1113 #[doc(hidden)]
1115 #[deprecated(
1116 since = "0.3.8",
1117 note = "use `parsed.with_offset_minute_signed()` instead"
1118 )]
1119 #[inline]
1120 pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
1121 if value > i8::MAX.cast_unsigned() {
1122 None
1123 } else {
1124 self.with_offset_minute_signed(value.cast_signed())
1125 }
1126 }
1127
1128 #[inline]
1130 pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
1131 self.offset_minute = Option_ri8::Some(const_try_opt!(ri8::new(value)));
1132 Some(self)
1133 }
1134
1135 #[doc(hidden)]
1137 #[deprecated(
1138 since = "0.3.8",
1139 note = "use `parsed.with_offset_second_signed()` instead"
1140 )]
1141 #[inline]
1142 pub const fn with_offset_second(self, value: u8) -> Option<Self> {
1143 if value > i8::MAX.cast_unsigned() {
1144 None
1145 } else {
1146 self.with_offset_second_signed(value.cast_signed())
1147 }
1148 }
1149
1150 #[inline]
1152 pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
1153 self.offset_second = Option_ri8::Some(const_try_opt!(ri8::new(value)));
1154 Some(self)
1155 }
1156
1157 #[inline]
1159 pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
1160 self.unix_timestamp_nanos = Option_ri128::Some(const_try_opt!(ri128::new(value)));
1161 Some(self)
1162 }
1163}
1164
1165impl TryFrom<Parsed> for Date {
1166 type Error = error::TryFromParsed;
1167
1168 #[inline]
1169 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1170 macro_rules! match_ {
1172 (_ => $catch_all:expr $(,)?) => {
1173 $catch_all
1174 };
1175 (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
1176 if let ($(Some($name)),*) = ($(parsed.$name()),*) {
1177 $arm
1178 } else {
1179 match_!($($rest)*)
1180 }
1181 };
1182 }
1183
1184 #[inline]
1187 const fn adjustment(year: i32) -> i16 {
1188 match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
1190 Weekday::Monday => 7,
1191 Weekday::Tuesday => 1,
1192 Weekday::Wednesday => 2,
1193 Weekday::Thursday => 3,
1194 Weekday::Friday => 4,
1195 Weekday::Saturday => 5,
1196 Weekday::Sunday => 6,
1197 }
1198 }
1199
1200 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
1203 parsed.year(),
1204 parsed.year_century(),
1205 parsed.year_century_is_negative(),
1206 parsed.year_last_two(),
1207 ) {
1208 let year = if is_negative {
1209 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
1210 } else {
1211 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
1212 };
1213 parsed.year = Option_ri32::from(ri32::new(year));
1214 }
1215 if let (None, Some(century), Some(is_negative), Some(last_two)) = (
1216 parsed.iso_year(),
1217 parsed.iso_year_century(),
1218 parsed.iso_year_century_is_negative(),
1219 parsed.iso_year_last_two(),
1220 ) {
1221 let iso_year = if is_negative {
1222 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
1223 } else {
1224 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
1225 };
1226 parsed.iso_year = Option_ri32::from(ri32::new(iso_year));
1227 }
1228
1229 match_! {
1230 (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
1231 (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
1232 (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
1233 iso_year,
1234 iso_week_number.get(),
1235 weekday,
1236 )?),
1237 (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
1238 year,
1239 (sunday_week_number.cast_signed().extend::<i16>() * 7
1240 + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
1241 - adjustment(year)
1242 + 1).cast_unsigned(),
1243 )?),
1244 (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
1245 year,
1246 (monday_week_number.cast_signed().extend::<i16>() * 7
1247 + weekday.number_days_from_monday().cast_signed().extend::<i16>()
1248 - adjustment(year)
1249 + 1).cast_unsigned(),
1250 )?),
1251 _ => Err(InsufficientInformation),
1252 }
1253 }
1254}
1255
1256impl TryFrom<Parsed> for Time {
1257 type Error = error::TryFromParsed;
1258
1259 #[inline]
1260 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1261 let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
1262 (Some(hour), _, _) => hour,
1263 (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
1264 (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
1265 (_, Some(hour), Some(false)) => hour.get(),
1266 (_, Some(hour), Some(true)) => hour.get() + 12,
1267 _ => return Err(InsufficientInformation),
1268 };
1269
1270 if parsed.hour_24().is_none()
1271 && parsed.hour_12().is_some()
1272 && parsed.hour_12_is_pm().is_some()
1273 && parsed.minute().is_none()
1274 && parsed.second().is_none()
1275 && parsed.subsecond().is_none()
1276 {
1277 return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
1278 }
1279
1280 match (parsed.minute(), parsed.second(), parsed.subsecond()) {
1282 (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
1283 (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
1284 (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
1285 (Some(minute), Some(second), Some(subsecond)) => {
1286 Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
1287 }
1288 _ => Err(InsufficientInformation),
1289 }
1290 }
1291}
1292
1293#[inline]
1294fn utc_offset_try_from_parsed<const REQUIRED: bool>(
1295 parsed: Parsed,
1296) -> Result<UtcOffset, error::TryFromParsed> {
1297 let hour = match (REQUIRED, parsed.offset_hour()) {
1298 (true, None) => return Err(InsufficientInformation),
1300 (false, None) => return Ok(UtcOffset::UTC),
1303 (_, Some(hour)) => hour,
1305 };
1306 let minute = parsed.offset_minute_signed();
1307 let second = minute.and_then(|_| parsed.offset_second_signed());
1309
1310 let minute = minute.unwrap_or(0);
1311 let second = second.unwrap_or(0);
1312
1313 UtcOffset::from_hms(hour, minute, second).map_err(Into::into)
1314}
1315
1316impl TryFrom<Parsed> for UtcOffset {
1317 type Error = error::TryFromParsed;
1318
1319 #[inline]
1320 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1321 utc_offset_try_from_parsed::<true>(parsed)
1322 }
1323}
1324
1325impl TryFrom<Parsed> for PrimitiveDateTime {
1326 type Error = error::TryFromParsed;
1327
1328 #[inline]
1329 fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1330 Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
1331 }
1332}
1333
1334impl TryFrom<Parsed> for UtcDateTime {
1335 type Error = error::TryFromParsed;
1336
1337 #[inline]
1338 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1339 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1340 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1341 if let Some(subsecond) = parsed.subsecond() {
1342 value = value.replace_nanosecond(subsecond)?;
1343 }
1344 return Ok(value);
1345 }
1346
1347 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1351 if parsed.set_second(59).is_none() {
1352 bug!("59 is a valid second");
1353 }
1354 if parsed.set_subsecond(999_999_999).is_none() {
1355 bug!("999_999_999 is a valid subsecond");
1356 }
1357 true
1358 } else {
1359 false
1360 };
1361
1362 let dt = OffsetDateTime::new_in_offset(
1363 Date::try_from(parsed)?,
1364 Time::try_from(parsed)?,
1365 utc_offset_try_from_parsed::<false>(parsed)?,
1366 )
1367 .to_utc();
1368
1369 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1370 return Err(error::TryFromParsed::ComponentRange(
1371 error::ComponentRange::conditional("second"),
1372 ));
1373 }
1374 Ok(dt)
1375 }
1376}
1377
1378impl TryFrom<Parsed> for OffsetDateTime {
1379 type Error = error::TryFromParsed;
1380
1381 #[inline]
1382 fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1383 if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1384 let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1385 if let Some(subsecond) = parsed.subsecond() {
1386 value = value.replace_nanosecond(subsecond)?;
1387 }
1388 return Ok(value);
1389 }
1390
1391 let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1395 if parsed.set_second(59).is_none() {
1396 bug!("59 is a valid second");
1397 }
1398 if parsed.set_subsecond(999_999_999).is_none() {
1399 bug!("999_999_999 is a valid subsecond");
1400 }
1401 true
1402 } else {
1403 false
1404 };
1405
1406 let dt = Self::new_in_offset(
1407 Date::try_from(parsed)?,
1408 Time::try_from(parsed)?,
1409 UtcOffset::try_from(parsed)?,
1410 );
1411
1412 if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1413 return Err(error::TryFromParsed::ComponentRange(
1414 error::ComponentRange::conditional("second"),
1415 ));
1416 }
1417 Ok(dt)
1418 }
1419}