time/parsing/
parsed.rs

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