time/parsing/
parsed.rs

1//! Information parsed from an input and format description.
2
3use core::num::{NonZeroU16, NonZeroU8};
4
5use deranged::{
6    OptionRangedI128, OptionRangedI32, OptionRangedI8, OptionRangedU16, OptionRangedU32,
7    OptionRangedU8, RangedI128, RangedI32, RangedI8, RangedU16, RangedU32, RangedU8,
8};
9use num_conv::prelude::*;
10
11use crate::convert::{Day, Hour, Minute, Nanosecond, Second};
12use crate::date::{MAX_YEAR, MIN_YEAR};
13use crate::error::TryFromParsed::InsufficientInformation;
14#[cfg(feature = "alloc")]
15use crate::format_description::OwnedFormatItem;
16use crate::format_description::{modifier, BorrowedFormatItem, Component};
17use crate::internal_macros::{bug, const_try_opt};
18use crate::parsing::component::{
19    parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
20    parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second,
21    parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period,
22};
23use crate::parsing::ParsedItem;
24use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
25
26/// Sealed to prevent downstream implementations.
27mod sealed {
28    use super::*;
29
30    /// A trait to allow `parse_item` to be generic.
31    pub trait AnyFormatItem {
32        /// Parse a single item, returning the remaining input on success.
33        fn parse_item<'a>(
34            &self,
35            parsed: &mut Parsed,
36            input: &'a [u8],
37        ) -> Result<&'a [u8], error::ParseFromDescription>;
38    }
39}
40
41impl sealed::AnyFormatItem for BorrowedFormatItem<'_> {
42    fn parse_item<'a>(
43        &self,
44        parsed: &mut Parsed,
45        input: &'a [u8],
46    ) -> Result<&'a [u8], error::ParseFromDescription> {
47        match self {
48            Self::Literal(literal) => Parsed::parse_literal(input, literal),
49            Self::Component(component) => parsed.parse_component(input, *component),
50            Self::Compound(compound) => parsed.parse_items(input, compound),
51            Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
52            Self::First(items) => {
53                let mut first_err = None;
54
55                for item in items.iter() {
56                    match parsed.parse_item(input, item) {
57                        Ok(remaining_input) => return Ok(remaining_input),
58                        Err(err) if first_err.is_none() => first_err = Some(err),
59                        Err(_) => {}
60                    }
61                }
62
63                match first_err {
64                    Some(err) => Err(err),
65                    // This location will be reached if the slice is empty, skipping the `for` loop.
66                    // As this case is expected to be uncommon, there's no need to check up front.
67                    None => Ok(input),
68                }
69            }
70        }
71    }
72}
73
74#[cfg(feature = "alloc")]
75impl sealed::AnyFormatItem for OwnedFormatItem {
76    fn parse_item<'a>(
77        &self,
78        parsed: &mut Parsed,
79        input: &'a [u8],
80    ) -> Result<&'a [u8], error::ParseFromDescription> {
81        match self {
82            Self::Literal(literal) => Parsed::parse_literal(input, literal),
83            Self::Component(component) => parsed.parse_component(input, *component),
84            Self::Compound(compound) => parsed.parse_items(input, compound),
85            Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
86            Self::First(items) => {
87                let mut first_err = None;
88
89                for item in items.iter() {
90                    match parsed.parse_item(input, item) {
91                        Ok(remaining_input) => return Ok(remaining_input),
92                        Err(err) if first_err.is_none() => first_err = Some(err),
93                        Err(_) => {}
94                    }
95                }
96
97                match first_err {
98                    Some(err) => Err(err),
99                    // This location will be reached if the slice is empty, skipping the `for` loop.
100                    // As this case is expected to be uncommon, there's no need to check up front.
101                    None => Ok(input),
102                }
103            }
104        }
105    }
106}
107
108/// All information parsed.
109///
110/// This information is directly used to construct the final values.
111///
112/// Most users will not need think about this struct in any way. It is public to allow for manual
113/// control over values, in the instance that the default parser is insufficient.
114#[derive(Debug, Clone, Copy)]
115pub struct Parsed {
116    /// Calendar year.
117    year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
118    /// The last two digits of the calendar year.
119    year_last_two: OptionRangedU8<0, 99>,
120    /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
121    iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
122    /// The last two digits of the ISO week year.
123    iso_year_last_two: OptionRangedU8<0, 99>,
124    /// Month of the year.
125    month: Option<Month>,
126    /// Week of the year, where week one begins on the first Sunday of the calendar year.
127    sunday_week_number: OptionRangedU8<0, 53>,
128    /// Week of the year, where week one begins on the first Monday of the calendar year.
129    monday_week_number: OptionRangedU8<0, 53>,
130    /// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
131    iso_week_number: OptionRangedU8<1, 53>,
132    /// Day of the week.
133    weekday: Option<Weekday>,
134    /// Day of the year.
135    ordinal: OptionRangedU16<1, 366>,
136    /// Day of the month.
137    day: OptionRangedU8<1, 31>,
138    /// Hour within the day.
139    hour_24: OptionRangedU8<0, { Hour::per(Day) - 1 }>,
140    /// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in
141    /// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field.
142    hour_12: OptionRangedU8<1, 12>,
143    /// Whether the `hour_12` field indicates a time that "PM".
144    hour_12_is_pm: Option<bool>,
145    /// Minute within the hour.
146    // minute: MaybeUninit<u8>,
147    minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>,
148    /// Second within the minute.
149    // do not subtract one, as leap seconds may be allowed
150    second: OptionRangedU8<0, { Second::per(Minute) }>,
151    /// Nanosecond within the second.
152    subsecond: OptionRangedU32<0, { Nanosecond::per(Second) - 1 }>,
153    /// Whole hours of the UTC offset.
154    offset_hour: OptionRangedI8<-23, 23>,
155    /// Minutes within the hour of the UTC offset.
156    offset_minute:
157        OptionRangedI8<{ -((Minute::per(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as _ }>,
158    /// Seconds within the minute of the UTC offset.
159    offset_second:
160        OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as _ }>,
161    /// The Unix timestamp in nanoseconds.
162    unix_timestamp_nanos: OptionRangedI128<
163        {
164            OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
165                .unix_timestamp_nanos()
166        },
167        {
168            OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
169                .unix_timestamp_nanos()
170        },
171    >,
172    /// Indicates whether the [`UtcOffset`] is negative. This information is obtained when parsing
173    /// the offset hour, but may not otherwise be stored due to "-0" being equivalent to "0".
174    offset_is_negative: Option<bool>,
175    /// Indicates whether a leap second is permitted to be parsed. This is required by some
176    /// well-known formats.
177    pub(super) leap_second_allowed: bool,
178}
179
180impl Default for Parsed {
181    fn default() -> Self {
182        Self::new()
183    }
184}
185
186impl Parsed {
187    /// Create a new instance of `Parsed` with no information known.
188    pub const fn new() -> Self {
189        Self {
190            year: OptionRangedI32::None,
191            year_last_two: OptionRangedU8::None,
192            iso_year: OptionRangedI32::None,
193            iso_year_last_two: OptionRangedU8::None,
194            month: None,
195            sunday_week_number: OptionRangedU8::None,
196            monday_week_number: OptionRangedU8::None,
197            iso_week_number: OptionRangedU8::None,
198            weekday: None,
199            ordinal: OptionRangedU16::None,
200            day: OptionRangedU8::None,
201            hour_24: OptionRangedU8::None,
202            hour_12: OptionRangedU8::None,
203            hour_12_is_pm: None,
204            minute: OptionRangedU8::None,
205            second: OptionRangedU8::None,
206            subsecond: OptionRangedU32::None,
207            offset_hour: OptionRangedI8::None,
208            offset_minute: OptionRangedI8::None,
209            offset_second: OptionRangedI8::None,
210            unix_timestamp_nanos: OptionRangedI128::None,
211            offset_is_negative: None,
212            leap_second_allowed: false,
213        }
214    }
215
216    /// Parse a single [`BorrowedFormatItem`] or [`OwnedFormatItem`], mutating the struct. The
217    /// remaining input is returned as the `Ok` value.
218    ///
219    /// If a [`BorrowedFormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing
220    /// will not fail; the input will be returned as-is if the expected format is not present.
221    pub fn parse_item<'a>(
222        &mut self,
223        input: &'a [u8],
224        item: &impl sealed::AnyFormatItem,
225    ) -> Result<&'a [u8], error::ParseFromDescription> {
226        item.parse_item(self, input)
227    }
228
229    /// Parse a sequence of [`BorrowedFormatItem`]s or [`OwnedFormatItem`]s, mutating the struct.
230    /// The remaining input is returned as the `Ok` value.
231    ///
232    /// This method will fail if any of the contained [`BorrowedFormatItem`]s or
233    /// [`OwnedFormatItem`]s fail to parse. `self` will not be mutated in this instance.
234    pub fn parse_items<'a>(
235        &mut self,
236        mut input: &'a [u8],
237        items: &[impl sealed::AnyFormatItem],
238    ) -> Result<&'a [u8], error::ParseFromDescription> {
239        // Make a copy that we can mutate. It will only be set to the user's copy if everything
240        // succeeds.
241        let mut this = *self;
242        for item in items {
243            input = this.parse_item(input, item)?;
244        }
245        *self = this;
246        Ok(input)
247    }
248
249    /// Parse a literal byte sequence. The remaining input is returned as the `Ok` value.
250    pub fn parse_literal<'a>(
251        input: &'a [u8],
252        literal: &[u8],
253    ) -> Result<&'a [u8], error::ParseFromDescription> {
254        input
255            .strip_prefix(literal)
256            .ok_or(error::ParseFromDescription::InvalidLiteral)
257    }
258
259    /// Parse a single component, mutating the struct. The remaining input is returned as the `Ok`
260    /// value.
261    pub fn parse_component<'a>(
262        &mut self,
263        input: &'a [u8],
264        component: Component,
265    ) -> Result<&'a [u8], error::ParseFromDescription> {
266        use error::ParseFromDescription::InvalidComponent;
267
268        match component {
269            Component::Day(modifiers) => parse_day(input, modifiers)
270                .and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
271                .ok_or(InvalidComponent("day")),
272            Component::Month(modifiers) => parse_month(input, modifiers)
273                .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
274                .ok_or(InvalidComponent("month")),
275            Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
276                .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
277                .ok_or(InvalidComponent("ordinal")),
278            Component::Weekday(modifiers) => parse_weekday(input, modifiers)
279                .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
280                .ok_or(InvalidComponent("weekday")),
281            Component::WeekNumber(modifiers) => {
282                let ParsedItem(remaining, value) =
283                    parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
284                match modifiers.repr {
285                    modifier::WeekNumberRepr::Iso => {
286                        NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
287                    }
288                    modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
289                    modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
290                }
291                .ok_or(InvalidComponent("week number"))?;
292                Ok(remaining)
293            }
294            Component::Year(modifiers) => {
295                let ParsedItem(remaining, value) =
296                    parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
297                match (modifiers.iso_week_based, modifiers.repr) {
298                    (false, modifier::YearRepr::Full) => self.set_year(value),
299                    (false, modifier::YearRepr::LastTwo) => {
300                        self.set_year_last_two(value.cast_unsigned().truncate())
301                    }
302                    (true, modifier::YearRepr::Full) => self.set_iso_year(value),
303                    (true, modifier::YearRepr::LastTwo) => {
304                        self.set_iso_year_last_two(value.cast_unsigned().truncate())
305                    }
306                }
307                .ok_or(InvalidComponent("year"))?;
308                Ok(remaining)
309            }
310            Component::Hour(modifiers) => {
311                let ParsedItem(remaining, value) =
312                    parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
313                if modifiers.is_12_hour_clock {
314                    NonZeroU8::new(value).and_then(|value| self.set_hour_12(value))
315                } else {
316                    self.set_hour_24(value)
317                }
318                .ok_or(InvalidComponent("hour"))?;
319                Ok(remaining)
320            }
321            Component::Minute(modifiers) => parse_minute(input, modifiers)
322                .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
323                .ok_or(InvalidComponent("minute")),
324            Component::Period(modifiers) => parse_period(input, modifiers)
325                .and_then(|parsed| {
326                    parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
327                })
328                .ok_or(InvalidComponent("period")),
329            Component::Second(modifiers) => parse_second(input, modifiers)
330                .and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
331                .ok_or(InvalidComponent("second")),
332            Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
333                .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
334                .ok_or(InvalidComponent("subsecond")),
335            Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
336                .and_then(|parsed| {
337                    parsed.consume_value(|(value, is_negative)| {
338                        self.set_offset_hour(value)?;
339                        self.offset_is_negative = Some(is_negative);
340                        Some(())
341                    })
342                })
343                .ok_or(InvalidComponent("offset hour")),
344            Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
345                .and_then(|parsed| {
346                    parsed.consume_value(|value| self.set_offset_minute_signed(value))
347                })
348                .ok_or(InvalidComponent("offset minute")),
349            Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
350                .and_then(|parsed| {
351                    parsed.consume_value(|value| self.set_offset_second_signed(value))
352                })
353                .ok_or(InvalidComponent("offset second")),
354            Component::Ignore(modifiers) => parse_ignore(input, modifiers)
355                .map(ParsedItem::<()>::into_inner)
356                .ok_or(InvalidComponent("ignore")),
357            Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers)
358                .and_then(|parsed| {
359                    parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
360                })
361                .ok_or(InvalidComponent("unix_timestamp")),
362            Component::End(modifiers) => parse_end(input, modifiers)
363                .map(ParsedItem::<()>::into_inner)
364                .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
365        }
366    }
367}
368
369/// Getter methods
370impl Parsed {
371    /// Obtain the `year` component.
372    pub const fn year(&self) -> Option<i32> {
373        self.year.get_primitive()
374    }
375
376    /// Obtain the `year_last_two` component.
377    pub const fn year_last_two(&self) -> Option<u8> {
378        self.year_last_two.get_primitive()
379    }
380
381    /// Obtain the `iso_year` component.
382    pub const fn iso_year(&self) -> Option<i32> {
383        self.iso_year.get_primitive()
384    }
385
386    /// Obtain the `iso_year_last_two` component.
387    pub const fn iso_year_last_two(&self) -> Option<u8> {
388        self.iso_year_last_two.get_primitive()
389    }
390
391    /// Obtain the `month` component.
392    pub const fn month(&self) -> Option<Month> {
393        self.month
394    }
395
396    /// Obtain the `sunday_week_number` component.
397    pub const fn sunday_week_number(&self) -> Option<u8> {
398        self.sunday_week_number.get_primitive()
399    }
400
401    /// Obtain the `monday_week_number` component.
402    pub const fn monday_week_number(&self) -> Option<u8> {
403        self.monday_week_number.get_primitive()
404    }
405
406    /// Obtain the `iso_week_number` component.
407    pub const fn iso_week_number(&self) -> Option<NonZeroU8> {
408        NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive()))
409    }
410
411    /// Obtain the `weekday` component.
412    pub const fn weekday(&self) -> Option<Weekday> {
413        self.weekday
414    }
415
416    /// Obtain the `ordinal` component.
417    pub const fn ordinal(&self) -> Option<NonZeroU16> {
418        NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive()))
419    }
420
421    /// Obtain the `day` component.
422    pub const fn day(&self) -> Option<NonZeroU8> {
423        NonZeroU8::new(const_try_opt!(self.day.get_primitive()))
424    }
425
426    /// Obtain the `hour_24` component.
427    pub const fn hour_24(&self) -> Option<u8> {
428        self.hour_24.get_primitive()
429    }
430
431    /// Obtain the `hour_12` component.
432    pub const fn hour_12(&self) -> Option<NonZeroU8> {
433        NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive()))
434    }
435
436    /// Obtain the `hour_12_is_pm` component.
437    pub const fn hour_12_is_pm(&self) -> Option<bool> {
438        self.hour_12_is_pm
439    }
440
441    /// Obtain the `minute` component.
442    pub const fn minute(&self) -> Option<u8> {
443        self.minute.get_primitive()
444    }
445
446    /// Obtain the `second` component.
447    pub const fn second(&self) -> Option<u8> {
448        self.second.get_primitive()
449    }
450
451    /// Obtain the `subsecond` component.
452    pub const fn subsecond(&self) -> Option<u32> {
453        self.subsecond.get_primitive()
454    }
455
456    /// Obtain the `offset_hour` component.
457    pub const fn offset_hour(&self) -> Option<i8> {
458        self.offset_hour.get_primitive()
459    }
460
461    /// Obtain the absolute value of the `offset_minute` component.
462    #[doc(hidden)]
463    #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
464    pub const fn offset_minute(&self) -> Option<u8> {
465        Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
466    }
467
468    /// Obtain the `offset_minute` component.
469    pub const fn offset_minute_signed(&self) -> Option<i8> {
470        match (self.offset_minute.get_primitive(), self.offset_is_negative) {
471            (Some(offset_minute), Some(true)) => Some(-offset_minute),
472            (Some(offset_minute), _) => Some(offset_minute),
473            (None, _) => None,
474        }
475    }
476
477    /// Obtain the absolute value of the `offset_second` component.
478    #[doc(hidden)]
479    #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
480    pub const fn offset_second(&self) -> Option<u8> {
481        Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
482    }
483
484    /// Obtain the `offset_second` component.
485    pub const fn offset_second_signed(&self) -> Option<i8> {
486        match (self.offset_second.get_primitive(), self.offset_is_negative) {
487            (Some(offset_second), Some(true)) => Some(-offset_second),
488            (Some(offset_second), _) => Some(offset_second),
489            (None, _) => None,
490        }
491    }
492
493    /// Obtain the `unix_timestamp_nanos` component.
494    pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
495        self.unix_timestamp_nanos.get_primitive()
496    }
497}
498
499/// Generate setters based on the builders.
500macro_rules! setters {
501    ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
502        #[doc = concat!("Set the `", stringify!($setter), "` component.")]
503        pub fn $setter(&mut self, value: $type) -> Option<()> {
504            *self = self.$builder(value)?;
505            Some(())
506        }
507    )*};
508}
509
510/// Setter methods
511///
512/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The
513/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
514impl Parsed {
515    setters! {
516        year set_year with_year i32;
517        year_last_two set_year_last_two with_year_last_two u8;
518        iso_year set_iso_year with_iso_year i32;
519        iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
520        month set_month with_month Month;
521        sunday_week_number set_sunday_week_number with_sunday_week_number u8;
522        monday_week_number set_monday_week_number with_monday_week_number u8;
523        iso_week_number set_iso_week_number with_iso_week_number NonZeroU8;
524        weekday set_weekday with_weekday Weekday;
525        ordinal set_ordinal with_ordinal NonZeroU16;
526        day set_day with_day NonZeroU8;
527        hour_24 set_hour_24 with_hour_24 u8;
528        hour_12 set_hour_12 with_hour_12 NonZeroU8;
529        hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
530        minute set_minute with_minute u8;
531        second set_second with_second u8;
532        subsecond set_subsecond with_subsecond u32;
533        offset_hour set_offset_hour with_offset_hour i8;
534        offset_minute set_offset_minute_signed with_offset_minute_signed i8;
535        offset_second set_offset_second_signed with_offset_second_signed i8;
536        unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
537    }
538
539    /// Set the `offset_minute` component.
540    #[doc(hidden)]
541    #[deprecated(
542        since = "0.3.8",
543        note = "use `parsed.set_offset_minute_signed()` instead"
544    )]
545    pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
546        if value > i8::MAX.cast_unsigned() {
547            None
548        } else {
549            self.set_offset_minute_signed(value.cast_signed())
550        }
551    }
552
553    /// Set the `offset_minute` component.
554    #[doc(hidden)]
555    #[deprecated(
556        since = "0.3.8",
557        note = "use `parsed.set_offset_second_signed()` instead"
558    )]
559    pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
560        if value > i8::MAX.cast_unsigned() {
561            None
562        } else {
563            self.set_offset_second_signed(value.cast_signed())
564        }
565    }
566}
567
568/// Builder methods
569///
570/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
571/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
572impl Parsed {
573    /// Set the `year` component and return `self`.
574    pub const fn with_year(mut self, value: i32) -> Option<Self> {
575        self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
576        Some(self)
577    }
578
579    /// Set the `year_last_two` component and return `self`.
580    pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
581        self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
582        Some(self)
583    }
584
585    /// Set the `iso_year` component and return `self`.
586    pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
587        self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
588        Some(self)
589    }
590
591    /// Set the `iso_year_last_two` component and return `self`.
592    pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
593        self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
594        Some(self)
595    }
596
597    /// Set the `month` component and return `self`.
598    pub const fn with_month(mut self, value: Month) -> Option<Self> {
599        self.month = Some(value);
600        Some(self)
601    }
602
603    /// Set the `sunday_week_number` component and return `self`.
604    pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
605        self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
606        Some(self)
607    }
608
609    /// Set the `monday_week_number` component and return `self`.
610    pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
611        self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
612        Some(self)
613    }
614
615    /// Set the `iso_week_number` component and return `self`.
616    pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> {
617        self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
618        Some(self)
619    }
620
621    /// Set the `weekday` component and return `self`.
622    pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
623        self.weekday = Some(value);
624        Some(self)
625    }
626
627    /// Set the `ordinal` component and return `self`.
628    pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> {
629        self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
630        Some(self)
631    }
632
633    /// Set the `day` component and return `self`.
634    pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> {
635        self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
636        Some(self)
637    }
638
639    /// Set the `hour_24` component and return `self`.
640    pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
641        self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
642        Some(self)
643    }
644
645    /// Set the `hour_12` component and return `self`.
646    pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> {
647        self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
648        Some(self)
649    }
650
651    /// Set the `hour_12_is_pm` component and return `self`.
652    pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
653        self.hour_12_is_pm = Some(value);
654        Some(self)
655    }
656
657    /// Set the `minute` component and return `self`.
658    pub const fn with_minute(mut self, value: u8) -> Option<Self> {
659        self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
660        Some(self)
661    }
662
663    /// Set the `second` component and return `self`.
664    pub const fn with_second(mut self, value: u8) -> Option<Self> {
665        self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
666        Some(self)
667    }
668
669    /// Set the `subsecond` component and return `self`.
670    pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
671        self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
672        Some(self)
673    }
674
675    /// Set the `offset_hour` component and return `self`.
676    pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
677        self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
678        Some(self)
679    }
680
681    /// Set the `offset_minute` component and return `self`.
682    #[doc(hidden)]
683    #[deprecated(
684        since = "0.3.8",
685        note = "use `parsed.with_offset_minute_signed()` instead"
686    )]
687    pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
688        if value > i8::MAX as u8 {
689            None
690        } else {
691            self.with_offset_minute_signed(value as _)
692        }
693    }
694
695    /// Set the `offset_minute` component and return `self`.
696    pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
697        self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
698        Some(self)
699    }
700
701    /// Set the `offset_minute` component and return `self`.
702    #[doc(hidden)]
703    #[deprecated(
704        since = "0.3.8",
705        note = "use `parsed.with_offset_second_signed()` instead"
706    )]
707    pub const fn with_offset_second(self, value: u8) -> Option<Self> {
708        if value > i8::MAX as u8 {
709            None
710        } else {
711            self.with_offset_second_signed(value as _)
712        }
713    }
714
715    /// Set the `offset_second` component and return `self`.
716    pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
717        self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
718        Some(self)
719    }
720
721    /// Set the `unix_timestamp_nanos` component and return `self`.
722    pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
723        self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
724        Some(self)
725    }
726}
727
728impl TryFrom<Parsed> for Date {
729    type Error = error::TryFromParsed;
730
731    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
732        /// Match on the components that need to be present.
733        macro_rules! match_ {
734            (_ => $catch_all:expr $(,)?) => {
735                $catch_all
736            };
737            (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
738                if let ($(Some($name)),*) = ($(parsed.$name()),*) {
739                    $arm
740                } else {
741                    match_!($($rest)*)
742                }
743            };
744        }
745
746        /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
747        /// numbering.
748        const fn adjustment(year: i32) -> i16 {
749            // Safety: `ordinal` is not zero.
750            match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
751                Weekday::Monday => 7,
752                Weekday::Tuesday => 1,
753                Weekday::Wednesday => 2,
754                Weekday::Thursday => 3,
755                Weekday::Friday => 4,
756                Weekday::Saturday => 5,
757                Weekday::Sunday => 6,
758            }
759        }
760
761        // TODO Only the basics have been covered. There are many other valid values that are not
762        // currently constructed from the information known.
763
764        match_! {
765            (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
766            (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
767            (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
768                iso_year,
769                iso_week_number.get(),
770                weekday,
771            )?),
772            (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
773                year,
774                (sunday_week_number.cast_signed().extend::<i16>() * 7
775                    + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
776                    - adjustment(year)
777                    + 1).cast_unsigned(),
778            )?),
779            (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
780                year,
781                (monday_week_number.cast_signed().extend::<i16>() * 7
782                    + weekday.number_days_from_monday().cast_signed().extend::<i16>()
783                    - adjustment(year)
784                    + 1).cast_unsigned(),
785            )?),
786            _ => Err(InsufficientInformation),
787        }
788    }
789}
790
791impl TryFrom<Parsed> for Time {
792    type Error = error::TryFromParsed;
793
794    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
795        let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
796            (Some(hour), _, _) => hour,
797            (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
798            (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
799            (_, Some(hour), Some(false)) => hour.get(),
800            (_, Some(hour), Some(true)) => hour.get() + 12,
801            _ => return Err(InsufficientInformation),
802        };
803
804        if parsed.hour_24().is_none()
805            && parsed.hour_12().is_some()
806            && parsed.hour_12_is_pm().is_some()
807            && parsed.minute().is_none()
808            && parsed.second().is_none()
809            && parsed.subsecond().is_none()
810        {
811            return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
812        }
813
814        // Reject combinations such as hour-second with minute omitted.
815        match (parsed.minute(), parsed.second(), parsed.subsecond()) {
816            (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
817            (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
818            (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
819            (Some(minute), Some(second), Some(subsecond)) => {
820                Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
821            }
822            _ => Err(InsufficientInformation),
823        }
824    }
825}
826
827impl TryFrom<Parsed> for UtcOffset {
828    type Error = error::TryFromParsed;
829
830    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
831        let hour = parsed.offset_hour().ok_or(InsufficientInformation)?;
832        let minute = parsed.offset_minute_signed().unwrap_or(0);
833        let second = parsed.offset_second_signed().unwrap_or(0);
834
835        Self::from_hms(hour, minute, second).map_err(|mut err| {
836            // Provide the user a more accurate error.
837            if err.name == "hours" {
838                err.name = "offset hour";
839            } else if err.name == "minutes" {
840                err.name = "offset minute";
841            } else if err.name == "seconds" {
842                err.name = "offset second";
843            }
844            err.into()
845        })
846    }
847}
848
849impl TryFrom<Parsed> for PrimitiveDateTime {
850    type Error = error::TryFromParsed;
851
852    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
853        Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
854    }
855}
856
857impl TryFrom<Parsed> for OffsetDateTime {
858    type Error = error::TryFromParsed;
859
860    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
861        if let Some(timestamp) = parsed.unix_timestamp_nanos() {
862            let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
863            if let Some(subsecond) = parsed.subsecond() {
864                value = value.replace_nanosecond(subsecond)?;
865            }
866            return Ok(value);
867        }
868
869        // Some well-known formats explicitly allow leap seconds. We don't currently support them,
870        // so treat it as the nearest preceding moment that can be represented. Because leap seconds
871        // always fall at the end of a month UTC, reject any that are at other times.
872        let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
873            if parsed.set_second(59).is_none() {
874                bug!("59 is a valid second");
875            }
876            if parsed.set_subsecond(999_999_999).is_none() {
877                bug!("999_999_999 is a valid subsecond");
878            }
879            true
880        } else {
881            false
882        };
883
884        let dt = Self::new_in_offset(
885            Date::try_from(parsed)?,
886            Time::try_from(parsed)?,
887            UtcOffset::try_from(parsed)?,
888        );
889
890        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
891            return Err(error::TryFromParsed::ComponentRange(
892                error::ComponentRange {
893                    name: "second",
894                    minimum: 0,
895                    maximum: 59,
896                    value: 60,
897                    conditional_range: true,
898                },
899            ));
900        }
901        Ok(dt)
902    }
903}