1use core::num::NonZero;
4
5use deranged::{
6    OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI8, OptionRangedU16,
7    OptionRangedU32, OptionRangedU8, RangedI128, RangedI16, RangedI32, RangedI8, RangedU16,
8    RangedU32, RangedU8,
9};
10use num_conv::prelude::*;
11
12use crate::convert::{Day, Hour, Minute, Nanosecond, Second};
13use crate::date::{MAX_YEAR, MIN_YEAR};
14use crate::error::TryFromParsed::InsufficientInformation;
15#[cfg(feature = "alloc")]
16use crate::format_description::OwnedFormatItem;
17use crate::format_description::{modifier, BorrowedFormatItem, Component};
18use crate::internal_macros::{bug, const_try_opt};
19use crate::parsing::component::{
20    parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
21    parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second,
22    parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period,
23};
24use crate::parsing::ParsedItem;
25use crate::{
26    error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
27};
28
29mod sealed {
31    use super::*;
32
33    pub trait AnyFormatItem {
35        fn parse_item<'a>(
37            &self,
38            parsed: &mut Parsed,
39            input: &'a [u8],
40        ) -> Result<&'a [u8], error::ParseFromDescription>;
41    }
42}
43
44impl sealed::AnyFormatItem for BorrowedFormatItem<'_> {
45    #[inline]
46    fn parse_item<'a>(
47        &self,
48        parsed: &mut Parsed,
49        input: &'a [u8],
50    ) -> Result<&'a [u8], error::ParseFromDescription> {
51        match self {
52            Self::Literal(literal) => Parsed::parse_literal(input, literal),
53            Self::Component(component) => parsed.parse_component(input, *component),
54            Self::Compound(compound) => parsed.parse_items(input, compound),
55            Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
56            Self::First(items) => {
57                let mut first_err = None;
58
59                for item in items.iter() {
60                    match parsed.parse_item(input, item) {
61                        Ok(remaining_input) => return Ok(remaining_input),
62                        Err(err) if first_err.is_none() => first_err = Some(err),
63                        Err(_) => {}
64                    }
65                }
66
67                match first_err {
68                    Some(err) => Err(err),
69                    None => Ok(input),
72                }
73            }
74        }
75    }
76}
77
78#[cfg(feature = "alloc")]
79impl sealed::AnyFormatItem for OwnedFormatItem {
80    #[inline]
81    fn parse_item<'a>(
82        &self,
83        parsed: &mut Parsed,
84        input: &'a [u8],
85    ) -> Result<&'a [u8], error::ParseFromDescription> {
86        match self {
87            Self::Literal(literal) => Parsed::parse_literal(input, literal),
88            Self::Component(component) => parsed.parse_component(input, *component),
89            Self::Compound(compound) => parsed.parse_items(input, compound),
90            Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
91            Self::First(items) => {
92                let mut first_err = None;
93
94                for item in items.iter() {
95                    match parsed.parse_item(input, item) {
96                        Ok(remaining_input) => return Ok(remaining_input),
97                        Err(err) if first_err.is_none() => first_err = Some(err),
98                        Err(_) => {}
99                    }
100                }
101
102                match first_err {
103                    Some(err) => Err(err),
104                    None => Ok(input),
107                }
108            }
109        }
110    }
111}
112
113#[derive(Debug, Clone, Copy)]
120pub struct Parsed {
121    year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
123    year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
125    year_last_two: OptionRangedU8<0, 99>,
127    iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
129    iso_year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
131    iso_year_last_two: OptionRangedU8<0, 99>,
133    month: Option<Month>,
135    sunday_week_number: OptionRangedU8<0, 53>,
137    monday_week_number: OptionRangedU8<0, 53>,
139    iso_week_number: OptionRangedU8<1, 53>,
141    weekday: Option<Weekday>,
143    ordinal: OptionRangedU16<1, 366>,
145    day: OptionRangedU8<1, 31>,
147    hour_24: OptionRangedU8<0, { Hour::per_t::<u8>(Day) - 1 }>,
149    hour_12: OptionRangedU8<1, 12>,
152    hour_12_is_pm: Option<bool>,
154    minute: OptionRangedU8<0, { Minute::per_t::<u8>(Hour) - 1 }>,
156    second: OptionRangedU8<0, { Second::per_t::<u8>(Minute) }>,
159    subsecond: OptionRangedU32<0, { Nanosecond::per_t::<u32>(Second) - 1 }>,
161    offset_hour: OptionRangedI8<-23, 23>,
163    offset_minute:
165        OptionRangedI8<{ -Minute::per_t::<i8>(Hour) + 1 }, { Minute::per_t::<i8>(Hour) - 1 }>,
166    offset_second:
168        OptionRangedI8<{ -Second::per_t::<i8>(Minute) + 1 }, { Second::per_t::<i8>(Minute) - 1 }>,
169    unix_timestamp_nanos: OptionRangedI128<
171        {
172            OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
173                .unix_timestamp_nanos()
174        },
175        {
176            OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
177                .unix_timestamp_nanos()
178        },
179    >,
180    offset_is_negative: bool,
183    year_century_is_negative: bool,
186    iso_year_century_is_negative: bool,
189    pub(super) leap_second_allowed: bool,
192}
193
194impl Default for Parsed {
195    #[inline]
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201impl Parsed {
202    #[inline]
204    pub const fn new() -> Self {
205        Self {
206            year: OptionRangedI32::None,
207            year_century: OptionRangedI16::None,
208            year_last_two: OptionRangedU8::None,
209            iso_year: OptionRangedI32::None,
210            iso_year_century: OptionRangedI16::None,
211            iso_year_last_two: OptionRangedU8::None,
212            month: None,
213            sunday_week_number: OptionRangedU8::None,
214            monday_week_number: OptionRangedU8::None,
215            iso_week_number: OptionRangedU8::None,
216            weekday: None,
217            ordinal: OptionRangedU16::None,
218            day: OptionRangedU8::None,
219            hour_24: OptionRangedU8::None,
220            hour_12: OptionRangedU8::None,
221            hour_12_is_pm: None,
222            minute: OptionRangedU8::None,
223            second: OptionRangedU8::None,
224            subsecond: OptionRangedU32::None,
225            offset_hour: OptionRangedI8::None,
226            offset_minute: OptionRangedI8::None,
227            offset_second: OptionRangedI8::None,
228            unix_timestamp_nanos: OptionRangedI128::None,
229            offset_is_negative: false,
230            year_century_is_negative: false,
231            iso_year_century_is_negative: false,
232            leap_second_allowed: false,
233        }
234    }
235
236    #[inline]
242    pub fn parse_item<'a>(
243        &mut self,
244        input: &'a [u8],
245        item: &impl sealed::AnyFormatItem,
246    ) -> Result<&'a [u8], error::ParseFromDescription> {
247        item.parse_item(self, input)
248    }
249
250    #[inline]
256    pub fn parse_items<'a>(
257        &mut self,
258        mut input: &'a [u8],
259        items: &[impl sealed::AnyFormatItem],
260    ) -> Result<&'a [u8], error::ParseFromDescription> {
261        let mut this = *self;
264        for item in items {
265            input = this.parse_item(input, item)?;
266        }
267        *self = this;
268        Ok(input)
269    }
270
271    #[inline]
273    pub fn parse_literal<'a>(
274        input: &'a [u8],
275        literal: &[u8],
276    ) -> Result<&'a [u8], error::ParseFromDescription> {
277        input
278            .strip_prefix(literal)
279            .ok_or(error::ParseFromDescription::InvalidLiteral)
280    }
281
282    pub fn parse_component<'a>(
285        &mut self,
286        input: &'a [u8],
287        component: Component,
288    ) -> Result<&'a [u8], error::ParseFromDescription> {
289        use error::ParseFromDescription::InvalidComponent;
290
291        match component {
292            Component::Day(modifiers) => parse_day(input, modifiers)
293                .and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
294                .ok_or(InvalidComponent("day")),
295            Component::Month(modifiers) => parse_month(input, modifiers)
296                .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
297                .ok_or(InvalidComponent("month")),
298            Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
299                .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
300                .ok_or(InvalidComponent("ordinal")),
301            Component::Weekday(modifiers) => parse_weekday(input, modifiers)
302                .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
303                .ok_or(InvalidComponent("weekday")),
304            Component::WeekNumber(modifiers) => {
305                let ParsedItem(remaining, value) =
306                    parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
307                match modifiers.repr {
308                    modifier::WeekNumberRepr::Iso => {
309                        NonZero::new(value).and_then(|value| self.set_iso_week_number(value))
310                    }
311                    modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
312                    modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
313                }
314                .ok_or(InvalidComponent("week number"))?;
315                Ok(remaining)
316            }
317            Component::Year(modifiers) => {
318                let ParsedItem(remaining, (value, is_negative)) =
319                    parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
320                match (modifiers.iso_week_based, modifiers.repr) {
321                    (false, modifier::YearRepr::Full) => self.set_year(value),
322                    (false, modifier::YearRepr::Century) => {
323                        self.set_year_century(value.truncate(), is_negative)
324                    }
325                    (false, modifier::YearRepr::LastTwo) => {
326                        self.set_year_last_two(value.cast_unsigned().truncate())
327                    }
328                    (true, modifier::YearRepr::Full) => self.set_iso_year(value),
329                    (true, modifier::YearRepr::Century) => {
330                        self.set_iso_year_century(value.truncate(), is_negative)
331                    }
332                    (true, modifier::YearRepr::LastTwo) => {
333                        self.set_iso_year_last_two(value.cast_unsigned().truncate())
334                    }
335                }
336                .ok_or(InvalidComponent("year"))?;
337                Ok(remaining)
338            }
339            Component::Hour(modifiers) => {
340                let ParsedItem(remaining, value) =
341                    parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
342                if modifiers.is_12_hour_clock {
343                    NonZero::new(value).and_then(|value| self.set_hour_12(value))
344                } else {
345                    self.set_hour_24(value)
346                }
347                .ok_or(InvalidComponent("hour"))?;
348                Ok(remaining)
349            }
350            Component::Minute(modifiers) => parse_minute(input, modifiers)
351                .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
352                .ok_or(InvalidComponent("minute")),
353            Component::Period(modifiers) => parse_period(input, modifiers)
354                .and_then(|parsed| {
355                    parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
356                })
357                .ok_or(InvalidComponent("period")),
358            Component::Second(modifiers) => parse_second(input, modifiers)
359                .and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
360                .ok_or(InvalidComponent("second")),
361            Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
362                .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
363                .ok_or(InvalidComponent("subsecond")),
364            Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
365                .and_then(|parsed| {
366                    parsed.consume_value(|(value, is_negative)| {
367                        self.set_offset_hour(value)?;
368                        self.offset_is_negative = is_negative;
369                        Some(())
370                    })
371                })
372                .ok_or(InvalidComponent("offset hour")),
373            Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
374                .and_then(|parsed| {
375                    parsed.consume_value(|value| self.set_offset_minute_signed(value))
376                })
377                .ok_or(InvalidComponent("offset minute")),
378            Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
379                .and_then(|parsed| {
380                    parsed.consume_value(|value| self.set_offset_second_signed(value))
381                })
382                .ok_or(InvalidComponent("offset second")),
383            Component::Ignore(modifiers) => parse_ignore(input, modifiers)
384                .map(ParsedItem::<()>::into_inner)
385                .ok_or(InvalidComponent("ignore")),
386            Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers)
387                .and_then(|parsed| {
388                    parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
389                })
390                .ok_or(InvalidComponent("unix_timestamp")),
391            Component::End(modifiers) => parse_end(input, modifiers)
392                .map(ParsedItem::<()>::into_inner)
393                .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
394        }
395    }
396}
397
398impl Parsed {
400    #[inline]
402    pub const fn year(&self) -> Option<i32> {
403        self.year.get_primitive()
404    }
405
406    #[inline]
411    pub const fn year_century(&self) -> Option<i16> {
412        self.year_century.get_primitive()
413    }
414
415    #[inline]
420    pub const fn year_century_is_negative(&self) -> Option<bool> {
421        match self.year_century() {
422            Some(_) => Some(self.year_century_is_negative),
423            None => None,
424        }
425    }
426
427    #[inline]
429    pub const fn year_last_two(&self) -> Option<u8> {
430        self.year_last_two.get_primitive()
431    }
432
433    #[inline]
435    pub const fn iso_year(&self) -> Option<i32> {
436        self.iso_year.get_primitive()
437    }
438
439    #[inline]
444    pub const fn iso_year_century(&self) -> Option<i16> {
445        self.iso_year_century.get_primitive()
446    }
447
448    #[inline]
453    pub const fn iso_year_century_is_negative(&self) -> Option<bool> {
454        match self.iso_year_century() {
455            Some(_) => Some(self.iso_year_century_is_negative),
456            None => None,
457        }
458    }
459
460    #[inline]
462    pub const fn iso_year_last_two(&self) -> Option<u8> {
463        self.iso_year_last_two.get_primitive()
464    }
465
466    #[inline]
468    pub const fn month(&self) -> Option<Month> {
469        self.month
470    }
471
472    #[inline]
474    pub const fn sunday_week_number(&self) -> Option<u8> {
475        self.sunday_week_number.get_primitive()
476    }
477
478    #[inline]
480    pub const fn monday_week_number(&self) -> Option<u8> {
481        self.monday_week_number.get_primitive()
482    }
483
484    #[inline]
486    pub const fn iso_week_number(&self) -> Option<NonZero<u8>> {
487        NonZero::new(const_try_opt!(self.iso_week_number.get_primitive()))
488    }
489
490    #[inline]
492    pub const fn weekday(&self) -> Option<Weekday> {
493        self.weekday
494    }
495
496    #[inline]
498    pub const fn ordinal(&self) -> Option<NonZero<u16>> {
499        NonZero::new(const_try_opt!(self.ordinal.get_primitive()))
500    }
501
502    #[inline]
504    pub const fn day(&self) -> Option<NonZero<u8>> {
505        NonZero::new(const_try_opt!(self.day.get_primitive()))
506    }
507
508    #[inline]
510    pub const fn hour_24(&self) -> Option<u8> {
511        self.hour_24.get_primitive()
512    }
513
514    #[inline]
516    pub const fn hour_12(&self) -> Option<NonZero<u8>> {
517        NonZero::new(const_try_opt!(self.hour_12.get_primitive()))
518    }
519
520    #[inline]
522    pub const fn hour_12_is_pm(&self) -> Option<bool> {
523        self.hour_12_is_pm
524    }
525
526    #[inline]
528    pub const fn minute(&self) -> Option<u8> {
529        self.minute.get_primitive()
530    }
531
532    #[inline]
534    pub const fn second(&self) -> Option<u8> {
535        self.second.get_primitive()
536    }
537
538    #[inline]
540    pub const fn subsecond(&self) -> Option<u32> {
541        self.subsecond.get_primitive()
542    }
543
544    #[inline]
546    pub const fn offset_hour(&self) -> Option<i8> {
547        self.offset_hour.get_primitive()
548    }
549
550    #[doc(hidden)]
552    #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
553    #[inline]
554    pub const fn offset_minute(&self) -> Option<u8> {
555        Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
556    }
557
558    #[inline]
560    pub const fn offset_minute_signed(&self) -> Option<i8> {
561        match (self.offset_minute.get_primitive(), self.offset_is_negative) {
562            (Some(offset_minute), true) => Some(-offset_minute),
563            (Some(offset_minute), _) => Some(offset_minute),
564            (None, _) => None,
565        }
566    }
567
568    #[doc(hidden)]
570    #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
571    #[inline]
572    pub const fn offset_second(&self) -> Option<u8> {
573        Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
574    }
575
576    #[inline]
578    pub const fn offset_second_signed(&self) -> Option<i8> {
579        match (self.offset_second.get_primitive(), self.offset_is_negative) {
580            (Some(offset_second), true) => Some(-offset_second),
581            (Some(offset_second), _) => Some(offset_second),
582            (None, _) => None,
583        }
584    }
585
586    #[inline]
588    pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
589        self.unix_timestamp_nanos.get_primitive()
590    }
591}
592
593macro_rules! setters {
595    ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
596        #[doc = concat!("Set the `", stringify!($name), "` component.")]
597        #[inline]
598        pub fn $setter(&mut self, value: $type) -> Option<()> {
599            *self = self.$builder(value)?;
600            Some(())
601        }
602    )*};
603}
604
605impl Parsed {
610    setters! {
611        year set_year with_year i32;
612    }
613
614    #[inline]
619    pub fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
620        self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
621        if value != 0 {
622            self.year_century_is_negative = value.is_negative();
623        } else {
624            self.year_century_is_negative = is_negative;
625        }
626        Some(())
627    }
628
629    setters! {
630        year_last_two set_year_last_two with_year_last_two u8;
631        iso_year set_iso_year with_iso_year i32;
632        iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
633    }
634
635    #[inline]
640    pub fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
641        self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
642        if value != 0 {
643            self.iso_year_century_is_negative = value.is_negative();
644        } else {
645            self.iso_year_century_is_negative = is_negative;
646        }
647        Some(())
648    }
649
650    setters! {
651        month set_month with_month Month;
652        sunday_week_number set_sunday_week_number with_sunday_week_number u8;
653        monday_week_number set_monday_week_number with_monday_week_number u8;
654        iso_week_number set_iso_week_number with_iso_week_number NonZero<u8>;
655        weekday set_weekday with_weekday Weekday;
656        ordinal set_ordinal with_ordinal NonZero<u16>;
657        day set_day with_day NonZero<u8>;
658        hour_24 set_hour_24 with_hour_24 u8;
659        hour_12 set_hour_12 with_hour_12 NonZero<u8>;
660        hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
661        minute set_minute with_minute u8;
662        second set_second with_second u8;
663        subsecond set_subsecond with_subsecond u32;
664        offset_hour set_offset_hour with_offset_hour i8;
665        offset_minute set_offset_minute_signed with_offset_minute_signed i8;
666        offset_second set_offset_second_signed with_offset_second_signed i8;
667        unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
668    }
669
670    #[doc(hidden)]
672    #[deprecated(
673        since = "0.3.8",
674        note = "use `parsed.set_offset_minute_signed()` instead"
675    )]
676    #[inline]
677    pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
678        if value > i8::MAX.cast_unsigned() {
679            None
680        } else {
681            self.set_offset_minute_signed(value.cast_signed())
682        }
683    }
684
685    #[doc(hidden)]
687    #[deprecated(
688        since = "0.3.8",
689        note = "use `parsed.set_offset_second_signed()` instead"
690    )]
691    #[inline]
692    pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
693        if value > i8::MAX.cast_unsigned() {
694            None
695        } else {
696            self.set_offset_second_signed(value.cast_signed())
697        }
698    }
699}
700
701impl Parsed {
706    #[inline]
708    pub const fn with_year(mut self, value: i32) -> Option<Self> {
709        self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
710        Some(self)
711    }
712
713    #[inline]
718    pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
719        self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
720        if value != 0 {
721            self.year_century_is_negative = value.is_negative();
722        } else {
723            self.year_century_is_negative = is_negative;
724        }
725        Some(self)
726    }
727
728    #[inline]
730    pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
731        self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
732        Some(self)
733    }
734
735    #[inline]
737    pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
738        self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
739        Some(self)
740    }
741
742    #[inline]
747    pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
748        self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
749        if value != 0 {
750            self.iso_year_century_is_negative = value.is_negative();
751        } else {
752            self.iso_year_century_is_negative = is_negative;
753        }
754        Some(self)
755    }
756
757    #[inline]
759    pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
760        self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
761        Some(self)
762    }
763
764    #[inline]
766    pub const fn with_month(mut self, value: Month) -> Option<Self> {
767        self.month = Some(value);
768        Some(self)
769    }
770
771    #[inline]
773    pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
774        self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
775        Some(self)
776    }
777
778    #[inline]
780    pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
781        self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
782        Some(self)
783    }
784
785    #[inline]
787    pub const fn with_iso_week_number(mut self, value: NonZero<u8>) -> Option<Self> {
788        self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
789        Some(self)
790    }
791
792    #[inline]
794    pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
795        self.weekday = Some(value);
796        Some(self)
797    }
798
799    #[inline]
801    pub const fn with_ordinal(mut self, value: NonZero<u16>) -> Option<Self> {
802        self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
803        Some(self)
804    }
805
806    #[inline]
808    pub const fn with_day(mut self, value: NonZero<u8>) -> Option<Self> {
809        self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
810        Some(self)
811    }
812
813    #[inline]
815    pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
816        self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
817        Some(self)
818    }
819
820    #[inline]
822    pub const fn with_hour_12(mut self, value: NonZero<u8>) -> Option<Self> {
823        self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
824        Some(self)
825    }
826
827    #[inline]
829    pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
830        self.hour_12_is_pm = Some(value);
831        Some(self)
832    }
833
834    #[inline]
836    pub const fn with_minute(mut self, value: u8) -> Option<Self> {
837        self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
838        Some(self)
839    }
840
841    #[inline]
843    pub const fn with_second(mut self, value: u8) -> Option<Self> {
844        self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
845        Some(self)
846    }
847
848    #[inline]
850    pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
851        self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
852        Some(self)
853    }
854
855    #[inline]
857    pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
858        self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
859        Some(self)
860    }
861
862    #[doc(hidden)]
864    #[deprecated(
865        since = "0.3.8",
866        note = "use `parsed.with_offset_minute_signed()` instead"
867    )]
868    #[inline]
869    pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
870        if value > i8::MAX as u8 {
871            None
872        } else {
873            self.with_offset_minute_signed(value as i8)
874        }
875    }
876
877    #[inline]
879    pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
880        self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
881        Some(self)
882    }
883
884    #[doc(hidden)]
886    #[deprecated(
887        since = "0.3.8",
888        note = "use `parsed.with_offset_second_signed()` instead"
889    )]
890    #[inline]
891    pub const fn with_offset_second(self, value: u8) -> Option<Self> {
892        if value > i8::MAX as u8 {
893            None
894        } else {
895            self.with_offset_second_signed(value as i8)
896        }
897    }
898
899    #[inline]
901    pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
902        self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
903        Some(self)
904    }
905
906    #[inline]
908    pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
909        self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
910        Some(self)
911    }
912}
913
914impl TryFrom<Parsed> for Date {
915    type Error = error::TryFromParsed;
916
917    #[inline]
918    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
919        macro_rules! match_ {
921            (_ => $catch_all:expr $(,)?) => {
922                $catch_all
923            };
924            (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
925                if let ($(Some($name)),*) = ($(parsed.$name()),*) {
926                    $arm
927                } else {
928                    match_!($($rest)*)
929                }
930            };
931        }
932
933        #[inline]
936        const fn adjustment(year: i32) -> i16 {
937            match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
939                Weekday::Monday => 7,
940                Weekday::Tuesday => 1,
941                Weekday::Wednesday => 2,
942                Weekday::Thursday => 3,
943                Weekday::Friday => 4,
944                Weekday::Saturday => 5,
945                Weekday::Sunday => 6,
946            }
947        }
948
949        if let (None, Some(century), Some(is_negative), Some(last_two)) = (
952            parsed.year(),
953            parsed.year_century(),
954            parsed.year_century_is_negative(),
955            parsed.year_last_two(),
956        ) {
957            let year = if is_negative {
958                100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
959            } else {
960                100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
961            };
962            parsed.year = OptionRangedI32::from(RangedI32::new(year));
963        }
964        if let (None, Some(century), Some(is_negative), Some(last_two)) = (
965            parsed.iso_year(),
966            parsed.iso_year_century(),
967            parsed.iso_year_century_is_negative(),
968            parsed.iso_year_last_two(),
969        ) {
970            let iso_year = if is_negative {
971                100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
972            } else {
973                100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
974            };
975            parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year));
976        }
977
978        match_! {
979            (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
980            (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
981            (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
982                iso_year,
983                iso_week_number.get(),
984                weekday,
985            )?),
986            (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
987                year,
988                (sunday_week_number.cast_signed().extend::<i16>() * 7
989                    + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
990                    - adjustment(year)
991                    + 1).cast_unsigned(),
992            )?),
993            (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
994                year,
995                (monday_week_number.cast_signed().extend::<i16>() * 7
996                    + weekday.number_days_from_monday().cast_signed().extend::<i16>()
997                    - adjustment(year)
998                    + 1).cast_unsigned(),
999            )?),
1000            _ => Err(InsufficientInformation),
1001        }
1002    }
1003}
1004
1005impl TryFrom<Parsed> for Time {
1006    type Error = error::TryFromParsed;
1007
1008    #[inline]
1009    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1010        let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
1011            (Some(hour), _, _) => hour,
1012            (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
1013            (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
1014            (_, Some(hour), Some(false)) => hour.get(),
1015            (_, Some(hour), Some(true)) => hour.get() + 12,
1016            _ => return Err(InsufficientInformation),
1017        };
1018
1019        if parsed.hour_24().is_none()
1020            && parsed.hour_12().is_some()
1021            && parsed.hour_12_is_pm().is_some()
1022            && parsed.minute().is_none()
1023            && parsed.second().is_none()
1024            && parsed.subsecond().is_none()
1025        {
1026            return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
1027        }
1028
1029        match (parsed.minute(), parsed.second(), parsed.subsecond()) {
1031            (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
1032            (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
1033            (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
1034            (Some(minute), Some(second), Some(subsecond)) => {
1035                Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
1036            }
1037            _ => Err(InsufficientInformation),
1038        }
1039    }
1040}
1041
1042#[inline]
1043fn utc_offset_try_from_parsed<const REQUIRED: bool>(
1044    parsed: Parsed,
1045) -> Result<UtcOffset, error::TryFromParsed> {
1046    let hour = match (REQUIRED, parsed.offset_hour()) {
1047        (true, None) => return Err(InsufficientInformation),
1049        (false, None) => return Ok(UtcOffset::UTC),
1052        (_, Some(hour)) => hour,
1054    };
1055    let minute = parsed.offset_minute_signed();
1056    let second = minute.and_then(|_| parsed.offset_second_signed());
1058
1059    let minute = minute.unwrap_or(0);
1060    let second = second.unwrap_or(0);
1061
1062    UtcOffset::from_hms(hour, minute, second).map_err(|mut err| {
1063        if err.name == "hours" {
1065            err.name = "offset hour";
1066        } else if err.name == "minutes" {
1067            err.name = "offset minute";
1068        } else if err.name == "seconds" {
1069            err.name = "offset second";
1070        }
1071        err.into()
1072    })
1073}
1074
1075impl TryFrom<Parsed> for UtcOffset {
1076    type Error = error::TryFromParsed;
1077
1078    #[inline]
1079    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1080        utc_offset_try_from_parsed::<true>(parsed)
1081    }
1082}
1083
1084impl TryFrom<Parsed> for PrimitiveDateTime {
1085    type Error = error::TryFromParsed;
1086
1087    #[inline]
1088    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1089        Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
1090    }
1091}
1092
1093impl TryFrom<Parsed> for UtcDateTime {
1094    type Error = error::TryFromParsed;
1095
1096    #[inline]
1097    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1098        if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1099            let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1100            if let Some(subsecond) = parsed.subsecond() {
1101                value = value.replace_nanosecond(subsecond)?;
1102            }
1103            return Ok(value);
1104        }
1105
1106        let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1110            if parsed.set_second(59).is_none() {
1111                bug!("59 is a valid second");
1112            }
1113            if parsed.set_subsecond(999_999_999).is_none() {
1114                bug!("999_999_999 is a valid subsecond");
1115            }
1116            true
1117        } else {
1118            false
1119        };
1120
1121        let dt = OffsetDateTime::new_in_offset(
1122            Date::try_from(parsed)?,
1123            Time::try_from(parsed)?,
1124            utc_offset_try_from_parsed::<false>(parsed)?,
1125        )
1126        .to_utc();
1127
1128        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1129            return Err(error::TryFromParsed::ComponentRange(
1130                error::ComponentRange {
1131                    name: "second",
1132                    minimum: 0,
1133                    maximum: 59,
1134                    value: 60,
1135                    conditional_message: Some("because leap seconds are not supported"),
1136                },
1137            ));
1138        }
1139        Ok(dt)
1140    }
1141}
1142
1143impl TryFrom<Parsed> for OffsetDateTime {
1144    type Error = error::TryFromParsed;
1145
1146    #[inline]
1147    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1148        if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1149            let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1150            if let Some(subsecond) = parsed.subsecond() {
1151                value = value.replace_nanosecond(subsecond)?;
1152            }
1153            return Ok(value);
1154        }
1155
1156        let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1160            if parsed.set_second(59).is_none() {
1161                bug!("59 is a valid second");
1162            }
1163            if parsed.set_subsecond(999_999_999).is_none() {
1164                bug!("999_999_999 is a valid subsecond");
1165            }
1166            true
1167        } else {
1168            false
1169        };
1170
1171        let dt = Self::new_in_offset(
1172            Date::try_from(parsed)?,
1173            Time::try_from(parsed)?,
1174            UtcOffset::try_from(parsed)?,
1175        );
1176
1177        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1178            return Err(error::TryFromParsed::ComponentRange(
1179                error::ComponentRange {
1180                    name: "second",
1181                    minimum: 0,
1182                    maximum: 59,
1183                    value: 60,
1184                    conditional_message: Some("because leap seconds are not supported"),
1185                },
1186            ));
1187        }
1188        Ok(dt)
1189    }
1190}