time/parsing/
parsed.rs

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