summaryrefslogtreecommitdiffstats
path: root/third_party/rust/time/src/parsing/parsed.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/time/src/parsing/parsed.rs')
-rw-r--r--third_party/rust/time/src/parsing/parsed.rs759
1 files changed, 759 insertions, 0 deletions
diff --git a/third_party/rust/time/src/parsing/parsed.rs b/third_party/rust/time/src/parsing/parsed.rs
new file mode 100644
index 0000000000..7b2279cbb1
--- /dev/null
+++ b/third_party/rust/time/src/parsing/parsed.rs
@@ -0,0 +1,759 @@
+//! Information parsed from an input and format description.
+
+use core::mem::MaybeUninit;
+use core::num::{NonZeroU16, NonZeroU8};
+
+use crate::error::TryFromParsed::InsufficientInformation;
+use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
+#[cfg(feature = "alloc")]
+use crate::format_description::OwnedFormatItem;
+use crate::format_description::{Component, FormatItem};
+use crate::parsing::component::{
+ parse_day, parse_hour, parse_minute, parse_month, parse_offset_hour, parse_offset_minute,
+ parse_offset_second, parse_ordinal, parse_period, parse_second, parse_subsecond,
+ parse_week_number, parse_weekday, parse_year, Period,
+};
+use crate::parsing::ParsedItem;
+use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
+
+/// Sealed to prevent downstream implementations.
+mod sealed {
+ use super::*;
+
+ /// A trait to allow `parse_item` to be generic.
+ pub trait AnyFormatItem {
+ /// Parse a single item, returning the remaining input on success.
+ fn parse_item<'a>(
+ &self,
+ parsed: &mut Parsed,
+ input: &'a [u8],
+ ) -> Result<&'a [u8], error::ParseFromDescription>;
+ }
+}
+
+impl sealed::AnyFormatItem for FormatItem<'_> {
+ fn parse_item<'a>(
+ &self,
+ parsed: &mut Parsed,
+ input: &'a [u8],
+ ) -> Result<&'a [u8], error::ParseFromDescription> {
+ match self {
+ Self::Literal(literal) => Parsed::parse_literal(input, literal),
+ Self::Component(component) => parsed.parse_component(input, *component),
+ Self::Compound(compound) => parsed.parse_items(input, compound),
+ Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
+ Self::First(items) => {
+ let mut first_err = None;
+
+ for item in items.iter() {
+ match parsed.parse_item(input, item) {
+ Ok(remaining_input) => return Ok(remaining_input),
+ Err(err) if first_err.is_none() => first_err = Some(err),
+ Err(_) => {}
+ }
+ }
+
+ match first_err {
+ Some(err) => Err(err),
+ // This location will be reached if the slice is empty, skipping the `for` loop.
+ // As this case is expected to be uncommon, there's no need to check up front.
+ None => Ok(input),
+ }
+ }
+ }
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl sealed::AnyFormatItem for OwnedFormatItem {
+ fn parse_item<'a>(
+ &self,
+ parsed: &mut Parsed,
+ input: &'a [u8],
+ ) -> Result<&'a [u8], error::ParseFromDescription> {
+ match self {
+ Self::Literal(literal) => Parsed::parse_literal(input, literal),
+ Self::Component(component) => parsed.parse_component(input, *component),
+ Self::Compound(compound) => parsed.parse_items(input, compound),
+ Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
+ Self::First(items) => {
+ let mut first_err = None;
+
+ for item in items.iter() {
+ match parsed.parse_item(input, item) {
+ Ok(remaining_input) => return Ok(remaining_input),
+ Err(err) if first_err.is_none() => first_err = Some(err),
+ Err(_) => {}
+ }
+ }
+
+ match first_err {
+ Some(err) => Err(err),
+ // This location will be reached if the slice is empty, skipping the `for` loop.
+ // As this case is expected to be uncommon, there's no need to check up front.
+ None => Ok(input),
+ }
+ }
+ }
+ }
+}
+
+/// All information parsed.
+///
+/// This information is directly used to construct the final values.
+///
+/// Most users will not need think about this struct in any way. It is public to allow for manual
+/// control over values, in the instance that the default parser is insufficient.
+#[derive(Debug, Clone, Copy)]
+pub struct Parsed {
+ /// Bitflags indicating whether a particular field is present.
+ flags: u16,
+ /// Calendar year.
+ year: MaybeUninit<i32>,
+ /// The last two digits of the calendar year.
+ year_last_two: MaybeUninit<u8>,
+ /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
+ iso_year: MaybeUninit<i32>,
+ /// The last two digits of the ISO week year.
+ iso_year_last_two: MaybeUninit<u8>,
+ /// Month of the year.
+ month: Option<Month>,
+ /// Week of the year, where week one begins on the first Sunday of the calendar year.
+ sunday_week_number: MaybeUninit<u8>,
+ /// Week of the year, where week one begins on the first Monday of the calendar year.
+ monday_week_number: MaybeUninit<u8>,
+ /// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
+ iso_week_number: Option<NonZeroU8>,
+ /// Day of the week.
+ weekday: Option<Weekday>,
+ /// Day of the year.
+ ordinal: Option<NonZeroU16>,
+ /// Day of the month.
+ day: Option<NonZeroU8>,
+ /// Hour within the day.
+ hour_24: MaybeUninit<u8>,
+ /// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in
+ /// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field.
+ hour_12: Option<NonZeroU8>,
+ /// Whether the `hour_12` field indicates a time that "PM".
+ hour_12_is_pm: Option<bool>,
+ /// Minute within the hour.
+ minute: MaybeUninit<u8>,
+ /// Second within the minute.
+ second: MaybeUninit<u8>,
+ /// Nanosecond within the second.
+ subsecond: MaybeUninit<u32>,
+ /// Whole hours of the UTC offset.
+ offset_hour: MaybeUninit<i8>,
+ /// Minutes within the hour of the UTC offset.
+ offset_minute: MaybeUninit<i8>,
+ /// Seconds within the minute of the UTC offset.
+ offset_second: MaybeUninit<i8>,
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+impl Parsed {
+ const YEAR_FLAG: u16 = 1 << 0;
+ const YEAR_LAST_TWO_FLAG: u16 = 1 << 1;
+ const ISO_YEAR_FLAG: u16 = 1 << 2;
+ const ISO_YEAR_LAST_TWO_FLAG: u16 = 1 << 3;
+ const SUNDAY_WEEK_NUMBER_FLAG: u16 = 1 << 4;
+ const MONDAY_WEEK_NUMBER_FLAG: u16 = 1 << 5;
+ const HOUR_24_FLAG: u16 = 1 << 6;
+ const MINUTE_FLAG: u16 = 1 << 7;
+ const SECOND_FLAG: u16 = 1 << 8;
+ const SUBSECOND_FLAG: u16 = 1 << 9;
+ const OFFSET_HOUR_FLAG: u16 = 1 << 10;
+ const OFFSET_MINUTE_FLAG: u16 = 1 << 11;
+ const OFFSET_SECOND_FLAG: u16 = 1 << 12;
+ /// Indicates whether a leap second is permitted to be parsed. This is required by some
+ /// well-known formats.
+ const LEAP_SECOND_ALLOWED_FLAG: u16 = 1 << 13;
+}
+
+impl Parsed {
+ /// Create a new instance of `Parsed` with no information known.
+ pub const fn new() -> Self {
+ Self {
+ flags: 0,
+ year: MaybeUninit::uninit(),
+ year_last_two: MaybeUninit::uninit(),
+ iso_year: MaybeUninit::uninit(),
+ iso_year_last_two: MaybeUninit::uninit(),
+ month: None,
+ sunday_week_number: MaybeUninit::uninit(),
+ monday_week_number: MaybeUninit::uninit(),
+ iso_week_number: None,
+ weekday: None,
+ ordinal: None,
+ day: None,
+ hour_24: MaybeUninit::uninit(),
+ hour_12: None,
+ hour_12_is_pm: None,
+ minute: MaybeUninit::uninit(),
+ second: MaybeUninit::uninit(),
+ subsecond: MaybeUninit::uninit(),
+ offset_hour: MaybeUninit::uninit(),
+ offset_minute: MaybeUninit::uninit(),
+ offset_second: MaybeUninit::uninit(),
+ }
+ }
+
+ /// Parse a single [`FormatItem`] or [`OwnedFormatItem`], mutating the struct. The remaining
+ /// input is returned as the `Ok` value.
+ ///
+ /// If a [`FormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing will not
+ /// fail; the input will be returned as-is if the expected format is not present.
+ pub fn parse_item<'a>(
+ &mut self,
+ input: &'a [u8],
+ item: &impl sealed::AnyFormatItem,
+ ) -> Result<&'a [u8], error::ParseFromDescription> {
+ item.parse_item(self, input)
+ }
+
+ /// Parse a sequence of [`FormatItem`]s or [`OwnedFormatItem`]s, mutating the struct. The
+ /// remaining input is returned as the `Ok` value.
+ ///
+ /// This method will fail if any of the contained [`FormatItem`]s or [`OwnedFormatItem`]s fail
+ /// to parse. `self` will not be mutated in this instance.
+ pub fn parse_items<'a>(
+ &mut self,
+ mut input: &'a [u8],
+ items: &[impl sealed::AnyFormatItem],
+ ) -> Result<&'a [u8], error::ParseFromDescription> {
+ // Make a copy that we can mutate. It will only be set to the user's copy if everything
+ // succeeds.
+ let mut this = *self;
+ for item in items {
+ input = this.parse_item(input, item)?;
+ }
+ *self = this;
+ Ok(input)
+ }
+
+ /// Parse a literal byte sequence. The remaining input is returned as the `Ok` value.
+ pub fn parse_literal<'a>(
+ input: &'a [u8],
+ literal: &[u8],
+ ) -> Result<&'a [u8], error::ParseFromDescription> {
+ input
+ .strip_prefix(literal)
+ .ok_or(error::ParseFromDescription::InvalidLiteral)
+ }
+
+ /// Parse a single component, mutating the struct. The remaining input is returned as the `Ok`
+ /// value.
+ pub fn parse_component<'a>(
+ &mut self,
+ input: &'a [u8],
+ component: Component,
+ ) -> Result<&'a [u8], error::ParseFromDescription> {
+ use error::ParseFromDescription::InvalidComponent;
+
+ match component {
+ Component::Day(modifiers) => parse_day(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
+ .ok_or(InvalidComponent("day")),
+ Component::Month(modifiers) => parse_month(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
+ .ok_or(InvalidComponent("month")),
+ Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
+ .ok_or(InvalidComponent("ordinal")),
+ Component::Weekday(modifiers) => parse_weekday(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
+ .ok_or(InvalidComponent("weekday")),
+ Component::WeekNumber(modifiers) => {
+ let ParsedItem(remaining, value) =
+ parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
+ match modifiers.repr {
+ WeekNumberRepr::Iso => {
+ NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
+ }
+ WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
+ WeekNumberRepr::Monday => self.set_monday_week_number(value),
+ }
+ .ok_or(InvalidComponent("week number"))?;
+ Ok(remaining)
+ }
+ Component::Year(modifiers) => {
+ let ParsedItem(remaining, value) =
+ parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
+ match (modifiers.iso_week_based, modifiers.repr) {
+ (false, YearRepr::Full) => self.set_year(value),
+ (false, YearRepr::LastTwo) => self.set_year_last_two(value as _),
+ (true, YearRepr::Full) => self.set_iso_year(value),
+ (true, YearRepr::LastTwo) => self.set_iso_year_last_two(value as _),
+ }
+ .ok_or(InvalidComponent("year"))?;
+ Ok(remaining)
+ }
+ Component::Hour(modifiers) => {
+ let ParsedItem(remaining, value) =
+ parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
+ if modifiers.is_12_hour_clock {
+ NonZeroU8::new(value).and_then(|value| self.set_hour_12(value))
+ } else {
+ self.set_hour_24(value)
+ }
+ .ok_or(InvalidComponent("hour"))?;
+ Ok(remaining)
+ }
+ Component::Minute(modifiers) => parse_minute(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
+ .ok_or(InvalidComponent("minute")),
+ Component::Period(modifiers) => parse_period(input, modifiers)
+ .and_then(|parsed| {
+ parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
+ })
+ .ok_or(InvalidComponent("period")),
+ Component::Second(modifiers) => parse_second(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
+ .ok_or(InvalidComponent("second")),
+ Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
+ .ok_or(InvalidComponent("subsecond")),
+ Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
+ .and_then(|parsed| parsed.consume_value(|value| self.set_offset_hour(value)))
+ .ok_or(InvalidComponent("offset hour")),
+ Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
+ .and_then(|parsed| {
+ parsed.consume_value(|value| self.set_offset_minute_signed(value))
+ })
+ .ok_or(InvalidComponent("offset minute")),
+ Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
+ .and_then(|parsed| {
+ parsed.consume_value(|value| self.set_offset_second_signed(value))
+ })
+ .ok_or(InvalidComponent("offset second")),
+ }
+ }
+}
+
+/// Generate getters for each of the fields.
+macro_rules! getters {
+ ($($(@$flag:ident)? $name:ident: $ty:ty),+ $(,)?) => {$(
+ getters!(! $(@$flag)? $name: $ty);
+ )*};
+ (! $name:ident : $ty:ty) => {
+ /// Obtain the named component.
+ pub const fn $name(&self) -> Option<$ty> {
+ self.$name
+ }
+ };
+ (! @$flag:ident $name:ident : $ty:ty) => {
+ /// Obtain the named component.
+ pub const fn $name(&self) -> Option<$ty> {
+ if self.flags & Self::$flag != Self::$flag {
+ None
+ } else {
+ // SAFETY: We just checked if the field is present.
+ Some(unsafe { self.$name.assume_init() })
+ }
+ }
+ };
+}
+
+/// Getter methods
+impl Parsed {
+ getters! {
+ @YEAR_FLAG year: i32,
+ @YEAR_LAST_TWO_FLAG year_last_two: u8,
+ @ISO_YEAR_FLAG iso_year: i32,
+ @ISO_YEAR_LAST_TWO_FLAG iso_year_last_two: u8,
+ month: Month,
+ @SUNDAY_WEEK_NUMBER_FLAG sunday_week_number: u8,
+ @MONDAY_WEEK_NUMBER_FLAG monday_week_number: u8,
+ iso_week_number: NonZeroU8,
+ weekday: Weekday,
+ ordinal: NonZeroU16,
+ day: NonZeroU8,
+ @HOUR_24_FLAG hour_24: u8,
+ hour_12: NonZeroU8,
+ hour_12_is_pm: bool,
+ @MINUTE_FLAG minute: u8,
+ @SECOND_FLAG second: u8,
+ @SUBSECOND_FLAG subsecond: u32,
+ @OFFSET_HOUR_FLAG offset_hour: i8,
+ }
+
+ /// Obtain the absolute value of the offset minute.
+ #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
+ pub const fn offset_minute(&self) -> Option<u8> {
+ Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
+ }
+
+ /// Obtain the offset minute as an `i8`.
+ pub const fn offset_minute_signed(&self) -> Option<i8> {
+ if self.flags & Self::OFFSET_MINUTE_FLAG != Self::OFFSET_MINUTE_FLAG {
+ None
+ } else {
+ // SAFETY: We just checked if the field is present.
+ Some(unsafe { self.offset_minute.assume_init() })
+ }
+ }
+
+ /// Obtain the absolute value of the offset second.
+ #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
+ pub const fn offset_second(&self) -> Option<u8> {
+ Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
+ }
+
+ /// Obtain the offset second as an `i8`.
+ pub const fn offset_second_signed(&self) -> Option<i8> {
+ if self.flags & Self::OFFSET_SECOND_FLAG != Self::OFFSET_SECOND_FLAG {
+ None
+ } else {
+ // SAFETY: We just checked if the field is present.
+ Some(unsafe { self.offset_second.assume_init() })
+ }
+ }
+
+ /// Obtain whether leap seconds are permitted in the current format.
+ pub(crate) const fn leap_second_allowed(&self) -> bool {
+ self.flags & Self::LEAP_SECOND_ALLOWED_FLAG == Self::LEAP_SECOND_ALLOWED_FLAG
+ }
+}
+
+/// Generate setters for each of the fields.
+///
+/// This macro should only be used for fields where the value is not validated beyond its type.
+macro_rules! setters {
+ ($($(@$flag:ident)? $setter_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
+ setters!(! $(@$flag)? $setter_name $name: $ty);
+ )*};
+ (! $setter_name:ident $name:ident : $ty:ty) => {
+ /// Set the named component.
+ pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
+ self.$name = Some(value);
+ Some(())
+ }
+ };
+ (! @$flag:ident $setter_name:ident $name:ident : $ty:ty) => {
+ /// Set the named component.
+ pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
+ self.$name = MaybeUninit::new(value);
+ self.flags |= Self::$flag;
+ Some(())
+ }
+ };
+}
+
+/// Setter methods
+///
+/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The
+/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
+impl Parsed {
+ setters! {
+ @YEAR_FLAG set_year year: i32,
+ @YEAR_LAST_TWO_FLAG set_year_last_two year_last_two: u8,
+ @ISO_YEAR_FLAG set_iso_year iso_year: i32,
+ @ISO_YEAR_LAST_TWO_FLAG set_iso_year_last_two iso_year_last_two: u8,
+ set_month month: Month,
+ @SUNDAY_WEEK_NUMBER_FLAG set_sunday_week_number sunday_week_number: u8,
+ @MONDAY_WEEK_NUMBER_FLAG set_monday_week_number monday_week_number: u8,
+ set_iso_week_number iso_week_number: NonZeroU8,
+ set_weekday weekday: Weekday,
+ set_ordinal ordinal: NonZeroU16,
+ set_day day: NonZeroU8,
+ @HOUR_24_FLAG set_hour_24 hour_24: u8,
+ set_hour_12 hour_12: NonZeroU8,
+ set_hour_12_is_pm hour_12_is_pm: bool,
+ @MINUTE_FLAG set_minute minute: u8,
+ @SECOND_FLAG set_second second: u8,
+ @SUBSECOND_FLAG set_subsecond subsecond: u32,
+ @OFFSET_HOUR_FLAG set_offset_hour offset_hour: i8,
+ }
+
+ /// Set the named component.
+ #[deprecated(
+ since = "0.3.8",
+ note = "use `parsed.set_offset_minute_signed()` instead"
+ )]
+ pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
+ if value > i8::MAX as u8 {
+ None
+ } else {
+ self.set_offset_minute_signed(value as _)
+ }
+ }
+
+ /// Set the `offset_minute` component.
+ pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
+ self.offset_minute = MaybeUninit::new(value);
+ self.flags |= Self::OFFSET_MINUTE_FLAG;
+ Some(())
+ }
+
+ /// Set the named component.
+ #[deprecated(
+ since = "0.3.8",
+ note = "use `parsed.set_offset_second_signed()` instead"
+ )]
+ pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
+ if value > i8::MAX as u8 {
+ None
+ } else {
+ self.set_offset_second_signed(value as _)
+ }
+ }
+
+ /// Set the `offset_second` component.
+ pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
+ self.offset_second = MaybeUninit::new(value);
+ self.flags |= Self::OFFSET_SECOND_FLAG;
+ Some(())
+ }
+
+ /// Set the leap second allowed flag.
+ pub(crate) fn set_leap_second_allowed(&mut self, value: bool) {
+ if value {
+ self.flags |= Self::LEAP_SECOND_ALLOWED_FLAG;
+ } else {
+ self.flags &= !Self::LEAP_SECOND_ALLOWED_FLAG;
+ }
+ }
+}
+
+/// Generate build methods for each of the fields.
+///
+/// This macro should only be used for fields where the value is not validated beyond its type.
+macro_rules! builders {
+ ($($(@$flag:ident)? $builder_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
+ builders!(! $(@$flag)? $builder_name $name: $ty);
+ )*};
+ (! $builder_name:ident $name:ident : $ty:ty) => {
+ /// Set the named component and return `self`.
+ pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
+ self.$name = Some(value);
+ Some(self)
+ }
+ };
+ (! @$flag:ident $builder_name:ident $name:ident : $ty:ty) => {
+ /// Set the named component and return `self`.
+ pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
+ self.$name = MaybeUninit::new(value);
+ self.flags |= Self::$flag;
+ Some(self)
+ }
+ };
+}
+
+/// Builder methods
+///
+/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
+/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
+impl Parsed {
+ builders! {
+ @YEAR_FLAG with_year year: i32,
+ @YEAR_LAST_TWO_FLAG with_year_last_two year_last_two: u8,
+ @ISO_YEAR_FLAG with_iso_year iso_year: i32,
+ @ISO_YEAR_LAST_TWO_FLAG with_iso_year_last_two iso_year_last_two: u8,
+ with_month month: Month,
+ @SUNDAY_WEEK_NUMBER_FLAG with_sunday_week_number sunday_week_number: u8,
+ @MONDAY_WEEK_NUMBER_FLAG with_monday_week_number monday_week_number: u8,
+ with_iso_week_number iso_week_number: NonZeroU8,
+ with_weekday weekday: Weekday,
+ with_ordinal ordinal: NonZeroU16,
+ with_day day: NonZeroU8,
+ @HOUR_24_FLAG with_hour_24 hour_24: u8,
+ with_hour_12 hour_12: NonZeroU8,
+ with_hour_12_is_pm hour_12_is_pm: bool,
+ @MINUTE_FLAG with_minute minute: u8,
+ @SECOND_FLAG with_second second: u8,
+ @SUBSECOND_FLAG with_subsecond subsecond: u32,
+ @OFFSET_HOUR_FLAG with_offset_hour offset_hour: i8,
+ }
+
+ /// Set the named component and return `self`.
+ #[deprecated(
+ since = "0.3.8",
+ note = "use `parsed.with_offset_minute_signed()` instead"
+ )]
+ pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
+ if value > i8::MAX as u8 {
+ None
+ } else {
+ self.with_offset_minute_signed(value as _)
+ }
+ }
+
+ /// Set the `offset_minute` component and return `self`.
+ pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
+ self.offset_minute = MaybeUninit::new(value);
+ self.flags |= Self::OFFSET_MINUTE_FLAG;
+ Some(self)
+ }
+
+ /// Set the named component and return `self`.
+ #[deprecated(
+ since = "0.3.8",
+ note = "use `parsed.with_offset_second_signed()` instead"
+ )]
+ pub const fn with_offset_second(self, value: u8) -> Option<Self> {
+ if value > i8::MAX as u8 {
+ None
+ } else {
+ self.with_offset_second_signed(value as _)
+ }
+ }
+
+ /// Set the `offset_second` component and return `self`.
+ pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
+ self.offset_second = MaybeUninit::new(value);
+ self.flags |= Self::OFFSET_SECOND_FLAG;
+ Some(self)
+ }
+}
+
+impl TryFrom<Parsed> for Date {
+ type Error = error::TryFromParsed;
+
+ fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
+ /// Match on the components that need to be present.
+ macro_rules! match_ {
+ (_ => $catch_all:expr $(,)?) => {
+ $catch_all
+ };
+ (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
+ if let ($(Some($name)),*) = ($(parsed.$name()),*) {
+ $arm
+ } else {
+ match_!($($rest)*)
+ }
+ };
+ }
+
+ /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
+ /// numbering.
+ const fn adjustment(year: i32) -> i16 {
+ match Date::__from_ordinal_date_unchecked(year, 1).weekday() {
+ Weekday::Monday => 7,
+ Weekday::Tuesday => 1,
+ Weekday::Wednesday => 2,
+ Weekday::Thursday => 3,
+ Weekday::Friday => 4,
+ Weekday::Saturday => 5,
+ Weekday::Sunday => 6,
+ }
+ }
+
+ // TODO Only the basics have been covered. There are many other valid values that are not
+ // currently constructed from the information known.
+
+ match_! {
+ (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
+ (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
+ (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
+ iso_year,
+ iso_week_number.get(),
+ weekday,
+ )?),
+ (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
+ year,
+ (sunday_week_number as i16 * 7 + weekday.number_days_from_sunday() as i16
+ - adjustment(year)
+ + 1) as u16,
+ )?),
+ (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
+ year,
+ (monday_week_number as i16 * 7 + weekday.number_days_from_monday() as i16
+ - adjustment(year)
+ + 1) as u16,
+ )?),
+ _ => Err(InsufficientInformation),
+ }
+ }
+}
+
+impl TryFrom<Parsed> for Time {
+ type Error = error::TryFromParsed;
+
+ fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
+ let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
+ (Some(hour), _, _) => hour,
+ (_, Some(hour), Some(false)) if hour.get() == 12 => 0,
+ (_, Some(hour), Some(true)) if hour.get() == 12 => 12,
+ (_, Some(hour), Some(false)) => hour.get(),
+ (_, Some(hour), Some(true)) => hour.get() + 12,
+ _ => return Err(InsufficientInformation),
+ };
+ if parsed.hour_24().is_none()
+ && parsed.hour_12().is_some()
+ && parsed.hour_12_is_pm().is_some()
+ && parsed.minute().is_none()
+ && parsed.second().is_none()
+ && parsed.subsecond().is_none()
+ {
+ return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
+ }
+ let minute = parsed.minute().ok_or(InsufficientInformation)?;
+ let second = parsed.second().unwrap_or(0);
+ let subsecond = parsed.subsecond().unwrap_or(0);
+ Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
+ }
+}
+
+impl TryFrom<Parsed> for UtcOffset {
+ type Error = error::TryFromParsed;
+
+ fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
+ let hour = parsed.offset_hour().ok_or(InsufficientInformation)?;
+ let minute = parsed.offset_minute_signed().unwrap_or(0);
+ let second = parsed.offset_second_signed().unwrap_or(0);
+
+ Self::from_hms(hour, minute, second).map_err(|mut err| {
+ // Provide the user a more accurate error.
+ if err.name == "hours" {
+ err.name = "offset hour";
+ } else if err.name == "minutes" {
+ err.name = "offset minute";
+ } else if err.name == "seconds" {
+ err.name = "offset second";
+ }
+ err.into()
+ })
+ }
+}
+
+impl TryFrom<Parsed> for PrimitiveDateTime {
+ type Error = error::TryFromParsed;
+
+ fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
+ Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
+ }
+}
+
+impl TryFrom<Parsed> for OffsetDateTime {
+ type Error = error::TryFromParsed;
+
+ #[allow(clippy::unwrap_in_result)] // We know the values are valid.
+ fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
+ // Some well-known formats explicitly allow leap seconds. We don't currently support them,
+ // so treat it as the nearest preceding moment that can be represented. Because leap seconds
+ // always fall at the end of a month UTC, reject any that are at other times.
+ let leap_second_input = if parsed.leap_second_allowed() && parsed.second() == Some(60) {
+ parsed.set_second(59).expect("59 is a valid second");
+ parsed
+ .set_subsecond(999_999_999)
+ .expect("999_999_999 is a valid subsecond");
+ true
+ } else {
+ false
+ };
+ let dt = PrimitiveDateTime::try_from(parsed)?.assume_offset(parsed.try_into()?);
+ if leap_second_input && !dt.is_valid_leap_second_stand_in() {
+ return Err(error::TryFromParsed::ComponentRange(
+ error::ComponentRange {
+ name: "second",
+ minimum: 0,
+ maximum: 59,
+ value: 60,
+ conditional_range: true,
+ },
+ ));
+ }
+ Ok(dt)
+ }
+}