time/parsing/
parsed.rs

1//! Information parsed from an input and format description.
2
3use 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
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    #[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                    // This location will be reached if the slice is empty, skipping the `for` loop.
70                    // As this case is expected to be uncommon, there's no need to check up front.
71                    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                    // This location will be reached if the slice is empty, skipping the `for` loop.
105                    // As this case is expected to be uncommon, there's no need to check up front.
106                    None => Ok(input),
107                }
108            }
109        }
110    }
111}
112
113/// All information parsed.
114///
115/// This information is directly used to construct the final values.
116///
117/// Most users will not need think about this struct in any way. It is public to allow for manual
118/// control over values, in the instance that the default parser is insufficient.
119#[derive(Debug, Clone, Copy)]
120pub struct Parsed {
121    /// Calendar year.
122    year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
123    /// All digits except the last two of the calendar year.
124    year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
125    /// The last two digits of the calendar year.
126    year_last_two: OptionRangedU8<0, 99>,
127    /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
128    iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
129    /// All digits except the last two of the ISO week year.
130    iso_year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>,
131    /// The last two digits of the ISO week year.
132    iso_year_last_two: OptionRangedU8<0, 99>,
133    /// Month of the year.
134    month: Option<Month>,
135    /// Week of the year, where week one begins on the first Sunday of the calendar year.
136    sunday_week_number: OptionRangedU8<0, 53>,
137    /// Week of the year, where week one begins on the first Monday of the calendar year.
138    monday_week_number: OptionRangedU8<0, 53>,
139    /// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
140    iso_week_number: OptionRangedU8<1, 53>,
141    /// Day of the week.
142    weekday: Option<Weekday>,
143    /// Day of the year.
144    ordinal: OptionRangedU16<1, 366>,
145    /// Day of the month.
146    day: OptionRangedU8<1, 31>,
147    /// Hour within the day.
148    hour_24: OptionRangedU8<0, { Hour::per_t::<u8>(Day) - 1 }>,
149    /// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in
150    /// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field.
151    hour_12: OptionRangedU8<1, 12>,
152    /// Whether the `hour_12` field indicates a time that "PM".
153    hour_12_is_pm: Option<bool>,
154    /// Minute within the hour.
155    minute: OptionRangedU8<0, { Minute::per_t::<u8>(Hour) - 1 }>,
156    /// Second within the minute.
157    // do not subtract one, as leap seconds may be allowed
158    second: OptionRangedU8<0, { Second::per_t::<u8>(Minute) }>,
159    /// Nanosecond within the second.
160    subsecond: OptionRangedU32<0, { Nanosecond::per_t::<u32>(Second) - 1 }>,
161    /// Whole hours of the UTC offset.
162    offset_hour: OptionRangedI8<-23, 23>,
163    /// Minutes within the hour of the UTC offset.
164    offset_minute:
165        OptionRangedI8<{ -Minute::per_t::<i8>(Hour) + 1 }, { Minute::per_t::<i8>(Hour) - 1 }>,
166    /// Seconds within the minute of the UTC offset.
167    offset_second:
168        OptionRangedI8<{ -Second::per_t::<i8>(Minute) + 1 }, { Second::per_t::<i8>(Minute) - 1 }>,
169    /// The Unix timestamp in nanoseconds.
170    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    /// Indicates whether the [`UtcOffset`] is negative. This information is obtained when parsing
181    /// the offset hour, but may not otherwise be stored due to "-0" being equivalent to "0".
182    offset_is_negative: bool,
183    /// Indicates whether the `year_century` component is negative. This information is obtained
184    /// when parsing, but may not otherwise be stored due to "-0" being equivalent to "0".
185    year_century_is_negative: bool,
186    /// Indicates whether the `iso_year_century` component is negative. This information is
187    /// obtained when parsing, but may not otherwise be stored due to "-0" being equivalent to "0".
188    iso_year_century_is_negative: bool,
189    /// Indicates whether a leap second is permitted to be parsed. This is required by some
190    /// well-known formats.
191    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    /// Create a new instance of `Parsed` with no information known.
203    #[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    /// Parse a single [`BorrowedFormatItem`] or [`OwnedFormatItem`], mutating the struct. The
237    /// remaining input is returned as the `Ok` value.
238    ///
239    /// If a [`BorrowedFormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing
240    /// will not fail; the input will be returned as-is if the expected format is not present.
241    #[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    /// Parse a sequence of [`BorrowedFormatItem`]s or [`OwnedFormatItem`]s, mutating the struct.
251    /// The remaining input is returned as the `Ok` value.
252    ///
253    /// This method will fail if any of the contained [`BorrowedFormatItem`]s or
254    /// [`OwnedFormatItem`]s fail to parse. `self` will not be mutated in this instance.
255    #[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        // Make a copy that we can mutate. It will only be set to the user's copy if everything
262        // succeeds.
263        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    /// Parse a literal byte sequence. The remaining input is returned as the `Ok` value.
272    #[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    /// Parse a single component, mutating the struct. The remaining input is returned as the `Ok`
283    /// value.
284    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
398/// Getter methods
399impl Parsed {
400    /// Obtain the `year` component.
401    #[inline]
402    pub const fn year(&self) -> Option<i32> {
403        self.year.get_primitive()
404    }
405
406    /// Obtain the `year_century` component.
407    ///
408    /// If the year is zero, the sign of the century is not stored. To differentiate between
409    /// positive and negative zero, use `year_century_is_negative`.
410    #[inline]
411    pub const fn year_century(&self) -> Option<i16> {
412        self.year_century.get_primitive()
413    }
414
415    /// Obtain the `year_century_is_negative` component.
416    ///
417    /// This indicates whether the value returned from `year_century` is negative. If the year is
418    /// zero, it is necessary to call this method for disambiguation.
419    #[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    /// Obtain the `year_last_two` component.
428    #[inline]
429    pub const fn year_last_two(&self) -> Option<u8> {
430        self.year_last_two.get_primitive()
431    }
432
433    /// Obtain the `iso_year` component.
434    #[inline]
435    pub const fn iso_year(&self) -> Option<i32> {
436        self.iso_year.get_primitive()
437    }
438
439    /// Obtain the `iso_year_century` component.
440    ///
441    /// If the year is zero, the sign of the century is not stored. To differentiate between
442    /// positive and negative zero, use `iso_year_century_is_negative`.
443    #[inline]
444    pub const fn iso_year_century(&self) -> Option<i16> {
445        self.iso_year_century.get_primitive()
446    }
447
448    /// Obtain the `iso_year_century_is_negative` component.
449    ///
450    /// This indicates whether the value returned from `iso_year_century` is negative. If the year
451    /// is zero, it is necessary to call this method for disambiguation.
452    #[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    /// Obtain the `iso_year_last_two` component.
461    #[inline]
462    pub const fn iso_year_last_two(&self) -> Option<u8> {
463        self.iso_year_last_two.get_primitive()
464    }
465
466    /// Obtain the `month` component.
467    #[inline]
468    pub const fn month(&self) -> Option<Month> {
469        self.month
470    }
471
472    /// Obtain the `sunday_week_number` component.
473    #[inline]
474    pub const fn sunday_week_number(&self) -> Option<u8> {
475        self.sunday_week_number.get_primitive()
476    }
477
478    /// Obtain the `monday_week_number` component.
479    #[inline]
480    pub const fn monday_week_number(&self) -> Option<u8> {
481        self.monday_week_number.get_primitive()
482    }
483
484    /// Obtain the `iso_week_number` component.
485    #[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    /// Obtain the `weekday` component.
491    #[inline]
492    pub const fn weekday(&self) -> Option<Weekday> {
493        self.weekday
494    }
495
496    /// Obtain the `ordinal` component.
497    #[inline]
498    pub const fn ordinal(&self) -> Option<NonZero<u16>> {
499        NonZero::new(const_try_opt!(self.ordinal.get_primitive()))
500    }
501
502    /// Obtain the `day` component.
503    #[inline]
504    pub const fn day(&self) -> Option<NonZero<u8>> {
505        NonZero::new(const_try_opt!(self.day.get_primitive()))
506    }
507
508    /// Obtain the `hour_24` component.
509    #[inline]
510    pub const fn hour_24(&self) -> Option<u8> {
511        self.hour_24.get_primitive()
512    }
513
514    /// Obtain the `hour_12` component.
515    #[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    /// Obtain the `hour_12_is_pm` component.
521    #[inline]
522    pub const fn hour_12_is_pm(&self) -> Option<bool> {
523        self.hour_12_is_pm
524    }
525
526    /// Obtain the `minute` component.
527    #[inline]
528    pub const fn minute(&self) -> Option<u8> {
529        self.minute.get_primitive()
530    }
531
532    /// Obtain the `second` component.
533    #[inline]
534    pub const fn second(&self) -> Option<u8> {
535        self.second.get_primitive()
536    }
537
538    /// Obtain the `subsecond` component.
539    #[inline]
540    pub const fn subsecond(&self) -> Option<u32> {
541        self.subsecond.get_primitive()
542    }
543
544    /// Obtain the `offset_hour` component.
545    #[inline]
546    pub const fn offset_hour(&self) -> Option<i8> {
547        self.offset_hour.get_primitive()
548    }
549
550    /// Obtain the absolute value of the `offset_minute` component.
551    #[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    /// Obtain the `offset_minute` component.
559    #[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    /// Obtain the absolute value of the `offset_second` component.
569    #[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    /// Obtain the `offset_second` component.
577    #[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    /// Obtain the `unix_timestamp_nanos` component.
587    #[inline]
588    pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
589        self.unix_timestamp_nanos.get_primitive()
590    }
591}
592
593/// Generate setters based on the builders.
594macro_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
605/// Setter methods
606///
607/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The
608/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
609impl Parsed {
610    setters! {
611        year set_year with_year i32;
612    }
613
614    /// Set the `year_century` component.
615    ///
616    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
617    /// the sign is inferred from the value.
618    #[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    /// Set the `iso_year_century` component.
636    ///
637    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
638    /// the sign is inferred from the value.
639    #[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    /// Set the `offset_minute` component.
671    #[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    /// Set the `offset_minute` component.
686    #[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
701/// Builder methods
702///
703/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
704/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
705impl Parsed {
706    /// Set the `year` component and return `self`.
707    #[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    /// Set the `year_century` component and return `self`.
714    ///
715    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
716    /// the sign is inferred from the value.
717    #[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    /// Set the `year_last_two` component and return `self`.
729    #[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    /// Set the `iso_year` component and return `self`.
736    #[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    /// Set the `iso_year_century` component and return `self`.
743    ///
744    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
745    /// the sign is inferred from the value.
746    #[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    /// Set the `iso_year_last_two` component and return `self`.
758    #[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    /// Set the `month` component and return `self`.
765    #[inline]
766    pub const fn with_month(mut self, value: Month) -> Option<Self> {
767        self.month = Some(value);
768        Some(self)
769    }
770
771    /// Set the `sunday_week_number` component and return `self`.
772    #[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    /// Set the `monday_week_number` component and return `self`.
779    #[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    /// Set the `iso_week_number` component and return `self`.
786    #[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    /// Set the `weekday` component and return `self`.
793    #[inline]
794    pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
795        self.weekday = Some(value);
796        Some(self)
797    }
798
799    /// Set the `ordinal` component and return `self`.
800    #[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    /// Set the `day` component and return `self`.
807    #[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    /// Set the `hour_24` component and return `self`.
814    #[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    /// Set the `hour_12` component and return `self`.
821    #[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    /// Set the `hour_12_is_pm` component and return `self`.
828    #[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    /// Set the `minute` component and return `self`.
835    #[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    /// Set the `second` component and return `self`.
842    #[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    /// Set the `subsecond` component and return `self`.
849    #[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    /// Set the `offset_hour` component and return `self`.
856    #[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    /// Set the `offset_minute` component and return `self`.
863    #[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    /// Set the `offset_minute` component and return `self`.
878    #[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    /// Set the `offset_minute` component and return `self`.
885    #[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    /// Set the `offset_second` component and return `self`.
900    #[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    /// Set the `unix_timestamp_nanos` component and return `self`.
907    #[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        /// Match on the components that need to be present.
920        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        /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
934        /// numbering.
935        #[inline]
936        const fn adjustment(year: i32) -> i16 {
937            // Safety: `ordinal` is not zero.
938            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 we do not have the year but we have *both* the century and the last two digits, we can
950        // construct the year. Likewise for the ISO year.
951        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        // Reject combinations such as hour-second with minute omitted.
1030        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        // An offset is required, but the hour is missing. Return an error.
1048        (true, None) => return Err(InsufficientInformation),
1049        // An offset is not required (e.g. for `UtcDateTime`). As the hour is missing, minutes and
1050        // seconds are not parsed. This is UTC.
1051        (false, None) => return Ok(UtcOffset::UTC),
1052        // Any other situation has an hour present.
1053        (_, Some(hour)) => hour,
1054    };
1055    let minute = parsed.offset_minute_signed();
1056    // Force `second` to be `None` if `minute` is `None`.
1057    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        // Provide the user a more accurate error.
1064        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        // Some well-known formats explicitly allow leap seconds. We don't currently support them,
1107        // so treat it as the nearest preceding moment that can be represented. Because leap seconds
1108        // always fall at the end of a month UTC, reject any that are at other times.
1109        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        // Some well-known formats explicitly allow leap seconds. We don't currently support them,
1157        // so treat it as the nearest preceding moment that can be represented. Because leap seconds
1158        // always fall at the end of a month UTC, reject any that are at other times.
1159        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}