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 const fn $setter(&mut self, value: $type) -> Option<()> {
599            match self.$builder(value) {
600                Some(value) => {
601                    *self = value;
602                    Some(())
603                },
604                None => None,
605            }
606        }
607    )*};
608}
609
610/// Setter methods
611///
612/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The
613/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
614impl Parsed {
615    setters! {
616        year set_year with_year i32;
617    }
618
619    /// Set the `year_century` component.
620    ///
621    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
622    /// the sign is inferred from the value.
623    #[inline]
624    pub const fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
625        self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
626        if value != 0 {
627            self.year_century_is_negative = value.is_negative();
628        } else {
629            self.year_century_is_negative = is_negative;
630        }
631        Some(())
632    }
633
634    setters! {
635        year_last_two set_year_last_two with_year_last_two u8;
636        iso_year set_iso_year with_iso_year i32;
637        iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
638    }
639
640    /// Set the `iso_year_century` component.
641    ///
642    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
643    /// the sign is inferred from the value.
644    #[inline]
645    pub const fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> {
646        self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
647        if value != 0 {
648            self.iso_year_century_is_negative = value.is_negative();
649        } else {
650            self.iso_year_century_is_negative = is_negative;
651        }
652        Some(())
653    }
654
655    setters! {
656        month set_month with_month Month;
657        sunday_week_number set_sunday_week_number with_sunday_week_number u8;
658        monday_week_number set_monday_week_number with_monday_week_number u8;
659        iso_week_number set_iso_week_number with_iso_week_number NonZero<u8>;
660        weekday set_weekday with_weekday Weekday;
661        ordinal set_ordinal with_ordinal NonZero<u16>;
662        day set_day with_day NonZero<u8>;
663        hour_24 set_hour_24 with_hour_24 u8;
664        hour_12 set_hour_12 with_hour_12 NonZero<u8>;
665        hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
666        minute set_minute with_minute u8;
667        second set_second with_second u8;
668        subsecond set_subsecond with_subsecond u32;
669        offset_hour set_offset_hour with_offset_hour i8;
670        offset_minute set_offset_minute_signed with_offset_minute_signed i8;
671        offset_second set_offset_second_signed with_offset_second_signed i8;
672        unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
673    }
674
675    /// Set the `offset_minute` component.
676    #[doc(hidden)]
677    #[deprecated(
678        since = "0.3.8",
679        note = "use `parsed.set_offset_minute_signed()` instead"
680    )]
681    #[inline]
682    pub const fn set_offset_minute(&mut self, value: u8) -> Option<()> {
683        if value > i8::MAX as u8 {
684            None
685        } else {
686            self.set_offset_minute_signed(value as i8)
687        }
688    }
689
690    /// Set the `offset_minute` component.
691    #[doc(hidden)]
692    #[deprecated(
693        since = "0.3.8",
694        note = "use `parsed.set_offset_second_signed()` instead"
695    )]
696    #[inline]
697    pub const fn set_offset_second(&mut self, value: u8) -> Option<()> {
698        if value > i8::MAX as u8 {
699            None
700        } else {
701            self.set_offset_second_signed(value as i8)
702        }
703    }
704}
705
706/// Builder methods
707///
708/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
709/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
710impl Parsed {
711    /// Set the `year` component and return `self`.
712    #[inline]
713    pub const fn with_year(mut self, value: i32) -> Option<Self> {
714        self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
715        Some(self)
716    }
717
718    /// Set the `year_century` component and return `self`.
719    ///
720    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
721    /// the sign is inferred from the value.
722    #[inline]
723    pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
724        self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
725        if value != 0 {
726            self.year_century_is_negative = value.is_negative();
727        } else {
728            self.year_century_is_negative = is_negative;
729        }
730        Some(self)
731    }
732
733    /// Set the `year_last_two` component and return `self`.
734    #[inline]
735    pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
736        self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
737        Some(self)
738    }
739
740    /// Set the `iso_year` component and return `self`.
741    #[inline]
742    pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
743        self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
744        Some(self)
745    }
746
747    /// Set the `iso_year_century` component and return `self`.
748    ///
749    /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise
750    /// the sign is inferred from the value.
751    #[inline]
752    pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> {
753        self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value)));
754        if value != 0 {
755            self.iso_year_century_is_negative = value.is_negative();
756        } else {
757            self.iso_year_century_is_negative = is_negative;
758        }
759        Some(self)
760    }
761
762    /// Set the `iso_year_last_two` component and return `self`.
763    #[inline]
764    pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
765        self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
766        Some(self)
767    }
768
769    /// Set the `month` component and return `self`.
770    #[inline]
771    pub const fn with_month(mut self, value: Month) -> Option<Self> {
772        self.month = Some(value);
773        Some(self)
774    }
775
776    /// Set the `sunday_week_number` component and return `self`.
777    #[inline]
778    pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
779        self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
780        Some(self)
781    }
782
783    /// Set the `monday_week_number` component and return `self`.
784    #[inline]
785    pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
786        self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
787        Some(self)
788    }
789
790    /// Set the `iso_week_number` component and return `self`.
791    #[inline]
792    pub const fn with_iso_week_number(mut self, value: NonZero<u8>) -> Option<Self> {
793        self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
794        Some(self)
795    }
796
797    /// Set the `weekday` component and return `self`.
798    #[inline]
799    pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
800        self.weekday = Some(value);
801        Some(self)
802    }
803
804    /// Set the `ordinal` component and return `self`.
805    #[inline]
806    pub const fn with_ordinal(mut self, value: NonZero<u16>) -> Option<Self> {
807        self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
808        Some(self)
809    }
810
811    /// Set the `day` component and return `self`.
812    #[inline]
813    pub const fn with_day(mut self, value: NonZero<u8>) -> Option<Self> {
814        self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
815        Some(self)
816    }
817
818    /// Set the `hour_24` component and return `self`.
819    #[inline]
820    pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
821        self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
822        Some(self)
823    }
824
825    /// Set the `hour_12` component and return `self`.
826    #[inline]
827    pub const fn with_hour_12(mut self, value: NonZero<u8>) -> Option<Self> {
828        self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
829        Some(self)
830    }
831
832    /// Set the `hour_12_is_pm` component and return `self`.
833    #[inline]
834    pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
835        self.hour_12_is_pm = Some(value);
836        Some(self)
837    }
838
839    /// Set the `minute` component and return `self`.
840    #[inline]
841    pub const fn with_minute(mut self, value: u8) -> Option<Self> {
842        self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
843        Some(self)
844    }
845
846    /// Set the `second` component and return `self`.
847    #[inline]
848    pub const fn with_second(mut self, value: u8) -> Option<Self> {
849        self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
850        Some(self)
851    }
852
853    /// Set the `subsecond` component and return `self`.
854    #[inline]
855    pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
856        self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
857        Some(self)
858    }
859
860    /// Set the `offset_hour` component and return `self`.
861    #[inline]
862    pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
863        self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
864        Some(self)
865    }
866
867    /// Set the `offset_minute` component and return `self`.
868    #[doc(hidden)]
869    #[deprecated(
870        since = "0.3.8",
871        note = "use `parsed.with_offset_minute_signed()` instead"
872    )]
873    #[inline]
874    pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
875        if value > i8::MAX as u8 {
876            None
877        } else {
878            self.with_offset_minute_signed(value as i8)
879        }
880    }
881
882    /// Set the `offset_minute` component and return `self`.
883    #[inline]
884    pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
885        self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
886        Some(self)
887    }
888
889    /// Set the `offset_minute` component and return `self`.
890    #[doc(hidden)]
891    #[deprecated(
892        since = "0.3.8",
893        note = "use `parsed.with_offset_second_signed()` instead"
894    )]
895    #[inline]
896    pub const fn with_offset_second(self, value: u8) -> Option<Self> {
897        if value > i8::MAX as u8 {
898            None
899        } else {
900            self.with_offset_second_signed(value as i8)
901        }
902    }
903
904    /// Set the `offset_second` component and return `self`.
905    #[inline]
906    pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
907        self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
908        Some(self)
909    }
910
911    /// Set the `unix_timestamp_nanos` component and return `self`.
912    #[inline]
913    pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
914        self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
915        Some(self)
916    }
917}
918
919impl TryFrom<Parsed> for Date {
920    type Error = error::TryFromParsed;
921
922    #[inline]
923    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
924        /// Match on the components that need to be present.
925        macro_rules! match_ {
926            (_ => $catch_all:expr $(,)?) => {
927                $catch_all
928            };
929            (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
930                if let ($(Some($name)),*) = ($(parsed.$name()),*) {
931                    $arm
932                } else {
933                    match_!($($rest)*)
934                }
935            };
936        }
937
938        /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
939        /// numbering.
940        #[inline]
941        const fn adjustment(year: i32) -> i16 {
942            // Safety: `ordinal` is not zero.
943            match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
944                Weekday::Monday => 7,
945                Weekday::Tuesday => 1,
946                Weekday::Wednesday => 2,
947                Weekday::Thursday => 3,
948                Weekday::Friday => 4,
949                Weekday::Saturday => 5,
950                Weekday::Sunday => 6,
951            }
952        }
953
954        // If we do not have the year but we have *both* the century and the last two digits, we can
955        // construct the year. Likewise for the ISO year.
956        if let (None, Some(century), Some(is_negative), Some(last_two)) = (
957            parsed.year(),
958            parsed.year_century(),
959            parsed.year_century_is_negative(),
960            parsed.year_last_two(),
961        ) {
962            let year = if is_negative {
963                100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
964            } else {
965                100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
966            };
967            parsed.year = OptionRangedI32::from(RangedI32::new(year));
968        }
969        if let (None, Some(century), Some(is_negative), Some(last_two)) = (
970            parsed.iso_year(),
971            parsed.iso_year_century(),
972            parsed.iso_year_century_is_negative(),
973            parsed.iso_year_last_two(),
974        ) {
975            let iso_year = if is_negative {
976                100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>()
977            } else {
978                100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>()
979            };
980            parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year));
981        }
982
983        match_! {
984            (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
985            (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
986            (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
987                iso_year,
988                iso_week_number.get(),
989                weekday,
990            )?),
991            (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
992                year,
993                (sunday_week_number.cast_signed().extend::<i16>() * 7
994                    + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
995                    - adjustment(year)
996                    + 1).cast_unsigned(),
997            )?),
998            (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
999                year,
1000                (monday_week_number.cast_signed().extend::<i16>() * 7
1001                    + weekday.number_days_from_monday().cast_signed().extend::<i16>()
1002                    - adjustment(year)
1003                    + 1).cast_unsigned(),
1004            )?),
1005            _ => Err(InsufficientInformation),
1006        }
1007    }
1008}
1009
1010impl TryFrom<Parsed> for Time {
1011    type Error = error::TryFromParsed;
1012
1013    #[inline]
1014    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1015        let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
1016            (Some(hour), _, _) => hour,
1017            (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
1018            (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
1019            (_, Some(hour), Some(false)) => hour.get(),
1020            (_, Some(hour), Some(true)) => hour.get() + 12,
1021            _ => return Err(InsufficientInformation),
1022        };
1023
1024        if parsed.hour_24().is_none()
1025            && parsed.hour_12().is_some()
1026            && parsed.hour_12_is_pm().is_some()
1027            && parsed.minute().is_none()
1028            && parsed.second().is_none()
1029            && parsed.subsecond().is_none()
1030        {
1031            return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
1032        }
1033
1034        // Reject combinations such as hour-second with minute omitted.
1035        match (parsed.minute(), parsed.second(), parsed.subsecond()) {
1036            (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
1037            (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
1038            (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
1039            (Some(minute), Some(second), Some(subsecond)) => {
1040                Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
1041            }
1042            _ => Err(InsufficientInformation),
1043        }
1044    }
1045}
1046
1047#[inline]
1048fn utc_offset_try_from_parsed<const REQUIRED: bool>(
1049    parsed: Parsed,
1050) -> Result<UtcOffset, error::TryFromParsed> {
1051    let hour = match (REQUIRED, parsed.offset_hour()) {
1052        // An offset is required, but the hour is missing. Return an error.
1053        (true, None) => return Err(InsufficientInformation),
1054        // An offset is not required (e.g. for `UtcDateTime`). As the hour is missing, minutes and
1055        // seconds are not parsed. This is UTC.
1056        (false, None) => return Ok(UtcOffset::UTC),
1057        // Any other situation has an hour present.
1058        (_, Some(hour)) => hour,
1059    };
1060    let minute = parsed.offset_minute_signed();
1061    // Force `second` to be `None` if `minute` is `None`.
1062    let second = minute.and_then(|_| parsed.offset_second_signed());
1063
1064    let minute = minute.unwrap_or(0);
1065    let second = second.unwrap_or(0);
1066
1067    UtcOffset::from_hms(hour, minute, second).map_err(|mut err| {
1068        // Provide the user a more accurate error.
1069        if err.name == "hours" {
1070            err.name = "offset hour";
1071        } else if err.name == "minutes" {
1072            err.name = "offset minute";
1073        } else if err.name == "seconds" {
1074            err.name = "offset second";
1075        }
1076        err.into()
1077    })
1078}
1079
1080impl TryFrom<Parsed> for UtcOffset {
1081    type Error = error::TryFromParsed;
1082
1083    #[inline]
1084    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1085        utc_offset_try_from_parsed::<true>(parsed)
1086    }
1087}
1088
1089impl TryFrom<Parsed> for PrimitiveDateTime {
1090    type Error = error::TryFromParsed;
1091
1092    #[inline]
1093    fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
1094        Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
1095    }
1096}
1097
1098impl TryFrom<Parsed> for UtcDateTime {
1099    type Error = error::TryFromParsed;
1100
1101    #[inline]
1102    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1103        if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1104            let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1105            if let Some(subsecond) = parsed.subsecond() {
1106                value = value.replace_nanosecond(subsecond)?;
1107            }
1108            return Ok(value);
1109        }
1110
1111        // Some well-known formats explicitly allow leap seconds. We don't currently support them,
1112        // so treat it as the nearest preceding moment that can be represented. Because leap seconds
1113        // always fall at the end of a month UTC, reject any that are at other times.
1114        let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1115            if parsed.set_second(59).is_none() {
1116                bug!("59 is a valid second");
1117            }
1118            if parsed.set_subsecond(999_999_999).is_none() {
1119                bug!("999_999_999 is a valid subsecond");
1120            }
1121            true
1122        } else {
1123            false
1124        };
1125
1126        let dt = OffsetDateTime::new_in_offset(
1127            Date::try_from(parsed)?,
1128            Time::try_from(parsed)?,
1129            utc_offset_try_from_parsed::<false>(parsed)?,
1130        )
1131        .to_utc();
1132
1133        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1134            return Err(error::TryFromParsed::ComponentRange(
1135                error::ComponentRange {
1136                    name: "second",
1137                    minimum: 0,
1138                    maximum: 59,
1139                    value: 60,
1140                    conditional_message: Some("because leap seconds are not supported"),
1141                },
1142            ));
1143        }
1144        Ok(dt)
1145    }
1146}
1147
1148impl TryFrom<Parsed> for OffsetDateTime {
1149    type Error = error::TryFromParsed;
1150
1151    #[inline]
1152    fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
1153        if let Some(timestamp) = parsed.unix_timestamp_nanos() {
1154            let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
1155            if let Some(subsecond) = parsed.subsecond() {
1156                value = value.replace_nanosecond(subsecond)?;
1157            }
1158            return Ok(value);
1159        }
1160
1161        // Some well-known formats explicitly allow leap seconds. We don't currently support them,
1162        // so treat it as the nearest preceding moment that can be represented. Because leap seconds
1163        // always fall at the end of a month UTC, reject any that are at other times.
1164        let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
1165            if parsed.set_second(59).is_none() {
1166                bug!("59 is a valid second");
1167            }
1168            if parsed.set_subsecond(999_999_999).is_none() {
1169                bug!("999_999_999 is a valid subsecond");
1170            }
1171            true
1172        } else {
1173            false
1174        };
1175
1176        let dt = Self::new_in_offset(
1177            Date::try_from(parsed)?,
1178            Time::try_from(parsed)?,
1179            UtcOffset::try_from(parsed)?,
1180        );
1181
1182        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
1183            return Err(error::TryFromParsed::ComponentRange(
1184                error::ComponentRange {
1185                    name: "second",
1186                    minimum: 0,
1187                    maximum: 59,
1188                    value: 60,
1189                    conditional_message: Some("because leap seconds are not supported"),
1190                },
1191            ));
1192        }
1193        Ok(dt)
1194    }
1195}