//! Information parsed from an input and format description. use core::mem::MaybeUninit; use core::num::{NonZeroU16, NonZeroU8}; use crate::date_time::{maybe_offset_from_offset, offset_kind, DateTime, MaybeOffset}; 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_ignore, parse_minute, parse_month, parse_offset_hour, parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second, parse_subsecond, parse_unix_timestamp, 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), } } } } } /// The type of the `flags` field in [`Parsed`]. Allows for changing a single location and having it /// effect all uses. type Flag = u32; /// 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: Flag, /// Calendar year. year: MaybeUninit, /// The last two digits of the calendar year. year_last_two: MaybeUninit, /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date). iso_year: MaybeUninit, /// The last two digits of the ISO week year. iso_year_last_two: MaybeUninit, /// Month of the year. month: Option, /// Week of the year, where week one begins on the first Sunday of the calendar year. sunday_week_number: MaybeUninit, /// Week of the year, where week one begins on the first Monday of the calendar year. monday_week_number: MaybeUninit, /// Week of the year, where week one is the Monday-to-Sunday period containing January 4. iso_week_number: Option, /// Day of the week. weekday: Option, /// Day of the year. ordinal: Option, /// Day of the month. day: Option, /// Hour within the day. hour_24: MaybeUninit, /// 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, /// Whether the `hour_12` field indicates a time that "PM". hour_12_is_pm: Option, /// Minute within the hour. minute: MaybeUninit, /// Second within the minute. second: MaybeUninit, /// Nanosecond within the second. subsecond: MaybeUninit, /// Whole hours of the UTC offset. offset_hour: MaybeUninit, /// Minutes within the hour of the UTC offset. offset_minute: MaybeUninit, /// Seconds within the minute of the UTC offset. offset_second: MaybeUninit, /// The Unix timestamp in nanoseconds. unix_timestamp_nanos: MaybeUninit, } #[allow(clippy::missing_docs_in_private_items)] impl Parsed { const YEAR_FLAG: Flag = 1 << 0; const YEAR_LAST_TWO_FLAG: Flag = 1 << 1; const ISO_YEAR_FLAG: Flag = 1 << 2; const ISO_YEAR_LAST_TWO_FLAG: Flag = 1 << 3; const SUNDAY_WEEK_NUMBER_FLAG: Flag = 1 << 4; const MONDAY_WEEK_NUMBER_FLAG: Flag = 1 << 5; const HOUR_24_FLAG: Flag = 1 << 6; const MINUTE_FLAG: Flag = 1 << 7; const SECOND_FLAG: Flag = 1 << 8; const SUBSECOND_FLAG: Flag = 1 << 9; const OFFSET_HOUR_FLAG: Flag = 1 << 10; const OFFSET_MINUTE_FLAG: Flag = 1 << 11; const OFFSET_SECOND_FLAG: Flag = 1 << 12; /// Indicates whether a leap second is permitted to be parsed. This is required by some /// well-known formats. pub(super) const LEAP_SECOND_ALLOWED_FLAG: Flag = 1 << 13; /// Indicates whether the `UtcOffset` is negative. This information is obtained when parsing the /// offset hour, but may not otherwise be stored due to "-0" being equivalent to "0". const OFFSET_IS_NEGATIVE_FLAG: Flag = 1 << 14; /// Does the value at `OFFSET_IS_NEGATIVE_FLAG` have any semantic meaning, or is it just the /// default value? If the latter, the value should be considered to have no meaning. const OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED: Flag = 1 << 15; const UNIX_TIMESTAMP_NANOS_FLAG: Flag = 1 << 16; } 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(), unix_timestamp_nanos: 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, is_negative)| { self.set_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED, true); self.set_flag(Self::OFFSET_IS_NEGATIVE_FLAG, is_negative); 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")), Component::Ignore(modifiers) => parse_ignore(input, modifiers) .map(ParsedItem::<()>::into_inner) .ok_or(InvalidComponent("ignore")), Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers) .and_then(|parsed| { parsed.consume_value(|value| self.set_unix_timestamp_nanos(value)) }) .ok_or(InvalidComponent("unix_timestamp")), } } /// Get the value of the provided flag. const fn get_flag(&self, flag: Flag) -> bool { self.flags & flag == flag } /// Set the value of the provided flag. pub(super) fn set_flag(&mut self, flag: Flag, value: bool) { if value { self.flags |= flag; } else { self.flags &= !flag; } } } /// 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.get_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, @UNIX_TIMESTAMP_NANOS_FLAG unix_timestamp_nanos: i128, } /// Obtain the absolute value of the offset minute. #[doc(hidden)] #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")] pub const fn offset_minute(&self) -> Option { 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 { if !self.get_flag(Self::OFFSET_MINUTE_FLAG) { None } else { // SAFETY: We just checked if the field is present. let value = unsafe { self.offset_minute.assume_init() }; if self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED) && (value.is_negative() != self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG)) { Some(-value) } else { Some(value) } } } /// Obtain the absolute value of the offset second. #[doc(hidden)] #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")] pub const fn offset_second(&self) -> Option { 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 { if !self.get_flag(Self::OFFSET_SECOND_FLAG) { None } else { // SAFETY: We just checked if the field is present. let value = unsafe { self.offset_second.assume_init() }; if self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED) && (value.is_negative() != self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG)) { Some(-value) } else { Some(value) } } } } /// 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.set_flag(Self::$flag, true); 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, @UNIX_TIMESTAMP_NANOS_FLAG set_unix_timestamp_nanos unix_timestamp_nanos: i128, } /// Set the named component. #[doc(hidden)] #[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.set_flag(Self::OFFSET_MINUTE_FLAG, true); Some(()) } /// Set the named component. #[doc(hidden)] #[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.set_flag(Self::OFFSET_SECOND_FLAG, true); Some(()) } } /// 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.$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.$name = MaybeUninit::new(value); self.flags |= Self::$flag; Some(self) } }; } /// Builder methods /// /// All builder methods return `Option`, 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, @UNIX_TIMESTAMP_NANOS_FLAG with_unix_timestamp_nanos unix_timestamp_nanos: i128, } /// Set the named component and return `self`. #[doc(hidden)] #[deprecated( since = "0.3.8", note = "use `parsed.with_offset_minute_signed()` instead" )] pub const fn with_offset_minute(self, value: u8) -> Option { 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.offset_minute = MaybeUninit::new(value); self.flags |= Self::OFFSET_MINUTE_FLAG; Some(self) } /// Set the named component and return `self`. #[doc(hidden)] #[deprecated( since = "0.3.8", note = "use `parsed.with_offset_second_signed()` instead" )] pub const fn with_offset_second(self, value: u8) -> Option { 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.offset_second = MaybeUninit::new(value); self.flags |= Self::OFFSET_SECOND_FLAG; Some(self) } } impl TryFrom for Date { type Error = error::TryFromParsed; fn try_from(parsed: Parsed) -> Result { /// 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 for Time { type Error = error::TryFromParsed; fn try_from(parsed: Parsed) -> Result { 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 for UtcOffset { type Error = error::TryFromParsed; fn try_from(parsed: Parsed) -> Result { 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 for PrimitiveDateTime { type Error = as TryFrom>::Error; fn try_from(parsed: Parsed) -> Result { parsed.try_into().map(Self) } } impl TryFrom for OffsetDateTime { type Error = as TryFrom>::Error; fn try_from(parsed: Parsed) -> Result { parsed.try_into().map(Self) } } impl TryFrom for DateTime { type Error = error::TryFromParsed; #[allow(clippy::unwrap_in_result)] // We know the values are valid. fn try_from(mut parsed: Parsed) -> Result { if O::HAS_LOGICAL_OFFSET { if let Some(timestamp) = parsed.unix_timestamp_nanos() { let DateTime { date, time, offset } = DateTime::::from_unix_timestamp_nanos(timestamp)?; return Ok(Self { date, time, offset: maybe_offset_from_offset::(offset), }); } } // 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.get_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG) && 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 = Self { date: Date::try_from(parsed)?, time: Time::try_from(parsed)?, offset: O::try_from_parsed(parsed)?, }; 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) } }