diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /vendor/time/src | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/time/src')
37 files changed, 2038 insertions, 1233 deletions
diff --git a/vendor/time/src/date.rs b/vendor/time/src/date.rs index 9f194b434..3f76adb2d 100644 --- a/vendor/time/src/date.rs +++ b/vendor/time/src/date.rs @@ -1,19 +1,28 @@ //! The [`Date`] struct and its associated `impl`s. use core::fmt; +use core::num::NonZeroI32; use core::ops::{Add, Sub}; use core::time::Duration as StdDuration; #[cfg(feature = "formatting")] use std::io; +use deranged::RangedI32; + use crate::convert::*; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{ + cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign, + impl_sub_assign, +}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year}; use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday}; +type Year = RangedI32<MIN_YEAR, MAX_YEAR>; + /// The minimum valid year. pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") { -999_999 @@ -39,32 +48,43 @@ pub struct Date { // | 2 bits | 21 bits | 9 bits | // | unassigned | year | ordinal | // The year is 15 bits when `large-dates` is not enabled. - value: i32, + value: NonZeroI32, } impl Date { /// The minimum valid `Date`. /// /// The value of this may vary depending on the feature flags enabled. - pub const MIN: Self = Self::__from_ordinal_date_unchecked(MIN_YEAR, 1); + // Safety: `ordinal` is not zero. + #[allow(clippy::undocumented_unsafe_blocks)] + pub const MIN: Self = unsafe { Self::__from_ordinal_date_unchecked(MIN_YEAR, 1) }; /// The maximum valid `Date`. /// /// The value of this may vary depending on the feature flags enabled. - pub const MAX: Self = Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)); + // Safety: `ordinal` is not zero. + #[allow(clippy::undocumented_unsafe_blocks)] + pub const MAX: Self = + unsafe { Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)) }; // region: constructors /// Construct a `Date` from the year and ordinal values, the validity of which must be /// guaranteed by the caller. + /// + /// # Safety + /// + /// `ordinal` must not be zero. `year` should be in the range `MIN_YEAR..=MAX_YEAR`, but this + /// is not a safety invariant. #[doc(hidden)] - pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { + pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { debug_assert!(year >= MIN_YEAR); debug_assert!(year <= MAX_YEAR); debug_assert!(ordinal != 0); debug_assert!(ordinal <= days_in_year(year)); Self { - value: (year << 9) | ordinal as i32, + // Safety: The caller must guarantee that `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) }, } } @@ -91,14 +111,29 @@ impl Date { [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], ]; - ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); - ensure_value_in_range!(day conditionally in 1 => days_in_year_month(year, month)); + ensure_ranged!(Year: year); + match day { + 1..=28 => {} + 29..=31 if day <= days_in_year_month(year, month) => {} + _ => { + return Err(crate::error::ComponentRange { + name: "day", + minimum: 1, + maximum: days_in_year_month(year, month) as _, + value: day as _, + conditional_range: true, + }); + } + } - Ok(Self::__from_ordinal_date_unchecked( - year, - DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] - + day as u16, - )) + // Safety: `ordinal` is not zero. + Ok(unsafe { + Self::__from_ordinal_date_unchecked( + year, + DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] + + day as u16, + ) + }) } /// Attempt to create a `Date` from the year and ordinal day number. @@ -114,9 +149,23 @@ impl Date { /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year. /// ``` pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); - ensure_value_in_range!(ordinal conditionally in 1 => days_in_year(year)); - Ok(Self::__from_ordinal_date_unchecked(year, ordinal)) + ensure_ranged!(Year: year); + match ordinal { + 1..=365 => {} + 366 if is_leap_year(year) => {} + _ => { + return Err(crate::error::ComponentRange { + name: "ordinal", + minimum: 1, + maximum: days_in_year(year) as _, + value: ordinal as _, + conditional_range: true, + }); + } + } + + // Safety: `ordinal` is not zero. + Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }) } /// Attempt to create a `Date` from the ISO year, week, and weekday. @@ -137,8 +186,20 @@ impl Date { week: u8, weekday: Weekday, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); - ensure_value_in_range!(week conditionally in 1 => weeks_in_year(year)); + ensure_ranged!(Year: year); + match week { + 1..=52 => {} + 53 if week <= weeks_in_year(year) => {} + _ => { + return Err(crate::error::ComponentRange { + name: "week", + minimum: 1, + maximum: weeks_in_year(year) as _, + value: week as _, + conditional_range: true, + }); + } + } let adj_year = year - 1; let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) @@ -155,14 +216,21 @@ impl Date { let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4; Ok(if ordinal <= 0 { - Self::__from_ordinal_date_unchecked( - year - 1, - (ordinal as u16).wrapping_add(days_in_year(year - 1)), - ) + // Safety: `ordinal` is not zero. + unsafe { + Self::__from_ordinal_date_unchecked( + year - 1, + (ordinal as u16).wrapping_add(days_in_year(year - 1)), + ) + } } else if ordinal > days_in_year(year) as i16 { - Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year)) + // Safety: `ordinal` is not zero. + unsafe { + Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year)) + } } else { - Self::__from_ordinal_date_unchecked(year, ordinal as _) + // Safety: `ordinal` is not zero. + unsafe { Self::__from_ordinal_date_unchecked(year, ordinal as _) } }) } @@ -181,9 +249,8 @@ impl Date { /// ``` #[doc(alias = "from_julian_date")] pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!( - julian_day in Self::MIN.to_julian_day() => Self::MAX.to_julian_day() - ); + type JulianDay = RangedI32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>; + ensure_ranged!(JulianDay: julian_day); Ok(Self::from_julian_day_unchecked(julian_day)) } @@ -223,7 +290,8 @@ impl Date { cascade!(ordinal in 1..366 => year); } - Self::__from_ordinal_date_unchecked(year, ordinal) + // Safety: `ordinal` is not zero. + unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) } } // endregion constructors @@ -237,7 +305,7 @@ impl Date { /// assert_eq!(date!(2020 - 01 - 01).year(), 2020); /// ``` pub const fn year(self) -> i32 { - self.value >> 9 + self.value.get() >> 9 } /// Get the month. @@ -316,7 +384,7 @@ impl Date { /// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365); /// ``` pub const fn ordinal(self) -> u16 { - (self.value & 0x1FF) as _ + (self.value.get() & 0x1FF) as _ } /// Get the ISO 8601 year and week number. @@ -483,14 +551,16 @@ impl Date { /// ``` pub const fn next_day(self) -> Option<Self> { if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) { - if self.value == Self::MAX.value { + if self.value.get() == Self::MAX.value.get() { None } else { - Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) + // Safety: `ordinal` is not zero. + unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) } } } else { Some(Self { - value: self.value + 1, + // Safety: `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked(self.value.get() + 1) }, }) } } @@ -517,18 +587,119 @@ impl Date { pub const fn previous_day(self) -> Option<Self> { if self.ordinal() != 1 { Some(Self { - value: self.value - 1, + // Safety: `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) }, }) - } else if self.value == Self::MIN.value { + } else if self.value.get() == Self::MIN.value.get() { None } else { - Some(Self::__from_ordinal_date_unchecked( - self.year() - 1, - days_in_year(self.year() - 1), - )) + // Safety: `ordinal` is not zero. + Some(unsafe { + Self::__from_ordinal_date_unchecked(self.year() - 1, days_in_year(self.year() - 1)) + }) } } + /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`. + /// + /// # Panics + /// Panics if an overflow occurred. + /// + /// # Examples + /// ``` + /// # use time::Weekday; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2023 - 06 - 28).next_occurrence(Weekday::Monday), + /// date!(2023 - 07 - 03) + /// ); + /// assert_eq!( + /// date!(2023 - 06 - 19).next_occurrence(Weekday::Monday), + /// date!(2023 - 06 - 26) + /// ); + /// ``` + pub const fn next_occurrence(self, weekday: Weekday) -> Self { + expect_opt!( + self.checked_next_occurrence(weekday), + "overflow calculating the next occurrence of a weekday" + ) + } + + /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`. + /// + /// # Panics + /// Panics if an overflow occurred. + /// + /// # Examples + /// ``` + /// # use time::Weekday; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2023 - 06 - 28).prev_occurrence(Weekday::Monday), + /// date!(2023 - 06 - 26) + /// ); + /// assert_eq!( + /// date!(2023 - 06 - 19).prev_occurrence(Weekday::Monday), + /// date!(2023 - 06 - 12) + /// ); + /// ``` + pub const fn prev_occurrence(self, weekday: Weekday) -> Self { + expect_opt!( + self.checked_prev_occurrence(weekday), + "overflow calculating the previous occurrence of a weekday" + ) + } + + /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`. + /// + /// # Panics + /// Panics if an overflow occurred or if `n == 0`. + /// + /// # Examples + /// ``` + /// # use time::Weekday; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2023 - 06 - 25).nth_next_occurrence(Weekday::Monday, 5), + /// date!(2023 - 07 - 24) + /// ); + /// assert_eq!( + /// date!(2023 - 06 - 26).nth_next_occurrence(Weekday::Monday, 5), + /// date!(2023 - 07 - 31) + /// ); + /// ``` + pub const fn nth_next_occurrence(self, weekday: Weekday, n: u8) -> Self { + expect_opt!( + self.checked_nth_next_occurrence(weekday, n), + "overflow calculating the next occurrence of a weekday" + ) + } + + /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`. + /// + /// # Panics + /// Panics if an overflow occurred or if `n == 0`. + /// + /// # Examples + /// ``` + /// # use time::Weekday; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2023 - 06 - 27).nth_prev_occurrence(Weekday::Monday, 3), + /// date!(2023 - 06 - 12) + /// ); + /// assert_eq!( + /// date!(2023 - 06 - 26).nth_prev_occurrence(Weekday::Monday, 3), + /// date!(2023 - 06 - 05) + /// ); + /// ``` + pub const fn nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Self { + expect_opt!( + self.checked_nth_prev_occurrence(weekday, n), + "overflow calculating the previous occurrence of a weekday" + ) + } + /// Get the Julian day for the date. /// /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is @@ -597,6 +768,49 @@ impl Date { } } + /// Computes `self + duration`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{Date, ext::NumericalStdDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.checked_add_std(1.std_days()), None); + /// assert_eq!( + /// date!(2020 - 12 - 31).checked_add_std(2.std_days()), + /// Some(date!(2021 - 01 - 02)) + /// ); + /// ``` + /// + /// # Note + /// + /// This function only takes whole days into account. + /// + /// ```rust + /// # use time::{Date, ext::NumericalStdDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.checked_add_std(23.std_hours()), Some(Date::MAX)); + /// assert_eq!( + /// date!(2020 - 12 - 31).checked_add_std(23.std_hours()), + /// Some(date!(2020 - 12 - 31)) + /// ); + /// assert_eq!( + /// date!(2020 - 12 - 31).checked_add_std(47.std_hours()), + /// Some(date!(2021 - 01 - 01)) + /// ); + /// ``` + pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> { + let whole_days = duration.as_secs() / Second::per(Day) as u64; + if whole_days > i32::MAX as u64 { + return None; + } + + let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _)); + if let Ok(date) = Self::from_julian_day(julian_day) { + Some(date) + } else { + None + } + } + /// Computes `self - duration`, returning `None` if an overflow occurred. /// /// ``` @@ -641,6 +855,109 @@ impl Date { None } } + + /// Computes `self - duration`, returning `None` if an overflow occurred. + /// + /// ``` + /// # use time::{Date, ext::NumericalStdDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MIN.checked_sub_std(1.std_days()), None); + /// assert_eq!( + /// date!(2020 - 12 - 31).checked_sub_std(2.std_days()), + /// Some(date!(2020 - 12 - 29)) + /// ); + /// ``` + /// + /// # Note + /// + /// This function only takes whole days into account. + /// + /// ``` + /// # use time::{Date, ext::NumericalStdDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MIN.checked_sub_std(23.std_hours()), Some(Date::MIN)); + /// assert_eq!( + /// date!(2020 - 12 - 31).checked_sub_std(23.std_hours()), + /// Some(date!(2020 - 12 - 31)) + /// ); + /// assert_eq!( + /// date!(2020 - 12 - 31).checked_sub_std(47.std_hours()), + /// Some(date!(2020 - 12 - 30)) + /// ); + /// ``` + pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> { + let whole_days = duration.as_secs() / Second::per(Day) as u64; + if whole_days > i32::MAX as u64 { + return None; + } + + let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _)); + if let Ok(date) = Self::from_julian_day(julian_day) { + Some(date) + } else { + None + } + } + + /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`. + /// Returns `None` if an overflow occurred. + pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> { + let day_diff = match weekday as i8 - self.weekday() as i8 { + 1 | -6 => 1, + 2 | -5 => 2, + 3 | -4 => 3, + 4 | -3 => 4, + 5 | -2 => 5, + 6 | -1 => 6, + val => { + debug_assert!(val == 0); + 7 + } + }; + + self.checked_add(Duration::days(day_diff)) + } + + /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`. + /// Returns `None` if an overflow occurred. + pub(crate) const fn checked_prev_occurrence(self, weekday: Weekday) -> Option<Self> { + let day_diff = match weekday as i8 - self.weekday() as i8 { + 1 | -6 => 6, + 2 | -5 => 5, + 3 | -4 => 4, + 4 | -3 => 3, + 5 | -2 => 2, + 6 | -1 => 1, + val => { + debug_assert!(val == 0); + 7 + } + }; + + self.checked_sub(Duration::days(day_diff)) + } + + /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`. + /// Returns `None` if an overflow occurred or if `n == 0`. + pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> { + if n == 0 { + return None; + } + + const_try_opt!(self.checked_next_occurrence(weekday)) + .checked_add(Duration::weeks(n as i64 - 1)) + } + + /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`. + /// Returns `None` if an overflow occurred or if `n == 0`. + pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> { + if n == 0 { + return None; + } + + const_try_opt!(self.checked_prev_occurrence(weekday)) + .checked_sub(Duration::weeks(n as i64 - 1)) + } // endregion: checked arithmetic // region: saturating arithmetic @@ -739,17 +1056,21 @@ impl Date { /// ``` #[must_use = "This method does not mutate the original `Date`."] pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); + ensure_ranged!(Year: year); let ordinal = self.ordinal(); // Dates in January and February are unaffected by leap years. if ordinal <= 59 { - return Ok(Self::__from_ordinal_date_unchecked(year, ordinal)); + // Safety: `ordinal` is not zero. + return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }); } match (is_leap_year(self.year()), is_leap_year(year)) { - (false, false) | (true, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal)), + (false, false) | (true, true) => { + // Safety: `ordinal` is not zero. + Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }) + } // February 29 does not exist in common years. (true, false) if ordinal == 60 => Err(error::ComponentRange { name: "day", @@ -760,10 +1081,12 @@ impl Date { }), // We're going from a common year to a leap year. Shift dates in March and later by // one day. - (false, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal + 1)), + // Safety: `ordinal` is not zero. + (false, true) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal + 1) }), // We're going from a leap year to a common year. Shift dates in January and // February by one day. - (true, false) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal - 1)), + // Safety: `ordinal` is not zero. + (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal - 1) }), } } @@ -801,17 +1124,27 @@ impl Date { /// ``` #[must_use = "This method does not mutate the original `Date`."] pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { - // Days 1-28 are present in every month, so we can skip checking. - if day == 0 || day >= 29 { - ensure_value_in_range!( - day conditionally in 1 => days_in_year_month(self.year(), self.month()) - ); + match day { + 1..=28 => {} + 29..=31 if day <= days_in_year_month(self.year(), self.month()) => {} + _ => { + return Err(crate::error::ComponentRange { + name: "day", + minimum: 1, + maximum: days_in_year_month(self.year(), self.month()) as _, + value: day as _, + conditional_range: true, + }); + } } - Ok(Self::__from_ordinal_date_unchecked( - self.year(), - (self.ordinal() as i16 - self.day() as i16 + day as i16) as _, - )) + // Safety: `ordinal` is not zero. + Ok(unsafe { + Self::__from_ordinal_date_unchecked( + self.year(), + (self.ordinal() as i16 - self.day() as i16 + day as i16) as _, + ) + }) } // endregion replacement } @@ -1014,10 +1347,8 @@ impl Add<StdDuration> for Date { type Output = Self; fn add(self, duration: StdDuration) -> Self::Output { - Self::from_julian_day( - self.to_julian_day() + (duration.as_secs() / Second.per(Day) as u64) as i32, - ) - .expect("overflow adding duration to date") + self.checked_add_std(duration) + .expect("overflow adding duration to date") } } @@ -1036,10 +1367,8 @@ impl Sub<StdDuration> for Date { type Output = Self; fn sub(self, duration: StdDuration) -> Self::Output { - Self::from_julian_day( - self.to_julian_day() - (duration.as_secs() / Second.per(Day) as u64) as i32, - ) - .expect("overflow subtracting duration from date") + self.checked_sub_std(duration) + .expect("overflow subtracting duration from date") } } diff --git a/vendor/time/src/date_time.rs b/vendor/time/src/date_time.rs index 819f6d36b..53cae5eb4 100644 --- a/vendor/time/src/date_time.rs +++ b/vendor/time/src/date_time.rs @@ -16,10 +16,16 @@ use std::io; #[cfg(feature = "std")] use std::time::SystemTime; +use deranged::RangedI64; + use crate::convert::*; use crate::date::{MAX_YEAR, MIN_YEAR}; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{ + bug, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign, + impl_sub_assign, +}; #[cfg(feature = "parsing")] use crate::parsing::{Parsable, Parsed}; use crate::{error, util, Date, Duration, Month, Time, UtcOffset, Weekday}; @@ -191,7 +197,10 @@ pub(crate) const fn maybe_offset_from_offset<O: MaybeOffset>( // endregion const trait methods hacks /// The Julian day of the Unix epoch. -const UNIX_EPOCH_JULIAN_DAY: i32 = Date::__from_ordinal_date_unchecked(1970, 1).to_julian_day(); +// Safety: `ordinal` is not zero. +#[allow(clippy::undocumented_unsafe_blocks)] +const UNIX_EPOCH_JULIAN_DAY: i32 = + unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.to_julian_day(); pub struct DateTime<O: MaybeOffset> { pub(crate) date: Date, @@ -226,7 +235,8 @@ impl DateTime<offset_kind::None> { impl DateTime<offset_kind::Fixed> { pub const UNIX_EPOCH: Self = Self { - date: Date::__from_ordinal_date_unchecked(1970, 1), + // Safety: `ordinal` is not zero. + date: unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }, time: Time::MIDNIGHT, offset: UtcOffset::UTC, }; @@ -248,28 +258,27 @@ impl<O: MaybeOffset> DateTime<O> { where O: HasLogicalOffset, { - #[allow(clippy::missing_docs_in_private_items)] - const MIN_TIMESTAMP: i64 = Date::MIN.midnight().assume_utc().unix_timestamp(); - #[allow(clippy::missing_docs_in_private_items)] - const MAX_TIMESTAMP: i64 = Date::MAX - .with_time(Time::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999)) - .assume_utc() - .unix_timestamp(); - - ensure_value_in_range!(timestamp in MIN_TIMESTAMP => MAX_TIMESTAMP); + type Timestamp = RangedI64< + { Date::MIN.midnight().assume_utc().unix_timestamp() }, + { Date::MAX.with_time(Time::MAX).assume_utc().unix_timestamp() }, + >; + ensure_ranged!(Timestamp: timestamp); // Use the unchecked method here, as the input validity has already been verified. let date = Date::from_julian_day_unchecked( - UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second.per(Day) as i64) as i32, + UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second::per(Day) as i64) as i32, ); - let seconds_within_day = timestamp.rem_euclid(Second.per(Day) as _); - let time = Time::__from_hms_nanos_unchecked( - (seconds_within_day / Second.per(Hour) as i64) as _, - ((seconds_within_day % Second.per(Hour) as i64) / Minute.per(Hour) as i64) as _, - (seconds_within_day % Second.per(Minute) as i64) as _, - 0, - ); + let seconds_within_day = timestamp.rem_euclid(Second::per(Day) as _); + // Safety: All values are in range. + let time = unsafe { + Time::__from_hms_nanos_unchecked( + (seconds_within_day / Second::per(Hour) as i64) as _, + ((seconds_within_day % Second::per(Hour) as i64) / Minute::per(Hour) as i64) as _, + (seconds_within_day % Second::per(Minute) as i64) as _, + 0, + ) + }; Ok(Self { date, @@ -284,17 +293,20 @@ impl<O: MaybeOffset> DateTime<O> { { let datetime = const_try!(Self::from_unix_timestamp(div_floor!( timestamp, - Nanosecond.per(Second) as i128 + Nanosecond::per(Second) as i128 ) as i64)); Ok(Self { date: datetime.date, - time: Time::__from_hms_nanos_unchecked( - datetime.hour(), - datetime.minute(), - datetime.second(), - timestamp.rem_euclid(Nanosecond.per(Second) as _) as u32, - ), + // Safety: `nanosecond` is in range due to `rem_euclid`. + time: unsafe { + Time::__from_hms_nanos_unchecked( + datetime.hour(), + datetime.minute(), + datetime.second(), + timestamp.rem_euclid(Nanosecond::per(Second) as _) as u32, + ) + }, offset: maybe_offset_from_offset::<O>(UtcOffset::UTC), }) } @@ -455,9 +467,9 @@ impl<O: MaybeOffset> DateTime<O> { let offset = maybe_offset_as_offset::<O>(self.offset).whole_seconds() as i64; let days = - (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second.per(Day) as i64; - let hours = self.hour() as i64 * Second.per(Hour) as i64; - let minutes = self.minute() as i64 * Second.per(Minute) as i64; + (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second::per(Day) as i64; + let hours = self.hour() as i64 * Second::per(Hour) as i64; + let minutes = self.minute() as i64 * Second::per(Minute) as i64; let seconds = self.second() as i64; days + hours + minutes + seconds - offset } @@ -466,7 +478,7 @@ impl<O: MaybeOffset> DateTime<O> { where O: HasLogicalOffset, { - self.unix_timestamp() as i128 * Nanosecond.per(Second) as i128 + self.nanosecond() as i128 + self.unix_timestamp() as i128 * Nanosecond::per(Second) as i128 + self.nanosecond() as i128 } // endregion unix timestamp getters // endregion: getters @@ -526,7 +538,8 @@ impl<O: MaybeOffset> DateTime<O> { } Some(DateTime { - date: Date::__from_ordinal_date_unchecked(year, ordinal), + // Safety: `ordinal` is not zero. + date: unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) }, time, offset, }) @@ -558,12 +571,12 @@ impl<O: MaybeOffset> DateTime<O> { let mut ordinal = ordinal as i16; // Cascade the values twice. This is needed because the values are adjusted twice above. - cascade!(second in 0..Second.per(Minute) as i16 => minute); - cascade!(second in 0..Second.per(Minute) as i16 => minute); - cascade!(minute in 0..Minute.per(Hour) as i16 => hour); - cascade!(minute in 0..Minute.per(Hour) as i16 => hour); - cascade!(hour in 0..Hour.per(Day) as i8 => ordinal); - cascade!(hour in 0..Hour.per(Day) as i8 => ordinal); + cascade!(second in 0..Second::per(Minute) as i16 => minute); + cascade!(second in 0..Second::per(Minute) as i16 => minute); + cascade!(minute in 0..Minute::per(Hour) as i16 => hour); + cascade!(minute in 0..Minute::per(Hour) as i16 => hour); + cascade!(hour in 0..Hour::per(Day) as i8 => ordinal); + cascade!(hour in 0..Hour::per(Day) as i8 => ordinal); cascade!(ordinal => year); debug_assert!(ordinal > 0); @@ -572,12 +585,15 @@ impl<O: MaybeOffset> DateTime<O> { ( year, ordinal as _, - Time::__from_hms_nanos_unchecked( - hour as _, - minute as _, - second as _, - self.nanosecond(), - ), + // Safety: The cascades above ensure the values are in range. + unsafe { + Time::__from_hms_nanos_unchecked( + hour as _, + minute as _, + second as _, + self.nanosecond(), + ) + }, ) } // endregion to offset @@ -653,6 +669,7 @@ impl<O: MaybeOffset> DateTime<O> { // endregion saturating arithmetic // region: replacement + #[must_use = "this does not modify the original value"] pub const fn replace_time(self, time: Time) -> Self { Self { date: self.date, @@ -661,6 +678,7 @@ impl<O: MaybeOffset> DateTime<O> { } } + #[must_use = "this does not modify the original value"] pub const fn replace_date(self, date: Date) -> Self { Self { date, @@ -669,6 +687,7 @@ impl<O: MaybeOffset> DateTime<O> { } } + #[must_use = "this does not modify the original value"] pub const fn replace_date_time(self, date_time: DateTime<offset_kind::None>) -> Self where O: HasLogicalOffset, @@ -680,6 +699,7 @@ impl<O: MaybeOffset> DateTime<O> { } } + #[must_use = "this does not modify the original value"] pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { Ok(Self { date: const_try!(self.date.replace_year(year)), @@ -688,6 +708,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> { Ok(Self { date: const_try!(self.date.replace_month(month)), @@ -696,6 +717,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { Ok(Self { date: const_try!(self.date.replace_day(day)), @@ -704,6 +726,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { Ok(Self { date: self.date, @@ -712,6 +735,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> { Ok(Self { date: self.date, @@ -720,6 +744,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> { Ok(Self { date: self.date, @@ -728,6 +753,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_millisecond( self, millisecond: u16, @@ -739,6 +765,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_microsecond( self, microsecond: u32, @@ -750,6 +777,7 @@ impl<O: MaybeOffset> DateTime<O> { }) } + #[must_use = "this does not modify the original value"] pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { Ok(Self { date: self.date, @@ -760,6 +788,7 @@ impl<O: MaybeOffset> DateTime<O> { // Don't gate this on just having an offset, as `ZonedDateTime` cannot be set to an arbitrary // offset. + #[must_use = "this does not modify the original value"] pub const fn replace_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed> where O: IsOffsetKindFixed, @@ -822,7 +851,9 @@ impl<O: MaybeOffset> DateTime<O> { } let (year, ordinal, time) = self.to_offset_raw(UtcOffset::UTC); - let Ok(date) = Date::from_ordinal_date(year, ordinal) else { return false }; + let Ok(date) = Date::from_ordinal_date(year, ordinal) else { + return false; + }; time.hour() == 23 && time.minute() == 59 @@ -1156,7 +1187,7 @@ impl From<DateTime<offset_kind::Fixed>> for SystemTime { impl From<js_sys::Date> for DateTime<offset_kind::Fixed> { fn from(js_date: js_sys::Date) -> Self { // get_time() returns milliseconds - let timestamp_nanos = (js_date.get_time() * Nanosecond.per(Millisecond) as f64) as i128; + let timestamp_nanos = (js_date.get_time() * Nanosecond::per(Millisecond) as f64) as i128; Self::from_unix_timestamp_nanos(timestamp_nanos) .expect("invalid timestamp: Timestamp cannot fit in range") } @@ -1171,7 +1202,7 @@ impl From<DateTime<offset_kind::Fixed>> for js_sys::Date { fn from(datetime: DateTime<offset_kind::Fixed>) -> Self { // new Date() takes milliseconds let timestamp = - (datetime.unix_timestamp_nanos() / Nanosecond.per(Millisecond) as i128) as f64; + (datetime.unix_timestamp_nanos() / Nanosecond::per(Millisecond) as i128) as f64; js_sys::Date::new(×tamp.into()) } } diff --git a/vendor/time/src/duration.rs b/vendor/time/src/duration.rs index 453d2254d..4b4134f9e 100644 --- a/vendor/time/src/duration.rs +++ b/vendor/time/src/duration.rs @@ -6,8 +6,13 @@ use core::iter::Sum; use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; use core::time::Duration as StdDuration; +use deranged::RangedI32; + use crate::convert::*; use crate::error; +use crate::internal_macros::{ + const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, +}; #[cfg(feature = "std")] use crate::Instant; @@ -20,11 +25,9 @@ pub(crate) enum Padding { Optimize, } -impl Default for Padding { - fn default() -> Self { - Self::Optimize - } -} +/// The type of the `nanosecond` field of `Duration`. +type Nanoseconds = + RangedI32<{ -(Nanosecond::per(Second) as i32 - 1) }, { Nanosecond::per(Second) as i32 - 1 }>; /// A span of time with nanosecond precision. /// @@ -32,12 +35,13 @@ impl Default for Padding { /// nanoseconds. /// /// This implementation allows for negative durations, unlike [`core::time::Duration`]. -#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Duration { /// Number of whole seconds. seconds: i64, /// Number of nanoseconds within the second. The sign always matches the `seconds` field. - nanoseconds: i32, // always -10^9 < nanoseconds < 10^9 + // Sign must match that of `seconds` (though this is not a safety requirement). + nanoseconds: Nanoseconds, #[allow(clippy::missing_docs_in_private_items)] padding: Padding, } @@ -51,10 +55,22 @@ impl fmt::Debug for Duration { } } -/// This is adapted from the `std` implementation, which uses mostly bit +impl Default for Duration { + fn default() -> Self { + Self { + seconds: 0, + nanoseconds: Nanoseconds::new_static::<0>(), + padding: Padding::Optimize, + } + } +} + +/// This is adapted from the [`std` implementation][std], which uses mostly bit /// operations to ensure the highest precision: -/// https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340 +/// /// Changes from `std` are marked and explained below. +/// +/// [std]: https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340 #[rustfmt::skip] // Skip `rustfmt` because it reformats the arguments of the macro weirdly. macro_rules! try_from_secs { ( @@ -87,7 +103,7 @@ macro_rules! try_from_secs { // the input is less than 1 second let t = <$double_ty>::from(mant) << ($offset + exp); let nanos_offset = $mant_bits + $offset; - let nanos_tmp = u128::from(Nanosecond.per(Second)) * u128::from(t); + let nanos_tmp = u128::from(Nanosecond::per(Second)) * u128::from(t); let nanos = (nanos_tmp >> nanos_offset) as u32; let rem_mask = (1 << nanos_offset) - 1; @@ -101,7 +117,7 @@ macro_rules! try_from_secs { // f32 does not have enough precision to trigger the second branch // since it can not represent numbers between 0.999_999_940_395 and 1.0. let nanos = nanos + add_ns as u32; - if ($mant_bits == 23) || (nanos != Nanosecond.per(Second)) { + if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) { (0, nanos) } else { (1, 0) @@ -110,7 +126,7 @@ macro_rules! try_from_secs { let secs = u64::from(mant >> ($mant_bits - exp)); let t = <$double_ty>::from((mant << exp) & MANT_MASK); let nanos_offset = $mant_bits; - let nanos_tmp = <$double_ty>::from(Nanosecond.per(Second)) * t; + let nanos_tmp = <$double_ty>::from(Nanosecond::per(Second)) * t; let nanos = (nanos_tmp >> nanos_offset) as u32; let rem_mask = (1 << nanos_offset) - 1; @@ -126,7 +142,7 @@ macro_rules! try_from_secs { // and 2.0. Bigger values result in even smaller precision of the // fractional part. let nanos = nanos + add_ns as u32; - if ($mant_bits == 23) || (nanos != Nanosecond.per(Second)) { + if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) { (secs, nanos) } else { (secs + 1, 0) @@ -144,7 +160,7 @@ macro_rules! try_from_secs { // following numbers -128..=127. The check above (exp < 63) // doesn't cover i64::MIN as that is -2^63, so we have this // additional case to handle the asymmetry of iN::MIN. - break 'value Self::new_unchecked(i64::MIN, 0); + break 'value Self::new_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>()); } else if $secs.is_nan() { // Change from std: std doesn't differentiate between the error // cases. @@ -164,7 +180,8 @@ macro_rules! try_from_secs { let secs_signed = ((secs as i64) ^ (mask as i64)) - (mask as i64); #[allow(trivial_numeric_casts)] let nanos_signed = ((nanos as i32) ^ (mask as i32)) - (mask as i32); - Self::new_unchecked(secs_signed, nanos_signed) + // Safety: `nanos_signed` is in range. + unsafe { Self::new_unchecked(secs_signed, nanos_signed) } } }}; } @@ -244,10 +261,10 @@ impl Duration { pub const WEEK: Self = Self::weeks(1); /// The minimum possible duration. Adding any negative duration to this will cause an overflow. - pub const MIN: Self = Self::new_unchecked(i64::MIN, -((Nanosecond.per(Second) - 1) as i32)); + pub const MIN: Self = Self::new_ranged(i64::MIN, Nanoseconds::MIN); /// The maximum possible duration. Adding any positive duration to this will cause an overflow. - pub const MAX: Self = Self::new_unchecked(i64::MAX, (Nanosecond.per(Second) - 1) as _); + pub const MAX: Self = Self::new_ranged(i64::MAX, Nanoseconds::MAX); // endregion constants // region: is_{sign} @@ -259,7 +276,7 @@ impl Duration { /// assert!(!1.nanoseconds().is_zero()); /// ``` pub const fn is_zero(self) -> bool { - self.seconds == 0 && self.nanoseconds == 0 + self.seconds == 0 && self.nanoseconds.get() == 0 } /// Check if a duration is negative. @@ -271,7 +288,7 @@ impl Duration { /// assert!(!1.seconds().is_negative()); /// ``` pub const fn is_negative(self) -> bool { - self.seconds < 0 || self.nanoseconds < 0 + self.seconds < 0 || self.nanoseconds.get() < 0 } /// Check if a duration is positive. @@ -283,7 +300,7 @@ impl Duration { /// assert!(!(-1).seconds().is_positive()); /// ``` pub const fn is_positive(self) -> bool { - self.seconds > 0 || self.nanoseconds > 0 + self.seconds > 0 || self.nanoseconds.get() > 0 } // endregion is_{sign} @@ -299,7 +316,10 @@ impl Duration { /// assert_eq!((-1).seconds().abs(), 1.seconds()); /// ``` pub const fn abs(self) -> Self { - Self::new_unchecked(self.seconds.saturating_abs(), self.nanoseconds.abs()) + match self.seconds.checked_abs() { + Some(seconds) => Self::new_ranged_unchecked(seconds, self.nanoseconds.abs()), + None => Self::MAX, + } } /// Convert the existing `Duration` to a `std::time::Duration` and its sign. This returns a @@ -312,21 +332,36 @@ impl Duration { /// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds()); /// ``` pub const fn unsigned_abs(self) -> StdDuration { - StdDuration::new(self.seconds.unsigned_abs(), self.nanoseconds.unsigned_abs()) + StdDuration::new( + self.seconds.unsigned_abs(), + self.nanoseconds.get().unsigned_abs(), + ) } // endregion abs // region: constructors /// Create a new `Duration` without checking the validity of the components. - pub(crate) const fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self { + /// + /// # Safety + /// + /// - `nanoseconds` must be in the range `-999_999_999..=999_999_999`. + /// + /// While the sign of `nanoseconds` is required to be the same as the sign of `seconds`, this is + /// not a safety invariant. + pub(crate) const unsafe fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self { + Self::new_ranged_unchecked( + seconds, + // Safety: The caller must uphold the safety invariants. + unsafe { Nanoseconds::new_unchecked(nanoseconds) }, + ) + } + + /// Create a new `Duration` without checking the validity of the components. + pub(crate) const fn new_ranged_unchecked(seconds: i64, nanoseconds: Nanoseconds) -> Self { if seconds < 0 { - debug_assert!(nanoseconds <= 0); - debug_assert!(nanoseconds > -(Nanosecond.per(Second) as i32)); + debug_assert!(nanoseconds.get() <= 0); } else if seconds > 0 { - debug_assert!(nanoseconds >= 0); - debug_assert!(nanoseconds < Nanosecond.per(Second) as _); - } else { - debug_assert!(nanoseconds.unsigned_abs() < Nanosecond.per(Second)); + debug_assert!(nanoseconds.get() >= 0); } Self { @@ -347,22 +382,46 @@ impl Duration { /// ``` pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self { seconds = expect_opt!( - seconds.checked_add(nanoseconds as i64 / Nanosecond.per(Second) as i64), + seconds.checked_add(nanoseconds as i64 / Nanosecond::per(Second) as i64), "overflow constructing `time::Duration`" ); - nanoseconds %= Nanosecond.per(Second) as i32; + nanoseconds %= Nanosecond::per(Second) as i32; if seconds > 0 && nanoseconds < 0 { // `seconds` cannot overflow here because it is positive. seconds -= 1; - nanoseconds += Nanosecond.per(Second) as i32; + nanoseconds += Nanosecond::per(Second) as i32; } else if seconds < 0 && nanoseconds > 0 { // `seconds` cannot overflow here because it is negative. seconds += 1; - nanoseconds -= Nanosecond.per(Second) as i32; + nanoseconds -= Nanosecond::per(Second) as i32; + } + + // Safety: `nanoseconds` is in range due to the modulus above. + unsafe { Self::new_unchecked(seconds, nanoseconds) } + } + + /// Create a new `Duration` with the provided seconds and nanoseconds. + pub(crate) const fn new_ranged(mut seconds: i64, mut nanoseconds: Nanoseconds) -> Self { + if seconds > 0 && nanoseconds.get() < 0 { + // `seconds` cannot overflow here because it is positive. + seconds -= 1; + // Safety: `nanoseconds` is negative with a maximum of 999,999,999, so adding a billion + // to it is guaranteed to result in an in-range value. + nanoseconds = unsafe { + Nanoseconds::new_unchecked(nanoseconds.get() + Nanosecond::per(Second) as i32) + }; + } else if seconds < 0 && nanoseconds.get() > 0 { + // `seconds` cannot overflow here because it is negative. + seconds += 1; + // Safety: `nanoseconds` is positive with a minimum of -999,999,999, so subtracting a + // billion from it is guaranteed to result in an in-range value. + nanoseconds = unsafe { + Nanoseconds::new_unchecked(nanoseconds.get() - Nanosecond::per(Second) as i32) + }; } - Self::new_unchecked(seconds, nanoseconds) + Self::new_ranged_unchecked(seconds, nanoseconds) } /// Create a new `Duration` with the given number of weeks. Equivalent to @@ -374,7 +433,7 @@ impl Duration { /// ``` pub const fn weeks(weeks: i64) -> Self { Self::seconds(expect_opt!( - weeks.checked_mul(Second.per(Week) as _), + weeks.checked_mul(Second::per(Week) as _), "overflow constructing `time::Duration`" )) } @@ -388,7 +447,7 @@ impl Duration { /// ``` pub const fn days(days: i64) -> Self { Self::seconds(expect_opt!( - days.checked_mul(Second.per(Day) as _), + days.checked_mul(Second::per(Day) as _), "overflow constructing `time::Duration`" )) } @@ -402,7 +461,7 @@ impl Duration { /// ``` pub const fn hours(hours: i64) -> Self { Self::seconds(expect_opt!( - hours.checked_mul(Second.per(Hour) as _), + hours.checked_mul(Second::per(Hour) as _), "overflow constructing `time::Duration`" )) } @@ -416,7 +475,7 @@ impl Duration { /// ``` pub const fn minutes(minutes: i64) -> Self { Self::seconds(expect_opt!( - minutes.checked_mul(Second.per(Minute) as _), + minutes.checked_mul(Second::per(Minute) as _), "overflow constructing `time::Duration`" )) } @@ -428,7 +487,7 @@ impl Duration { /// assert_eq!(Duration::seconds(1), 1_000.milliseconds()); /// ``` pub const fn seconds(seconds: i64) -> Self { - Self::new_unchecked(seconds, 0) + Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>()) } /// Creates a new `Duration` from the specified number of seconds represented as `f64`. @@ -611,11 +670,14 @@ impl Duration { /// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds()); /// ``` pub const fn milliseconds(milliseconds: i64) -> Self { - Self::new_unchecked( - milliseconds / Millisecond.per(Second) as i64, - (milliseconds % Millisecond.per(Second) as i64 * Nanosecond.per(Millisecond) as i64) - as _, - ) + // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. + unsafe { + Self::new_unchecked( + milliseconds / Millisecond::per(Second) as i64, + (milliseconds % Millisecond::per(Second) as i64 + * Nanosecond::per(Millisecond) as i64) as _, + ) + } } /// Create a new `Duration` with the given number of microseconds. @@ -626,11 +688,14 @@ impl Duration { /// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds()); /// ``` pub const fn microseconds(microseconds: i64) -> Self { - Self::new_unchecked( - microseconds / Microsecond.per(Second) as i64, - (microseconds % Microsecond.per(Second) as i64 * Nanosecond.per(Microsecond) as i64) - as _, - ) + // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. + unsafe { + Self::new_unchecked( + microseconds / Microsecond::per(Second) as i64, + (microseconds % Microsecond::per(Second) as i64 + * Nanosecond::per(Microsecond) as i64) as _, + ) + } } /// Create a new `Duration` with the given number of nanoseconds. @@ -641,10 +706,13 @@ impl Duration { /// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000); /// ``` pub const fn nanoseconds(nanoseconds: i64) -> Self { - Self::new_unchecked( - nanoseconds / Nanosecond.per(Second) as i64, - (nanoseconds % Nanosecond.per(Second) as i64) as _, - ) + // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. + unsafe { + Self::new_unchecked( + nanoseconds / Nanosecond::per(Second) as i64, + (nanoseconds % Nanosecond::per(Second) as i64) as _, + ) + } } /// Create a new `Duration` with the given number of nanoseconds. @@ -652,14 +720,15 @@ impl Duration { /// As the input range cannot be fully mapped to the output, this should only be used where it's /// known to result in a valid value. pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self { - let seconds = nanoseconds / Nanosecond.per(Second) as i128; - let nanoseconds = nanoseconds % Nanosecond.per(Second) as i128; + let seconds = nanoseconds / Nanosecond::per(Second) as i128; + let nanoseconds = nanoseconds % Nanosecond::per(Second) as i128; if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 { crate::expect_failed("overflow constructing `time::Duration`"); } - Self::new_unchecked(seconds as _, nanoseconds as _) + // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. + unsafe { Self::new_unchecked(seconds as _, nanoseconds as _) } } // endregion constructors @@ -674,7 +743,7 @@ impl Duration { /// assert_eq!((-6).days().whole_weeks(), 0); /// ``` pub const fn whole_weeks(self) -> i64 { - self.whole_seconds() / Second.per(Week) as i64 + self.whole_seconds() / Second::per(Week) as i64 } /// Get the number of whole days in the duration. @@ -687,7 +756,7 @@ impl Duration { /// assert_eq!((-23).hours().whole_days(), 0); /// ``` pub const fn whole_days(self) -> i64 { - self.whole_seconds() / Second.per(Day) as i64 + self.whole_seconds() / Second::per(Day) as i64 } /// Get the number of whole hours in the duration. @@ -700,7 +769,7 @@ impl Duration { /// assert_eq!((-59).minutes().whole_hours(), 0); /// ``` pub const fn whole_hours(self) -> i64 { - self.whole_seconds() / Second.per(Hour) as i64 + self.whole_seconds() / Second::per(Hour) as i64 } /// Get the number of whole minutes in the duration. @@ -713,7 +782,7 @@ impl Duration { /// assert_eq!((-59).seconds().whole_minutes(), 0); /// ``` pub const fn whole_minutes(self) -> i64 { - self.whole_seconds() / Second.per(Minute) as i64 + self.whole_seconds() / Second::per(Minute) as i64 } /// Get the number of whole seconds in the duration. @@ -737,7 +806,7 @@ impl Duration { /// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5); /// ``` pub fn as_seconds_f64(self) -> f64 { - self.seconds as f64 + self.nanoseconds as f64 / Nanosecond.per(Second) as f64 + self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per(Second) as f64 } /// Get the number of fractional seconds in the duration. @@ -748,7 +817,7 @@ impl Duration { /// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5); /// ``` pub fn as_seconds_f32(self) -> f32 { - self.seconds as f32 + self.nanoseconds as f32 / Nanosecond.per(Second) as f32 + self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per(Second) as f32 } /// Get the number of whole milliseconds in the duration. @@ -761,8 +830,8 @@ impl Duration { /// assert_eq!((-1).milliseconds().whole_milliseconds(), -1); /// ``` pub const fn whole_milliseconds(self) -> i128 { - self.seconds as i128 * Millisecond.per(Second) as i128 - + self.nanoseconds as i128 / Nanosecond.per(Millisecond) as i128 + self.seconds as i128 * Millisecond::per(Second) as i128 + + self.nanoseconds.get() as i128 / Nanosecond::per(Millisecond) as i128 } /// Get the number of milliseconds past the number of whole seconds. @@ -776,7 +845,7 @@ impl Duration { /// ``` // Allow the lint, as the value is guaranteed to be less than 1000. pub const fn subsec_milliseconds(self) -> i16 { - (self.nanoseconds / Nanosecond.per(Millisecond) as i32) as _ + (self.nanoseconds.get() / Nanosecond::per(Millisecond) as i32) as _ } /// Get the number of whole microseconds in the duration. @@ -789,8 +858,8 @@ impl Duration { /// assert_eq!((-1).microseconds().whole_microseconds(), -1); /// ``` pub const fn whole_microseconds(self) -> i128 { - self.seconds as i128 * Microsecond.per(Second) as i128 - + self.nanoseconds as i128 / Nanosecond.per(Microsecond) as i128 + self.seconds as i128 * Microsecond::per(Second) as i128 + + self.nanoseconds.get() as i128 / Nanosecond::per(Microsecond) as i128 } /// Get the number of microseconds past the number of whole seconds. @@ -803,7 +872,7 @@ impl Duration { /// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400); /// ``` pub const fn subsec_microseconds(self) -> i32 { - self.nanoseconds / Nanosecond.per(Microsecond) as i32 + self.nanoseconds.get() / Nanosecond::per(Microsecond) as i32 } /// Get the number of nanoseconds in the duration. @@ -816,7 +885,7 @@ impl Duration { /// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1); /// ``` pub const fn whole_nanoseconds(self) -> i128 { - self.seconds as i128 * Nanosecond.per(Second) as i128 + self.nanoseconds as i128 + self.seconds as i128 * Nanosecond::per(Second) as i128 + self.nanoseconds.get() as i128 } /// Get the number of nanoseconds past the number of whole seconds. @@ -829,6 +898,12 @@ impl Duration { /// assert_eq!((-1.000_000_400).seconds().subsec_nanoseconds(), -400); /// ``` pub const fn subsec_nanoseconds(self) -> i32 { + self.nanoseconds.get() + } + + /// Get the number of nanoseconds past the number of whole seconds. + #[cfg(feature = "quickcheck")] + pub(crate) const fn subsec_nanoseconds_ranged(self) -> Nanoseconds { self.nanoseconds } // endregion getters @@ -844,18 +919,19 @@ impl Duration { /// ``` pub const fn checked_add(self, rhs: Self) -> Option<Self> { let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds)); - let mut nanoseconds = self.nanoseconds + rhs.nanoseconds; + let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get(); - if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 { - nanoseconds -= Nanosecond.per(Second) as i32; + if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; seconds = const_try_opt!(seconds.checked_add(1)); - } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0 + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 { - nanoseconds += Nanosecond.per(Second) as i32; + nanoseconds += Nanosecond::per(Second) as i32; seconds = const_try_opt!(seconds.checked_sub(1)); } - Some(Self::new_unchecked(seconds, nanoseconds)) + // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. + unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } } /// Computes `self - rhs`, returning `None` if an overflow occurred. @@ -868,18 +944,19 @@ impl Duration { /// ``` pub const fn checked_sub(self, rhs: Self) -> Option<Self> { let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds)); - let mut nanoseconds = self.nanoseconds - rhs.nanoseconds; + let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); - if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 { - nanoseconds -= Nanosecond.per(Second) as i32; + if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; seconds = const_try_opt!(seconds.checked_add(1)); - } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0 + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 { - nanoseconds += Nanosecond.per(Second) as i32; + nanoseconds += Nanosecond::per(Second) as i32; seconds = const_try_opt!(seconds.checked_sub(1)); } - Some(Self::new_unchecked(seconds, nanoseconds)) + // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. + unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } } /// Computes `self * rhs`, returning `None` if an overflow occurred. @@ -894,14 +971,15 @@ impl Duration { /// ``` pub const fn checked_mul(self, rhs: i32) -> Option<Self> { // Multiply nanoseconds as i64, because it cannot overflow that way. - let total_nanos = self.nanoseconds as i64 * rhs as i64; - let extra_secs = total_nanos / Nanosecond.per(Second) as i64; - let nanoseconds = (total_nanos % Nanosecond.per(Second) as i64) as _; + let total_nanos = self.nanoseconds.get() as i64 * rhs as i64; + let extra_secs = total_nanos / Nanosecond::per(Second) as i64; + let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _; let seconds = const_try_opt!( const_try_opt!(self.seconds.checked_mul(rhs as _)).checked_add(extra_secs) ); - Some(Self::new_unchecked(seconds, nanoseconds)) + // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. + unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } } /// Computes `self / rhs`, returning `None` if `rhs == 0` or if the result would overflow. @@ -913,13 +991,16 @@ impl Duration { /// assert_eq!(1.seconds().checked_div(0), None); /// ``` pub const fn checked_div(self, rhs: i32) -> Option<Self> { - let seconds = const_try_opt!(self.seconds.checked_div(rhs as i64)); - let carry = self.seconds - seconds * (rhs as i64); - let extra_nanos = - const_try_opt!((carry * Nanosecond.per(Second) as i64).checked_div(rhs as i64)); - let nanoseconds = const_try_opt!(self.nanoseconds.checked_div(rhs)) + (extra_nanos as i32); + let (secs, extra_secs) = ( + const_try_opt!(self.seconds.checked_div(rhs as i64)), + self.seconds % (rhs as i64), + ); + let (mut nanos, extra_nanos) = (self.nanoseconds.get() / rhs, self.nanoseconds.get() % rhs); + nanos += ((extra_secs * (Nanosecond::per(Second) as i64) + extra_nanos as i64) + / (rhs as i64)) as i32; - Some(Self::new_unchecked(seconds, nanoseconds)) + // Safety: `nanoseconds` is in range. + unsafe { Some(Self::new_unchecked(secs, nanos)) } } // endregion checked arithmetic @@ -944,24 +1025,25 @@ impl Duration { } return Self::MIN; } - let mut nanoseconds = self.nanoseconds + rhs.nanoseconds; + let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get(); - if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 { - nanoseconds -= Nanosecond.per(Second) as i32; + if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; seconds = match seconds.checked_add(1) { Some(seconds) => seconds, None => return Self::MAX, }; - } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0 + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 { - nanoseconds += Nanosecond.per(Second) as i32; + nanoseconds += Nanosecond::per(Second) as i32; seconds = match seconds.checked_sub(1) { Some(seconds) => seconds, None => return Self::MIN, }; } - Self::new_unchecked(seconds, nanoseconds) + // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. + unsafe { Self::new_unchecked(seconds, nanoseconds) } } /// Computes `self - rhs`, saturating if an overflow occurred. @@ -984,24 +1066,25 @@ impl Duration { } return Self::MIN; } - let mut nanoseconds = self.nanoseconds - rhs.nanoseconds; + let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); - if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 { - nanoseconds -= Nanosecond.per(Second) as i32; + if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; seconds = match seconds.checked_add(1) { Some(seconds) => seconds, None => return Self::MAX, }; - } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0 + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 { - nanoseconds += Nanosecond.per(Second) as i32; + nanoseconds += Nanosecond::per(Second) as i32; seconds = match seconds.checked_sub(1) { Some(seconds) => seconds, None => return Self::MIN, }; } - Self::new_unchecked(seconds, nanoseconds) + // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. + unsafe { Self::new_unchecked(seconds, nanoseconds) } } /// Computes `self * rhs`, saturating if an overflow occurred. @@ -1018,9 +1101,9 @@ impl Duration { /// ``` pub const fn saturating_mul(self, rhs: i32) -> Self { // Multiply nanoseconds as i64, because it cannot overflow that way. - let total_nanos = self.nanoseconds as i64 * rhs as i64; - let extra_secs = total_nanos / Nanosecond.per(Second) as i64; - let nanoseconds = (total_nanos % Nanosecond.per(Second) as i64) as _; + let total_nanos = self.nanoseconds.get() as i64 * rhs as i64; + let extra_secs = total_nanos / Nanosecond::per(Second) as i64; + let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _; let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as _); if overflow1 { if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 { @@ -1036,7 +1119,8 @@ impl Duration { return Self::MIN; } - Self::new_unchecked(seconds, nanoseconds) + // Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above. + unsafe { Self::new_unchecked(seconds, nanoseconds) } } // endregion saturating arithmetic @@ -1094,13 +1178,13 @@ impl fmt::Display for Duration { // Even if this produces a de-normal float, because we're rounding we don't really care. let seconds = self.unsigned_abs().as_secs_f64(); - item!("d", seconds / Second.per(Day) as f64); - item!("h", seconds / Second.per(Hour) as f64); - item!("m", seconds / Second.per(Minute) as f64); + item!("d", seconds / Second::per(Day) as f64); + item!("h", seconds / Second::per(Hour) as f64); + item!("m", seconds / Second::per(Minute) as f64); item!("s", seconds); - item!("ms", seconds * Millisecond.per(Second) as f64); - item!("µs", seconds * Microsecond.per(Second) as f64); - item!("ns", seconds * Nanosecond.per(Second) as f64); + item!("ms", seconds * Millisecond::per(Second) as f64); + item!("µs", seconds * Microsecond::per(Second) as f64); + item!("ns", seconds * Nanosecond::per(Second) as f64); } else { // Precise, but verbose representation. @@ -1119,25 +1203,25 @@ impl fmt::Display for Duration { } let seconds = self.seconds.unsigned_abs(); - let nanoseconds = self.nanoseconds.unsigned_abs(); + let nanoseconds = self.nanoseconds.get().unsigned_abs(); - item!("d", seconds / Second.per(Day) as u64)?; + item!("d", seconds / Second::per(Day) as u64)?; item!( "h", - seconds / Second.per(Hour) as u64 % Hour.per(Day) as u64 + seconds / Second::per(Hour) as u64 % Hour::per(Day) as u64 )?; item!( "m", - seconds / Second.per(Minute) as u64 % Minute.per(Hour) as u64 + seconds / Second::per(Minute) as u64 % Minute::per(Hour) as u64 )?; - item!("s", seconds % Second.per(Minute) as u64)?; - item!("ms", nanoseconds / Nanosecond.per(Millisecond))?; + item!("s", seconds % Second::per(Minute) as u64)?; + item!("ms", nanoseconds / Nanosecond::per(Millisecond))?; item!( "µs", - nanoseconds / Nanosecond.per(Microsecond) as u32 - % Microsecond.per(Millisecond) as u32 + nanoseconds / Nanosecond::per(Microsecond) as u32 + % Microsecond::per(Millisecond) as u32 )?; - item!("ns", nanoseconds % Nanosecond.per(Microsecond) as u32)?; + item!("ns", nanoseconds % Nanosecond::per(Microsecond) as u32)?; } Ok(()) @@ -1169,6 +1253,7 @@ impl TryFrom<Duration> for StdDuration { .map_err(|_| error::ConversionRange)?, duration .nanoseconds + .get() .try_into() .map_err(|_| error::ConversionRange)?, )) @@ -1216,7 +1301,7 @@ impl Neg for Duration { type Output = Self; fn neg(self) -> Self::Output { - Self::new_unchecked(-self.seconds, -self.nanoseconds) + Self::new_ranged_unchecked(-self.seconds, self.nanoseconds.neg()) } } @@ -1390,7 +1475,7 @@ impl PartialOrd<StdDuration> for Duration { Some( self.seconds .cmp(&(rhs.as_secs() as _)) - .then_with(|| self.nanoseconds.cmp(&(rhs.subsec_nanos() as _))), + .then_with(|| self.nanoseconds.get().cmp(&(rhs.subsec_nanos() as _))), ) } } diff --git a/vendor/time/src/error/mod.rs b/vendor/time/src/error/mod.rs index 346b89f74..d2730c462 100644 --- a/vendor/time/src/error/mod.rs +++ b/vendor/time/src/error/mod.rs @@ -36,31 +36,47 @@ pub use parse_from_description::ParseFromDescription; #[cfg(feature = "parsing")] pub use try_from_parsed::TryFromParsed; +#[cfg(feature = "parsing")] +use crate::internal_macros::bug; + /// A unified error type for anything returned by a method in the time crate. /// /// This can be used when you either don't know or don't care about the exact error returned. /// `Result<_, time::Error>` (or its alias `time::Result<_>`) will work in these situations. #[allow(missing_copy_implementations, variant_size_differences)] -#[allow(clippy::missing_docs_in_private_items)] // variants only #[non_exhaustive] #[derive(Debug)] pub enum Error { + #[allow(missing_docs)] ConversionRange(ConversionRange), + #[allow(missing_docs)] ComponentRange(ComponentRange), #[cfg(feature = "local-offset")] + #[allow(missing_docs)] IndeterminateOffset(IndeterminateOffset), #[cfg(feature = "formatting")] + #[allow(missing_docs)] Format(Format), #[cfg(feature = "parsing")] + #[allow(missing_docs)] ParseFromDescription(ParseFromDescription), #[cfg(feature = "parsing")] + #[allow(missing_docs)] #[non_exhaustive] + #[deprecated( + since = "0.3.28", + note = "no longer output. moved to the `ParseFromDescription` variant" + )] UnexpectedTrailingCharacters, #[cfg(feature = "parsing")] + #[allow(missing_docs)] TryFromParsed(TryFromParsed), #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] + #[allow(missing_docs)] InvalidFormatDescription(InvalidFormatDescription), + #[allow(missing_docs)] DifferentVariant(DifferentVariant), + #[allow(missing_docs)] InvalidVariant(InvalidVariant), } @@ -76,7 +92,8 @@ impl fmt::Display for Error { #[cfg(feature = "parsing")] Self::ParseFromDescription(e) => e.fmt(f), #[cfg(feature = "parsing")] - Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"), + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters => bug!("variant should not be used"), #[cfg(feature = "parsing")] Self::TryFromParsed(e) => e.fmt(f), #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] @@ -100,7 +117,8 @@ impl std::error::Error for Error { #[cfg(feature = "parsing")] Self::ParseFromDescription(err) => Some(err), #[cfg(feature = "parsing")] - Self::UnexpectedTrailingCharacters => None, + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters => bug!("variant should not be used"), #[cfg(feature = "parsing")] Self::TryFromParsed(err) => Some(err), #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] diff --git a/vendor/time/src/error/parse.rs b/vendor/time/src/error/parse.rs index b90ac74e7..09935b1c9 100644 --- a/vendor/time/src/error/parse.rs +++ b/vendor/time/src/error/parse.rs @@ -3,18 +3,23 @@ use core::fmt; use crate::error::{self, ParseFromDescription, TryFromParsed}; +use crate::internal_macros::bug; /// An error that occurred at some stage of parsing. #[allow(variant_size_differences)] #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Parse { - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] TryFromParsed(TryFromParsed), - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] ParseFromDescription(ParseFromDescription), /// The input should have ended, but there were characters remaining. #[non_exhaustive] + #[deprecated( + since = "0.3.28", + note = "no longer output. moved to the `ParseFromDescription` variant" + )] UnexpectedTrailingCharacters, } @@ -23,7 +28,8 @@ impl fmt::Display for Parse { match self { Self::TryFromParsed(err) => err.fmt(f), Self::ParseFromDescription(err) => err.fmt(f), - Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"), + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters => bug!("variant should not be used"), } } } @@ -34,7 +40,8 @@ impl std::error::Error for Parse { match self { Self::TryFromParsed(err) => Some(err), Self::ParseFromDescription(err) => Some(err), - Self::UnexpectedTrailingCharacters => None, + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters => bug!("variant should not be used"), } } } @@ -78,7 +85,8 @@ impl From<Parse> for crate::Error { match err { Parse::TryFromParsed(err) => Self::TryFromParsed(err), Parse::ParseFromDescription(err) => Self::ParseFromDescription(err), - Parse::UnexpectedTrailingCharacters => Self::UnexpectedTrailingCharacters, + #[allow(deprecated)] + Parse::UnexpectedTrailingCharacters => bug!("variant should not be used"), } } } @@ -89,7 +97,8 @@ impl TryFrom<crate::Error> for Parse { fn try_from(err: crate::Error) -> Result<Self, Self::Error> { match err { crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)), - crate::Error::UnexpectedTrailingCharacters => Ok(Self::UnexpectedTrailingCharacters), + #[allow(deprecated)] + crate::Error::UnexpectedTrailingCharacters => bug!("variant should not be used"), crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)), _ => Err(error::DifferentVariant), } diff --git a/vendor/time/src/error/parse_from_description.rs b/vendor/time/src/error/parse_from_description.rs index 772f10d17..40124dce3 100644 --- a/vendor/time/src/error/parse_from_description.rs +++ b/vendor/time/src/error/parse_from_description.rs @@ -13,6 +13,9 @@ pub enum ParseFromDescription { InvalidLiteral, /// A dynamic component was not valid. InvalidComponent(&'static str), + /// The input was expected to have ended, but there are characters that remain. + #[non_exhaustive] + UnexpectedTrailingCharacters, } impl fmt::Display for ParseFromDescription { @@ -22,6 +25,9 @@ impl fmt::Display for ParseFromDescription { Self::InvalidComponent(name) => { write!(f, "the '{name}' component could not be parsed") } + Self::UnexpectedTrailingCharacters => { + f.write_str("unexpected trailing characters; the end of input was expected") + } } } } diff --git a/vendor/time/src/ext.rs b/vendor/time/src/ext.rs index 8b7759d8e..a2acc1101 100644 --- a/vendor/time/src/ext.rs +++ b/vendor/time/src/ext.rs @@ -117,31 +117,31 @@ impl NumericalDuration for f64 { } fn microseconds(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Microsecond) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Microsecond) as Self) as _) } fn milliseconds(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Millisecond) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Millisecond) as Self) as _) } fn seconds(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Second) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Second) as Self) as _) } fn minutes(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Minute) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Minute) as Self) as _) } fn hours(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Hour) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Hour) as Self) as _) } fn days(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Day) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Day) as Self) as _) } fn weeks(self) -> Duration { - Duration::nanoseconds((self * Nanosecond.per(Week) as Self) as _) + Duration::nanoseconds((self * Nanosecond::per(Week) as Self) as _) } } // endregion NumericalDuration @@ -220,19 +220,19 @@ impl NumericalStdDuration for u64 { } fn std_minutes(self) -> StdDuration { - StdDuration::from_secs(self * Second.per(Minute) as Self) + StdDuration::from_secs(self * Second::per(Minute) as Self) } fn std_hours(self) -> StdDuration { - StdDuration::from_secs(self * Second.per(Hour) as Self) + StdDuration::from_secs(self * Second::per(Hour) as Self) } fn std_days(self) -> StdDuration { - StdDuration::from_secs(self * Second.per(Day) as Self) + StdDuration::from_secs(self * Second::per(Day) as Self) } fn std_weeks(self) -> StdDuration { - StdDuration::from_secs(self * Second.per(Week) as Self) + StdDuration::from_secs(self * Second::per(Week) as Self) } } @@ -244,37 +244,37 @@ impl NumericalStdDuration for f64 { fn std_microseconds(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Microsecond) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Microsecond) as Self) as _) } fn std_milliseconds(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Millisecond) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Millisecond) as Self) as _) } fn std_seconds(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Second) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Second) as Self) as _) } fn std_minutes(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Minute) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Minute) as Self) as _) } fn std_hours(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Hour) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Hour) as Self) as _) } fn std_days(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Day) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Day) as Self) as _) } fn std_weeks(self) -> StdDuration { assert!(self >= 0.); - StdDuration::from_nanos((self * Nanosecond.per(Week) as Self) as _) + StdDuration::from_nanos((self * Nanosecond::per(Week) as Self) as _) } } // endregion NumericalStdDuration diff --git a/vendor/time/src/format_description/component.rs b/vendor/time/src/format_description/component.rs index 672559081..9119c0905 100644 --- a/vendor/time/src/format_description/component.rs +++ b/vendor/time/src/format_description/component.rs @@ -38,4 +38,7 @@ pub enum Component { Ignore(modifier::Ignore), /// A Unix timestamp. UnixTimestamp(modifier::UnixTimestamp), + /// The end of input. Parsing this component will fail if there is any input remaining. This + /// component neither affects formatting nor consumes any input when parsing. + End(modifier::End), } diff --git a/vendor/time/src/format_description/modifier.rs b/vendor/time/src/format_description/modifier.rs index cdac1ae97..3a57bb6f6 100644 --- a/vendor/time/src/format_description/modifier.rs +++ b/vendor/time/src/format_description/modifier.rs @@ -279,6 +279,13 @@ pub struct UnixTimestamp { pub sign_is_mandatory: bool, } +/// The end of input. +/// +/// There is currently not customization for this modifier. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct End; + /// Generate the provided code if and only if `pub` is present. macro_rules! if_pub { (pub $(#[$attr:meta])*; $($x:tt)*) => { @@ -385,10 +392,10 @@ impl_const_default! { /// Creates a modifier that indicates the stringified value contains [one or more /// digits](SubsecondDigits::OneOrMore). @pub Subsecond => Self { digits: SubsecondDigits::OneOrMore }; - /// Creates a modifier that indicates the value uses the `+` sign for all positive values - /// and is [padded with zeroes](Padding::Zero). + /// Creates a modifier that indicates the value only uses a sign for negative values and is + /// [padded with zeroes](Padding::Zero). @pub OffsetHour => Self { - sign_is_mandatory: true, + sign_is_mandatory: false, padding: Padding::Zero, }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @@ -406,4 +413,6 @@ impl_const_default! { precision: UnixTimestampPrecision::Second, sign_is_mandatory: false, }; + /// Creates a modifier used to represent the end of input. + @pub End => End; } diff --git a/vendor/time/src/format_description/parse/ast.rs b/vendor/time/src/format_description/parse/ast.rs index 12fc5d55d..c7fc5e05a 100644 --- a/vendor/time/src/format_description/parse/ast.rs +++ b/vendor/time/src/format_description/parse/ast.rs @@ -6,6 +6,7 @@ use alloc::vec::Vec; use core::iter; use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused}; +use crate::internal_macros::bug; /// One part of a complete format description. pub(super) enum Item<'a> { @@ -266,7 +267,9 @@ fn parse_component< let mut modifiers = Vec::new(); let trailing_whitespace = loop { - let Some(whitespace) = tokens.next_if_whitespace() else { break None }; + let Some(whitespace) = tokens.next_if_whitespace() else { + break None; + }; // This is not necessary for proper parsing, but provides a much better error when a nested // description is used where it's not allowed. diff --git a/vendor/time/src/format_description/parse/format_item.rs b/vendor/time/src/format_description/parse/format_item.rs index c0e64d92a..f33078ec4 100644 --- a/vendor/time/src/format_description/parse/format_item.rs +++ b/vendor/time/src/format_description/parse/format_item.rs @@ -6,6 +6,7 @@ use core::num::NonZeroU16; use core::str::{self, FromStr}; use super::{ast, unused, Error, Span, Spanned}; +use crate::internal_macros::bug; /// Parse an AST iterator into a sequence of format items. pub(super) fn parse<'a>( @@ -190,6 +191,8 @@ macro_rules! component_definition { _component_span: Span, ) -> Result<Self, Error> { + // rustc will complain if the modifier is empty. + #[allow(unused_mut)] let mut this = Self { $($field: None),* }; @@ -280,6 +283,7 @@ component_definition! { Day = "day" { padding = "padding": Option<Padding> => padding, }, + End = "end" {}, Hour = "hour" { padding = "padding": Option<Padding> => padding, base = "repr": Option<HourBase> => is_12_hour_clock, diff --git a/vendor/time/src/format_description/well_known/iso8601.rs b/vendor/time/src/format_description/well_known/iso8601.rs index f19181a92..756f034ef 100644 --- a/vendor/time/src/format_description/well_known/iso8601.rs +++ b/vendor/time/src/format_description/well_known/iso8601.rs @@ -4,23 +4,9 @@ mod adt_hack; use core::num::NonZeroU8; -pub use self::adt_hack::{DoNotRelyOnWhatThisIs, EncodedConfig}; - -/// A configuration for [`Iso8601`] that only parses values. -const PARSING_ONLY: EncodedConfig = Config { - formatted_components: FormattedComponents::None, - use_separators: false, - year_is_six_digits: false, - date_kind: DateKind::Calendar, - time_precision: TimePrecision::Hour { - decimal_digits: None, - }, - offset_precision: OffsetPrecision::Hour, -} -.encode(); - -/// The default configuration for [`Iso8601`]. -const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode(); +#[doc(hidden)] +pub use self::adt_hack::DoNotRelyOnWhatThisIs; +pub use self::adt_hack::EncodedConfig; /// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). /// @@ -43,7 +29,7 @@ const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode(); /// # Ok::<_, time::Error>(()) /// ``` #[derive(Clone, Copy, PartialEq, Eq)] -pub struct Iso8601<const CONFIG: EncodedConfig = DEFAULT_CONFIG>; +pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>; impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -53,7 +39,18 @@ impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> { } } -impl Iso8601<DEFAULT_CONFIG> { +/// Define associated constants for `Iso8601`. +macro_rules! define_assoc_consts { + ($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$( + const $const_name: EncodedConfig = $format.encode(); + impl Iso8601<$const_name> { + $(#[$doc])* + $vis const $const_name: Self = Self; + } + )*}; +} + +define_assoc_consts! { /// An [`Iso8601`] with the default configuration. /// /// The following is the default behavior: @@ -66,15 +63,29 @@ impl Iso8601<DEFAULT_CONFIG> { /// - The time has precision to the second and nine decimal digits. /// - The UTC offset has precision to the minute. /// - /// If you need different behavior, use [`Config::DEFAULT`] and [`Config`]'s methods to create - /// a custom configuration. - pub const DEFAULT: Self = Self; -} - -impl Iso8601<PARSING_ONLY> { + /// If you need different behavior, use another associated constant. For full customization, use + /// [`Config::DEFAULT`] and [`Config`]'s methods to create a custom configuration. + pub const DEFAULT = Config::DEFAULT; /// An [`Iso8601`] that can only be used for parsing. Using this to format a value is /// unspecified behavior. - pub const PARSING: Self = Self; + pub const PARSING = Config::PARSING; + /// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`]. + pub const DATE = Config::DEFAULT.set_formatted_components(FormattedComponents::Date); + /// An [`Iso8601`] that handles only the time, but is otherwise the same as [`Config::DEFAULT`]. + pub const TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::Time); + /// An [`Iso8601`] that handles only the UTC offset, but is otherwise the same as + /// [`Config::DEFAULT`]. + pub const OFFSET = Config::DEFAULT.set_formatted_components(FormattedComponents::Offset); + /// An [`Iso8601`] that handles the date and time, but is otherwise the same as + /// [`Config::DEFAULT`]. + pub const DATE_TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::DateTime); + /// An [`Iso8601`] that handles the date, time, and UTC offset. This is the same as + /// [`Config::DEFAULT`]. + pub const DATE_TIME_OFFSET = Config::DEFAULT; + /// An [`Iso8601`] that handles the time and UTC offset, but is otherwise the same as + /// [`Config::DEFAULT`]. + pub const TIME_OFFSET = Config::DEFAULT + .set_formatted_components(FormattedComponents::TimeOffset); } /// Which components to format. @@ -114,19 +125,19 @@ pub enum TimePrecision { /// Format the hour only. Minutes, seconds, and nanoseconds will be represented with the /// specified number of decimal digits, if any. Hour { - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] decimal_digits: Option<NonZeroU8>, }, /// Format the hour and minute. Seconds and nanoseconds will be represented with the specified /// number of decimal digits, if any. Minute { - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] decimal_digits: Option<NonZeroU8>, }, /// Format the hour, minute, and second. Nanoseconds will be represented with the specified /// number of decimal digits, if any. Second { - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] decimal_digits: Option<NonZeroU8>, }, } @@ -186,6 +197,19 @@ impl Config { offset_precision: OffsetPrecision::Minute, }; + /// A configuration that can only be used for parsing. Using this to format a value is + /// unspecified behavior. + const PARSING: Self = Self { + formatted_components: FormattedComponents::None, + use_separators: false, + year_is_six_digits: false, + date_kind: DateKind::Calendar, + time_precision: TimePrecision::Hour { + decimal_digits: None, + }, + offset_precision: OffsetPrecision::Hour, + }; + /// Set whether the format the date, time, and/or UTC offset. pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self { Self { diff --git a/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs index 1fdecaf26..12b1c725f 100644 --- a/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs +++ b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs @@ -49,11 +49,10 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { } impl Config { - /// Encode the configuration, permitting it to be used as a const parameter of - /// [`Iso8601`](super::Iso8601). + /// Encode the configuration, permitting it to be used as a const parameter of [`Iso8601`]. /// - /// The value returned by this method must only be used as a const parameter to - /// [`Iso8601`](super::Iso8601). Any other usage is unspecified behavior. + /// The value returned by this method must only be used as a const parameter to [`Iso8601`]. Any + /// other usage is unspecified behavior. pub const fn encode(&self) -> EncodedConfig { let mut bytes = [0; EncodedConfig::BITS as usize / 8]; diff --git a/vendor/time/src/formatting/formattable.rs b/vendor/time/src/formatting/formattable.rs index 2b4b35088..cad1d482d 100644 --- a/vendor/time/src/formatting/formattable.rs +++ b/vendor/time/src/formatting/formattable.rs @@ -59,7 +59,7 @@ mod sealed { } // region: custom formats -impl<'a> sealed::Sealed for FormatItem<'a> { +impl sealed::Sealed for FormatItem<'_> { fn format_into( &self, output: &mut impl io::Write, @@ -80,7 +80,7 @@ impl<'a> sealed::Sealed for FormatItem<'a> { } } -impl<'a> sealed::Sealed for [FormatItem<'a>] { +impl sealed::Sealed for [FormatItem<'_>] { fn format_into( &self, output: &mut impl io::Write, diff --git a/vendor/time/src/formatting/iso8601.rs b/vendor/time/src/formatting/iso8601.rs index 29d443ef4..67bc66b21 100644 --- a/vendor/time/src/formatting/iso8601.rs +++ b/vendor/time/src/formatting/iso8601.rs @@ -85,17 +85,17 @@ pub(super) fn format_time<const CONFIG: EncodedConfig>( match Iso8601::<CONFIG>::TIME_PRECISION { TimePrecision::Hour { decimal_digits } => { let hours = (hours as f64) - + (minutes as f64) / Minute.per(Hour) as f64 - + (seconds as f64) / Second.per(Hour) as f64 - + (nanoseconds as f64) / Nanosecond.per(Hour) as f64; + + (minutes as f64) / Minute::per(Hour) as f64 + + (seconds as f64) / Second::per(Hour) as f64 + + (nanoseconds as f64) / Nanosecond::per(Hour) as f64; format_float(output, hours, 2, decimal_digits)?; } TimePrecision::Minute { decimal_digits } => { bytes += format_number_pad_zero::<2>(output, hours)?; bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; let minutes = (minutes as f64) - + (seconds as f64) / Second.per(Minute) as f64 - + (nanoseconds as f64) / Nanosecond.per(Minute) as f64; + + (seconds as f64) / Second::per(Minute) as f64 + + (nanoseconds as f64) / Nanosecond::per(Minute) as f64; bytes += format_float(output, minutes, 2, decimal_digits)?; } TimePrecision::Second { decimal_digits } => { @@ -103,7 +103,7 @@ pub(super) fn format_time<const CONFIG: EncodedConfig>( bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; bytes += format_number_pad_zero::<2>(output, minutes)?; bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; - let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond.per(Second) as f64; + let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond::per(Second) as f64; bytes += format_float(output, seconds, 2, decimal_digits)?; } } diff --git a/vendor/time/src/formatting/mod.rs b/vendor/time/src/formatting/mod.rs index e5017063a..a22742236 100644 --- a/vendor/time/src/formatting/mod.rs +++ b/vendor/time/src/formatting/mod.rs @@ -47,75 +47,22 @@ pub(crate) trait DigitCount { /// The number of digits in the stringified value. fn num_digits(self) -> u8; } -impl DigitCount for u8 { - fn num_digits(self) -> u8 { - // Using a lookup table as with u32 is *not* faster in standalone benchmarks. - if self < 10 { - 1 - } else if self < 100 { - 2 - } else { - 3 - } - } -} -impl DigitCount for u16 { - fn num_digits(self) -> u8 { - // Using a lookup table as with u32 is *not* faster in standalone benchmarks. - if self < 10 { - 1 - } else if self < 100 { - 2 - } else if self < 1_000 { - 3 - } else if self < 10_000 { - 4 - } else { - 5 - } - } -} -impl DigitCount for u32 { - fn num_digits(self) -> u8 { - /// Lookup table - const TABLE: &[u64] = &[ - 0x0001_0000_0000, - 0x0001_0000_0000, - 0x0001_0000_0000, - 0x0001_FFFF_FFF6, - 0x0002_0000_0000, - 0x0002_0000_0000, - 0x0002_FFFF_FF9C, - 0x0003_0000_0000, - 0x0003_0000_0000, - 0x0003_FFFF_FC18, - 0x0004_0000_0000, - 0x0004_0000_0000, - 0x0004_0000_0000, - 0x0004_FFFF_D8F0, - 0x0005_0000_0000, - 0x0005_0000_0000, - 0x0005_FFFE_7960, - 0x0006_0000_0000, - 0x0006_0000_0000, - 0x0006_FFF0_BDC0, - 0x0007_0000_0000, - 0x0007_0000_0000, - 0x0007_0000_0000, - 0x0007_FF67_6980, - 0x0008_0000_0000, - 0x0008_0000_0000, - 0x0008_FA0A_1F00, - 0x0009_0000_0000, - 0x0009_0000_0000, - 0x0009_C465_3600, - 0x000A_0000_0000, - 0x000A_0000_0000, - ]; - ((self as u64 + TABLE[31_u32.saturating_sub(self.leading_zeros()) as usize]) >> 32) as _ - } +/// A macro to generate implementations of `DigitCount` for unsigned integers. +macro_rules! impl_digit_count { + ($($t:ty),* $(,)?) => { + $(impl DigitCount for $t { + fn num_digits(self) -> u8 { + match self.checked_ilog10() { + Some(n) => (n as u8) + 1, + None => 1, + } + } + })* + }; } + +impl_digit_count!(u8, u16, u32); // endregion extension trait /// Write all bytes to the output, returning the number of bytes written. @@ -250,7 +197,19 @@ pub(crate) fn format_component( (UnixTimestamp(modifier), Some(date), Some(time), Some(offset)) => { fmt_unix_timestamp(output, date, time, offset, modifier)? } - _ => return Err(error::Format::InsufficientTypeInformation), + (End(modifier::End {}), ..) => 0, + + // This is functionally the same as a wildcard arm, but it will cause an error if a new + // component is added. This is to avoid a bug where a new component, the code compiles, and + // formatting fails. + // Allow unreachable patterns because some branches may be fully matched above. + #[allow(unreachable_patterns)] + ( + Day(_) | Month(_) | Ordinal(_) | Weekday(_) | WeekNumber(_) | Year(_) | Hour(_) + | Minute(_) | Period(_) | Second(_) | Subsecond(_) | OffsetHour(_) | OffsetMinute(_) + | OffsetSecond(_) | Ignore(_) | UnixTimestamp(_) | End(_), + .., + ) => return Err(error::Format::InsufficientTypeInformation), }) } @@ -532,11 +491,13 @@ fn fmt_unix_timestamp( } modifier::UnixTimestampPrecision::Millisecond => format_number_pad_none( output, - (date_time.unix_timestamp_nanos() / Nanosecond.per(Millisecond) as i128).unsigned_abs(), + (date_time.unix_timestamp_nanos() / Nanosecond::per(Millisecond) as i128) + .unsigned_abs(), ), modifier::UnixTimestampPrecision::Microsecond => format_number_pad_none( output, - (date_time.unix_timestamp_nanos() / Nanosecond.per(Microsecond) as i128).unsigned_abs(), + (date_time.unix_timestamp_nanos() / Nanosecond::per(Microsecond) as i128) + .unsigned_abs(), ), modifier::UnixTimestampPrecision::Nanosecond => { format_number_pad_none(output, date_time.unix_timestamp_nanos().unsigned_abs()) diff --git a/vendor/time/src/instant.rs b/vendor/time/src/instant.rs index 58579b101..706c759c4 100644 --- a/vendor/time/src/instant.rs +++ b/vendor/time/src/instant.rs @@ -6,6 +6,7 @@ use core::ops::{Add, Sub}; use core::time::Duration as StdDuration; use std::time::Instant as StdInstant; +use crate::internal_macros::{impl_add_assign, impl_sub_assign}; use crate::Duration; /// A measurement of a monotonically non-decreasing clock. Opaque and useful only with [`Duration`]. diff --git a/vendor/time/src/internal_macros.rs b/vendor/time/src/internal_macros.rs new file mode 100644 index 000000000..857138983 --- /dev/null +++ b/vendor/time/src/internal_macros.rs @@ -0,0 +1,197 @@ +//! Macros for use within the library. They are not publicly available. + +/// Helper macro for easily implementing `OpAssign`. +macro_rules! __impl_assign { + ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$( + #[allow(unused_qualifications)] + $(#[$attr])* + impl core::ops::$op<$t> for $target { + fn $fn(&mut self, rhs: $t) { + *self = *self $sym rhs; + } + } + )+}; +} + +/// Implement `AddAssign` for the provided types. +macro_rules! impl_add_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + + AddAssign add_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `SubAssign` for the provided types. +macro_rules! impl_sub_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + - SubAssign sub_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `MulAssign` for the provided types. +macro_rules! impl_mul_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + * MulAssign mul_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `DivAssign` for the provided types. +macro_rules! impl_div_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + / DivAssign div_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Division of integers, rounding the resulting value towards negative infinity. +macro_rules! div_floor { + ($a:expr, $b:expr) => {{ + let _a = $a; + let _b = $b; + + let (_quotient, _remainder) = (_a / _b, _a % _b); + + if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) { + _quotient - 1 + } else { + _quotient + } + }}; +} + +/// Cascade an out-of-bounds value. +macro_rules! cascade { + (@ordinal ordinal) => {}; + (@year year) => {}; + + // Cascade an out-of-bounds value from "from" to "to". + ($from:ident in $min:literal.. $max:expr => $to:tt) => { + #[allow(unused_comparisons, unused_assignments)] + let min = $min; + let max = $max; + if $from >= max { + $from -= max - min; + $to += 1; + } else if $from < min { + $from += max - min; + $to -= 1; + } + }; + + // Special case the ordinal-to-year cascade, as it has different behavior. + ($ordinal:ident => $year:ident) => { + // We need to actually capture the idents. Without this, macro hygiene causes errors. + cascade!(@ordinal $ordinal); + cascade!(@year $year); + #[allow(unused_assignments)] + if $ordinal > crate::util::days_in_year($year) as i16 { + $ordinal -= crate::util::days_in_year($year) as i16; + $year += 1; + } else if $ordinal < 1 { + $year -= 1; + $ordinal += crate::util::days_in_year($year) as i16; + } + }; +} + +/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range. +macro_rules! ensure_ranged { + ($type:ident : $value:ident) => { + match $type::new($value) { + Some(val) => val, + None => { + #[allow(trivial_numeric_casts)] + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as _, + maximum: $type::MAX.get() as _, + value: $value as _, + conditional_range: false, + }); + } + } + }; + + ($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => { + match ($value $(as $as_type)?).checked_mul($factor) { + Some(val) => match $type::new(val) { + Some(val) => val, + None => { + #[allow(trivial_numeric_casts)] + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64 / $factor as i64, + maximum: $type::MAX.get() as i64 / $factor as i64, + value: $value as _, + conditional_range: false, + }); + } + }, + None => { + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64 / $factor as i64, + maximum: $type::MAX.get() as i64 / $factor as i64, + value: $value as _, + conditional_range: false, + }); + } + } + }; +} + +/// Try to unwrap an expression, returning if not possible. +/// +/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is +/// usable in `const` contexts. +macro_rules! const_try { + ($e:expr) => { + match $e { + Ok(value) => value, + Err(error) => return Err(error), + } + }; +} + +/// Try to unwrap an expression, returning if not possible. +/// +/// This is similar to the `?` operator, but is usable in `const` contexts. +macro_rules! const_try_opt { + ($e:expr) => { + match $e { + Some(value) => value, + None => return None, + } + }; +} + +/// Try to unwrap an expression, panicking if not possible. +/// +/// This is similar to `$e.expect($message)`, but is usable in `const` contexts. +macro_rules! expect_opt { + ($e:expr, $message:literal) => { + match $e { + Some(value) => value, + None => crate::expect_failed($message), + } + }; +} + +/// `unreachable!()`, but better. +macro_rules! bug { + () => { compile_error!("provide an error message to help fix a possible bug") }; + ($descr:literal $($rest:tt)?) => { + panic!(concat!("internal error: ", $descr) $($rest)?) + } +} + +pub(crate) use { + __impl_assign, bug, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, + impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, +}; diff --git a/vendor/time/src/lib.rs b/vendor/time/src/lib.rs index 2ab59cf8e..110c28648 100644 --- a/vendor/time/src/lib.rs +++ b/vendor/time/src/lib.rs @@ -76,55 +76,7 @@ #![doc(html_playground_url = "https://play.rust-lang.org")] #![cfg_attr(__time_03_docs, feature(doc_auto_cfg, doc_notable_trait))] -#![cfg_attr(coverage_nightly, feature(no_coverage))] #![cfg_attr(not(feature = "std"), no_std)] -#![deny( - anonymous_parameters, - clippy::all, - clippy::alloc_instead_of_core, - clippy::explicit_auto_deref, - clippy::obfuscated_if_else, - clippy::std_instead_of_core, - clippy::undocumented_unsafe_blocks, - illegal_floating_point_literal_pattern, - late_bound_lifetime_arguments, - path_statements, - patterns_in_fns_without_body, - rust_2018_idioms, - trivial_casts, - trivial_numeric_casts, - unreachable_pub, - unsafe_op_in_unsafe_fn, - unused_extern_crates, - rustdoc::broken_intra_doc_links, - rustdoc::private_intra_doc_links -)] -#![warn( - clippy::dbg_macro, - clippy::decimal_literal_representation, - clippy::get_unwrap, - clippy::missing_docs_in_private_items, - clippy::nursery, - clippy::print_stdout, - clippy::todo, - clippy::unimplemented, - clippy::uninlined_format_args, - clippy::unnested_or_patterns, - clippy::unwrap_in_result, - clippy::unwrap_used, - clippy::use_debug, - deprecated_in_future, - missing_copy_implementations, - missing_debug_implementations, - unused_qualifications, - variant_size_differences -)] -#![allow( - clippy::redundant_pub_crate, // suggests bad style - clippy::option_if_let_else, // suggests terrible code - clippy::unused_peekable, // temporary due to bug: remove when Rust 1.66 is released - clippy::std_instead_of_core, // temporary due to bug: remove when Rust 1.66 is released -)] #![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")] #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")] #![doc(test(attr(deny(warnings))))] @@ -133,185 +85,6 @@ #[cfg(feature = "alloc")] extern crate alloc; -// TODO(jhpratt) remove this after a while -#[cfg(unsound_local_offset)] -compile_error!( - "The `unsound_local_offset` flag was removed in time 0.3.18. If you need this functionality, \ - see the `time::util::local_offset::set_soundness` function." -); - -// region: macros -/// Helper macro for easily implementing `OpAssign`. -macro_rules! __impl_assign { - ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$( - #[allow(unused_qualifications)] - $(#[$attr])* - impl core::ops::$op<$t> for $target { - fn $fn(&mut self, rhs: $t) { - *self = *self $sym rhs; - } - } - )+}; -} - -/// Implement `AddAssign` for the provided types. -macro_rules! impl_add_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Implement `SubAssign` for the provided types. -macro_rules! impl_sub_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Implement `MulAssign` for the provided types. -macro_rules! impl_mul_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Implement `DivAssign` for the provided types. -macro_rules! impl_div_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Division of integers, rounding the resulting value towards negative infinity. -macro_rules! div_floor { - ($a:expr, $b:expr) => {{ - let _a = $a; - let _b = $b; - - let (_quotient, _remainder) = (_a / _b, _a % _b); - - if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) { - _quotient - 1 - } else { - _quotient - } - }}; -} - -/// Cascade an out-of-bounds value. -macro_rules! cascade { - (@ordinal ordinal) => {}; - (@year year) => {}; - - // Cascade an out-of-bounds value from "from" to "to". - ($from:ident in $min:literal.. $max:expr => $to:tt) => { - #[allow(unused_comparisons, unused_assignments)] - let min = $min; - let max = $max; - if $from >= max { - $from -= max - min; - $to += 1; - } else if $from < min { - $from += max - min; - $to -= 1; - } - }; - - // Special case the ordinal-to-year cascade, as it has different behavior. - ($ordinal:ident => $year:ident) => { - // We need to actually capture the idents. Without this, macro hygiene causes errors. - cascade!(@ordinal $ordinal); - cascade!(@year $year); - #[allow(unused_assignments)] - if $ordinal > crate::util::days_in_year($year) as i16 { - $ordinal -= crate::util::days_in_year($year) as i16; - $year += 1; - } else if $ordinal < 1 { - $year -= 1; - $ordinal += crate::util::days_in_year($year) as i16; - } - }; -} - -/// Returns `Err(error::ComponentRange)` if the value is not in range. -macro_rules! ensure_value_in_range { - ($value:ident in $start:expr => $end:expr) => {{ - let _start = $start; - let _end = $end; - #[allow(trivial_numeric_casts, unused_comparisons)] - if $value < _start || $value > _end { - return Err(crate::error::ComponentRange { - name: stringify!($value), - minimum: _start as _, - maximum: _end as _, - value: $value as _, - conditional_range: false, - }); - } - }}; - - ($value:ident conditionally in $start:expr => $end:expr) => {{ - let _start = $start; - let _end = $end; - #[allow(trivial_numeric_casts, unused_comparisons)] - if $value < _start || $value > _end { - return Err(crate::error::ComponentRange { - name: stringify!($value), - minimum: _start as _, - maximum: _end as _, - value: $value as _, - conditional_range: true, - }); - } - }}; -} - -/// Try to unwrap an expression, returning if not possible. -/// -/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is -/// usable in `const` contexts. -macro_rules! const_try { - ($e:expr) => { - match $e { - Ok(value) => value, - Err(error) => return Err(error), - } - }; -} - -/// Try to unwrap an expression, returning if not possible. -/// -/// This is similar to the `?` operator, but is usable in `const` contexts. -macro_rules! const_try_opt { - ($e:expr) => { - match $e { - Some(value) => value, - None => return None, - } - }; -} - -/// Try to unwrap an expression, panicking if not possible. -/// -/// This is similar to `$e.expect($message)`, but is usable in `const` contexts. -macro_rules! expect_opt { - ($e:expr, $message:literal) => { - match $e { - Some(value) => value, - None => crate::expect_failed($message), - } - }; -} - -/// `unreachable!()`, but better. -macro_rules! bug { - () => { compile_error!("provide an error message to help fix a possible bug") }; - ($descr:literal $($rest:tt)?) => { - panic!(concat!("internal error: ", $descr) $($rest)?) - } -} -// endregion macros - mod date; mod date_time; mod duration; @@ -323,6 +96,7 @@ pub mod format_description; pub mod formatting; #[cfg(feature = "std")] mod instant; +mod internal_macros; #[cfg(feature = "macros")] pub mod macros; mod month; @@ -345,8 +119,7 @@ mod utc_offset; pub mod util; mod weekday; -// Not public yet. -use time_core::convert; +pub use time_core::convert; pub use crate::date::Date; use crate::date_time::DateTime; diff --git a/vendor/time/src/month.rs b/vendor/time/src/month.rs index 0c657e982..ea3480f00 100644 --- a/vendor/time/src/month.rs +++ b/vendor/time/src/month.rs @@ -8,21 +8,32 @@ use self::Month::*; use crate::error; /// Months of the year. -#[allow(clippy::missing_docs_in_private_items)] // variants #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Month { + #[allow(missing_docs)] January = 1, + #[allow(missing_docs)] February = 2, + #[allow(missing_docs)] March = 3, + #[allow(missing_docs)] April = 4, + #[allow(missing_docs)] May = 5, + #[allow(missing_docs)] June = 6, + #[allow(missing_docs)] July = 7, + #[allow(missing_docs)] August = 8, + #[allow(missing_docs)] September = 9, + #[allow(missing_docs)] October = 10, + #[allow(missing_docs)] November = 11, + #[allow(missing_docs)] December = 12, } @@ -97,6 +108,60 @@ impl Month { December => January, } } + + /// Get n-th next month. + /// + /// ```rust + /// # use time::Month; + /// assert_eq!(Month::January.nth_next(4), Month::May); + /// assert_eq!(Month::July.nth_next(9), Month::April); + /// ``` + pub const fn nth_next(self, n: u8) -> Self { + match (self as u8 - 1 + n % 12) % 12 { + 0 => January, + 1 => February, + 2 => March, + 3 => April, + 4 => May, + 5 => June, + 6 => July, + 7 => August, + 8 => September, + 9 => October, + 10 => November, + val => { + debug_assert!(val == 11); + December + } + } + } + + /// Get n-th previous month. + /// + /// ```rust + /// # use time::Month; + /// assert_eq!(Month::January.nth_prev(4), Month::September); + /// assert_eq!(Month::July.nth_prev(9), Month::October); + /// ``` + pub const fn nth_prev(self, n: u8) -> Self { + match self as i8 - 1 - (n % 12) as i8 { + 1 | -11 => February, + 2 | -10 => March, + 3 | -9 => April, + 4 | -8 => May, + 5 | -7 => June, + 6 | -6 => July, + 7 | -5 => August, + 8 | -4 => September, + 9 | -3 => October, + 10 | -2 => November, + 11 | -1 => December, + val => { + debug_assert!(val == 0); + January + } + } + } } impl fmt::Display for Month { diff --git a/vendor/time/src/offset_date_time.rs b/vendor/time/src/offset_date_time.rs index d979174e1..e88097fe3 100644 --- a/vendor/time/src/offset_date_time.rs +++ b/vendor/time/src/offset_date_time.rs @@ -16,6 +16,7 @@ use std::time::SystemTime; use crate::date_time::offset_kind; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{const_try, const_try_opt}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::{error, Date, DateTime, Duration, Month, PrimitiveDateTime, Time, UtcOffset, Weekday}; diff --git a/vendor/time/src/parsing/component.rs b/vendor/time/src/parsing/component.rs index 23035893e..42d15e9f6 100644 --- a/vendor/time/src/parsing/component.rs +++ b/vendor/time/src/parsing/component.rs @@ -316,12 +316,12 @@ pub(crate) fn parse_unix_timestamp( let ParsedItem(input, sign) = opt(sign)(input); let ParsedItem(input, nano_timestamp) = match modifiers.precision { modifier::UnixTimestampPrecision::Second => { - n_to_m_digits::<1, 14, u128>(input)?.map(|val| val * Nanosecond.per(Second) as u128) + n_to_m_digits::<1, 14, u128>(input)?.map(|val| val * Nanosecond::per(Second) as u128) } modifier::UnixTimestampPrecision::Millisecond => n_to_m_digits::<1, 17, u128>(input)? - .map(|val| val * Nanosecond.per(Millisecond) as u128), + .map(|val| val * Nanosecond::per(Millisecond) as u128), modifier::UnixTimestampPrecision::Microsecond => n_to_m_digits::<1, 20, u128>(input)? - .map(|val| val * Nanosecond.per(Microsecond) as u128), + .map(|val| val * Nanosecond::per(Microsecond) as u128), modifier::UnixTimestampPrecision::Nanosecond => n_to_m_digits::<1, 23, _>(input)?, }; @@ -331,3 +331,15 @@ pub(crate) fn parse_unix_timestamp( _ => Some(ParsedItem(input, nano_timestamp as _)), } } + +/// Parse the `end` component, which represents the end of input. If any input is remaining, `None` +/// is returned. +pub(crate) const fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> { + let modifier::End {} = end; + + if input.is_empty() { + Some(ParsedItem(input, ())) + } else { + None + } +} diff --git a/vendor/time/src/parsing/iso8601.rs b/vendor/time/src/parsing/iso8601.rs index 1afee4e31..792c43280 100644 --- a/vendor/time/src/parsing/iso8601.rs +++ b/vendor/time/src/parsing/iso8601.rs @@ -125,16 +125,16 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { *parsed = parsed .with_hour_24(hour) .ok_or(InvalidComponent("hour"))? - .with_minute((fractional_part * Second.per(Minute) as f64) as _) + .with_minute((fractional_part * Second::per(Minute) as f64) as _) .ok_or(InvalidComponent("minute"))? .with_second( - (fractional_part * Second.per(Hour) as f64 % Minute.per(Hour) as f64) + (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64) as _, ) .ok_or(InvalidComponent("second"))? .with_subsecond( - (fractional_part * Nanosecond.per(Hour) as f64 - % Nanosecond.per(Second) as f64) as _, + (fractional_part * Nanosecond::per(Hour) as f64 + % Nanosecond::per(Second) as f64) as _, ) .ok_or(InvalidComponent("subsecond"))?; return Ok(input); @@ -162,11 +162,11 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { *parsed = parsed .with_minute(minute) .ok_or(InvalidComponent("minute"))? - .with_second((fractional_part * Second.per(Minute) as f64) as _) + .with_second((fractional_part * Second::per(Minute) as f64) as _) .ok_or(InvalidComponent("second"))? .with_subsecond( - (fractional_part * Nanosecond.per(Minute) as f64 - % Nanosecond.per(Second) as f64) as _, + (fractional_part * Nanosecond::per(Minute) as f64 + % Nanosecond::per(Second) as f64) as _, ) .ok_or(InvalidComponent("subsecond"))?; return Ok(input); @@ -209,7 +209,7 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { Some(ParsedItem(input, (second, Some(fractional_part)))) => ( input, second, - round(fractional_part * Nanosecond.per(Second) as f64) as _, + round(fractional_part * Nanosecond::per(Second) as f64) as _, ), None if extended_kind.is_extended() => { return Err(error::Parse::ParseFromDescription(InvalidComponent( @@ -271,17 +271,23 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { }; } - let input = min(input) - .and_then(|parsed_item| { - parsed_item.consume_value(|min| { - parsed.set_offset_minute_signed(if sign == b'-' { + match min(input) { + Some(ParsedItem(new_input, min)) => { + input = new_input; + parsed + .set_offset_minute_signed(if sign == b'-' { -(min as i8) } else { min as _ }) - }) - }) - .ok_or(InvalidComponent("offset minute"))?; + .ok_or(InvalidComponent("offset minute"))?; + } + None => { + // Omitted offset minute is assumed to be zero. + parsed.set_offset_minute_signed(0); + } + } + // If `:` was present, the format has already been set to extended. As such, this call // will do nothing in that case. If there wasn't `:` but minutes were // present, we know it's the basic format. Do not use `?` on the call, as diff --git a/vendor/time/src/parsing/parsable.rs b/vendor/time/src/parsing/parsable.rs index 78bbe64f0..30fda4db2 100644 --- a/vendor/time/src/parsing/parsable.rs +++ b/vendor/time/src/parsing/parsable.rs @@ -9,6 +9,7 @@ use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339}; use crate::format_description::FormatItem; #[cfg(feature = "alloc")] use crate::format_description::OwnedFormatItem; +use crate::internal_macros::bug; use crate::parsing::{Parsed, ParsedItem}; use crate::{error, Date, DateTime, Month, Time, UtcOffset, Weekday}; @@ -52,7 +53,9 @@ mod sealed { if self.parse_into(input, &mut parsed)?.is_empty() { Ok(parsed) } else { - Err(error::Parse::UnexpectedTrailingCharacters) + Err(error::Parse::ParseFromDescription( + error::ParseFromDescription::UnexpectedTrailingCharacters, + )) } } @@ -237,7 +240,7 @@ impl sealed::Sealed for Rfc2822 { }; // The RFC explicitly allows leap seconds. - parsed.set_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG, true); + parsed.leap_second_allowed = true; #[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522 let zone_literal = first_match( @@ -430,7 +433,9 @@ impl sealed::Sealed for Rfc2822 { }; if !input.is_empty() { - return Err(error::Parse::UnexpectedTrailingCharacters); + return Err(error::Parse::ParseFromDescription( + error::ParseFromDescription::UnexpectedTrailingCharacters, + )); } let mut nanosecond = 0; @@ -533,7 +538,7 @@ impl sealed::Sealed for Rfc3339 { }; // The RFC explicitly allows leap seconds. - parsed.set_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG, true); + parsed.leap_second_allowed = true; if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) { parsed @@ -662,7 +667,9 @@ impl sealed::Sealed for Rfc3339 { }; if !input.is_empty() { - return Err(error::Parse::UnexpectedTrailingCharacters); + return Err(error::Parse::ParseFromDescription( + error::ParseFromDescription::UnexpectedTrailingCharacters, + )); } // The RFC explicitly permits leap seconds. We don't currently support them, so treat it as diff --git a/vendor/time/src/parsing/parsed.rs b/vendor/time/src/parsing/parsed.rs index 04c74a720..f31087fe2 100644 --- a/vendor/time/src/parsing/parsed.rs +++ b/vendor/time/src/parsing/parsed.rs @@ -1,16 +1,22 @@ //! Information parsed from an input and format description. -use core::mem::MaybeUninit; use core::num::{NonZeroU16, NonZeroU8}; +use deranged::{ + OptionRangedI128, OptionRangedI32, OptionRangedI8, OptionRangedU16, OptionRangedU32, + OptionRangedU8, RangedI128, RangedI32, RangedI8, RangedU16, RangedU32, RangedU8, +}; + +use crate::convert::{Day, Hour, Minute, Nanosecond, Second}; +use crate::date::{MAX_YEAR, MIN_YEAR}; 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::format_description::{modifier, Component, FormatItem}; +use crate::internal_macros::const_try_opt; use crate::parsing::component::{ - parse_day, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour, + parse_day, parse_end, 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, }; @@ -99,10 +105,6 @@ impl sealed::AnyFormatItem for OwnedFormatItem { } } -/// 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. @@ -111,106 +113,97 @@ type Flag = u32; /// 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<i32>, + year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>, /// The last two digits of the calendar year. - year_last_two: MaybeUninit<u8>, + year_last_two: OptionRangedU8<0, 99>, /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date). - iso_year: MaybeUninit<i32>, + iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>, /// The last two digits of the ISO week year. - iso_year_last_two: MaybeUninit<u8>, + iso_year_last_two: OptionRangedU8<0, 99>, /// 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>, + sunday_week_number: OptionRangedU8<0, 53>, /// Week of the year, where week one begins on the first Monday of the calendar year. - monday_week_number: MaybeUninit<u8>, + monday_week_number: OptionRangedU8<0, 53>, /// Week of the year, where week one is the Monday-to-Sunday period containing January 4. - iso_week_number: Option<NonZeroU8>, + iso_week_number: OptionRangedU8<1, 53>, /// Day of the week. weekday: Option<Weekday>, /// Day of the year. - ordinal: Option<NonZeroU16>, + ordinal: OptionRangedU16<1, 366>, /// Day of the month. - day: Option<NonZeroU8>, + day: OptionRangedU8<1, 31>, /// Hour within the day. - hour_24: MaybeUninit<u8>, + hour_24: OptionRangedU8<0, { Hour::per(Day) - 1 }>, /// 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>, + hour_12: OptionRangedU8<1, 12>, /// Whether the `hour_12` field indicates a time that "PM". hour_12_is_pm: Option<bool>, /// Minute within the hour. - minute: MaybeUninit<u8>, + // minute: MaybeUninit<u8>, + minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>, /// Second within the minute. - second: MaybeUninit<u8>, + // do not subtract one, as leap seconds may be allowed + second: OptionRangedU8<0, { Second::per(Minute) }>, /// Nanosecond within the second. - subsecond: MaybeUninit<u32>, + subsecond: OptionRangedU32<0, { Nanosecond::per(Second) - 1 }>, /// Whole hours of the UTC offset. - offset_hour: MaybeUninit<i8>, + offset_hour: OptionRangedI8<-23, 23>, /// Minutes within the hour of the UTC offset. - offset_minute: MaybeUninit<i8>, + offset_minute: + OptionRangedI8<{ -((Minute::per(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as _ }>, /// Seconds within the minute of the UTC offset. - offset_second: MaybeUninit<i8>, + offset_second: + OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as _ }>, /// The Unix timestamp in nanoseconds. - unix_timestamp_nanos: MaybeUninit<i128>, -} - -#[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; + // unix_timestamp_nanos: MaybeUninit<i128>, + unix_timestamp_nanos: OptionRangedI128< + { Date::MIN.midnight().assume_utc().unix_timestamp_nanos() }, + { + Date::MAX + .with_time(Time::MAX) + .assume_utc() + .unix_timestamp_nanos() + }, + >, + /// 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". + offset_is_negative: Option<bool>, /// 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; + pub(super) leap_second_allowed: bool, } 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(), + year: OptionRangedI32::None, + year_last_two: OptionRangedU8::None, + iso_year: OptionRangedI32::None, + iso_year_last_two: OptionRangedU8::None, month: None, - sunday_week_number: MaybeUninit::uninit(), - monday_week_number: MaybeUninit::uninit(), - iso_week_number: None, + sunday_week_number: OptionRangedU8::None, + monday_week_number: OptionRangedU8::None, + iso_week_number: OptionRangedU8::None, weekday: None, - ordinal: None, - day: None, - hour_24: MaybeUninit::uninit(), - hour_12: None, + ordinal: OptionRangedU16::None, + day: OptionRangedU8::None, + hour_24: OptionRangedU8::None, + hour_12: OptionRangedU8::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(), + minute: OptionRangedU8::None, + second: OptionRangedU8::None, + subsecond: OptionRangedU32::None, + offset_hour: OptionRangedI8::None, + offset_minute: OptionRangedI8::None, + offset_second: OptionRangedI8::None, + unix_timestamp_nanos: OptionRangedI128::None, + offset_is_negative: None, + leap_second_allowed: false, } } @@ -283,11 +276,11 @@ impl Parsed { let ParsedItem(remaining, value) = parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?; match modifiers.repr { - WeekNumberRepr::Iso => { + modifier::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), + modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value), + modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value), } .ok_or(InvalidComponent("week number"))?; Ok(remaining) @@ -296,10 +289,10 @@ impl Parsed { 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 _), + (false, modifier::YearRepr::Full) => self.set_year(value), + (false, modifier::YearRepr::LastTwo) => self.set_year_last_two(value as _), + (true, modifier::YearRepr::Full) => self.set_iso_year(value), + (true, modifier::YearRepr::LastTwo) => self.set_iso_year_last_two(value as _), } .ok_or(InvalidComponent("year"))?; Ok(remaining) @@ -332,9 +325,9 @@ impl Parsed { 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) + self.set_offset_hour(value)?; + self.offset_is_negative = Some(is_negative); + Some(()) }) }) .ok_or(InvalidComponent("offset hour")), @@ -356,145 +349,152 @@ impl Parsed { parsed.consume_value(|value| self.set_unix_timestamp_nanos(value)) }) .ok_or(InvalidComponent("unix_timestamp")), + Component::End(modifiers) => parse_end(input, modifiers) + .map(ParsedItem::<()>::into_inner) + .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters), } } +} - /// Get the value of the provided flag. - const fn get_flag(&self, flag: Flag) -> bool { - self.flags & flag == flag +/// Getter methods +impl Parsed { + /// Obtain the `year` component. + pub const fn year(&self) -> Option<i32> { + self.year.get_primitive() } - /// 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; - } + /// Obtain the `year_last_two` component. + pub const fn year_last_two(&self) -> Option<u8> { + self.year_last_two.get_primitive() } -} -/// 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() }) - } - } - }; -} + /// Obtain the `iso_year` component. + pub const fn iso_year(&self) -> Option<i32> { + self.iso_year.get_primitive() + } -/// 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. + /// Obtain the `iso_year_last_two` component. + pub const fn iso_year_last_two(&self) -> Option<u8> { + self.iso_year_last_two.get_primitive() + } + + /// Obtain the `month` component. + pub const fn month(&self) -> Option<Month> { + self.month + } + + /// Obtain the `sunday_week_number` component. + pub const fn sunday_week_number(&self) -> Option<u8> { + self.sunday_week_number.get_primitive() + } + + /// Obtain the `monday_week_number` component. + pub const fn monday_week_number(&self) -> Option<u8> { + self.monday_week_number.get_primitive() + } + + /// Obtain the `iso_week_number` component. + pub const fn iso_week_number(&self) -> Option<NonZeroU8> { + NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive())) + } + + /// Obtain the `weekday` component. + pub const fn weekday(&self) -> Option<Weekday> { + self.weekday + } + + /// Obtain the `ordinal` component. + pub const fn ordinal(&self) -> Option<NonZeroU16> { + NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive())) + } + + /// Obtain the `day` component. + pub const fn day(&self) -> Option<NonZeroU8> { + NonZeroU8::new(const_try_opt!(self.day.get_primitive())) + } + + /// Obtain the `hour_24` component. + pub const fn hour_24(&self) -> Option<u8> { + self.hour_24.get_primitive() + } + + /// Obtain the `hour_12` component. + pub const fn hour_12(&self) -> Option<NonZeroU8> { + NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive())) + } + + /// Obtain the `hour_12_is_pm` component. + pub const fn hour_12_is_pm(&self) -> Option<bool> { + self.hour_12_is_pm + } + + /// Obtain the `minute` component. + pub const fn minute(&self) -> Option<u8> { + self.minute.get_primitive() + } + + /// Obtain the `second` component. + pub const fn second(&self) -> Option<u8> { + self.second.get_primitive() + } + + /// Obtain the `subsecond` component. + pub const fn subsecond(&self) -> Option<u32> { + self.subsecond.get_primitive() + } + + /// Obtain the `offset_hour` component. + pub const fn offset_hour(&self) -> Option<i8> { + self.offset_hour.get_primitive() + } + + /// Obtain the absolute value of the `offset_minute` component. #[doc(hidden)] #[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`. + /// Obtain the `offset_minute` component. pub const fn offset_minute_signed(&self) -> Option<i8> { - 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) - } + match (self.offset_minute.get_primitive(), self.offset_is_negative) { + (Some(offset_minute), Some(true)) => Some(-offset_minute), + (Some(offset_minute), _) => Some(offset_minute), + (None, _) => None, } } - /// Obtain the absolute value of the offset second. + /// Obtain the absolute value of the `offset_second` component. #[doc(hidden)] #[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`. + /// Obtain the `offset_second` component. pub const fn offset_second_signed(&self) -> Option<i8> { - 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) - } + match (self.offset_second.get_primitive(), self.offset_is_negative) { + (Some(offset_second), Some(true)) => Some(-offset_second), + (Some(offset_second), _) => Some(offset_second), + (None, _) => None, } } + + /// Obtain the `unix_timestamp_nanos` component. + pub const fn unix_timestamp_nanos(&self) -> Option<i128> { + self.unix_timestamp_nanos.get_primitive() + } } -/// Generate setters for each of the fields. -/// -/// This macro should only be used for fields where the value is not validated beyond its type. +/// Generate setters based on the builders. 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); + ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$( + #[doc = concat!("Set the `", stringify!($setter), "` component.")] + pub fn $setter(&mut self, value: $type) -> Option<()> { + *self = self.$builder(value)?; Some(()) } - }; + )*}; } /// Setter methods @@ -503,28 +503,30 @@ macro_rules! setters { /// 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. + year set_year with_year i32; + year_last_two set_year_last_two with_year_last_two u8; + iso_year set_iso_year with_iso_year i32; + iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8; + month set_month with_month Month; + sunday_week_number set_sunday_week_number with_sunday_week_number u8; + monday_week_number set_monday_week_number with_monday_week_number u8; + iso_week_number set_iso_week_number with_iso_week_number NonZeroU8; + weekday set_weekday with_weekday Weekday; + ordinal set_ordinal with_ordinal NonZeroU16; + day set_day with_day NonZeroU8; + hour_24 set_hour_24 with_hour_24 u8; + hour_12 set_hour_12 with_hour_12 NonZeroU8; + hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool; + minute set_minute with_minute u8; + second set_second with_second u8; + subsecond set_subsecond with_subsecond u32; + offset_hour set_offset_hour with_offset_hour i8; + offset_minute set_offset_minute_signed with_offset_minute_signed i8; + offset_second set_offset_second_signed with_offset_second_signed i8; + unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128; + } + + /// Set the `offset_minute` component. #[doc(hidden)] #[deprecated( since = "0.3.8", @@ -539,13 +541,6 @@ impl Parsed { } /// 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", @@ -558,37 +553,6 @@ impl Parsed { 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> { - 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 @@ -596,29 +560,115 @@ macro_rules! builders { /// 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, - @UNIX_TIMESTAMP_NANOS_FLAG with_unix_timestamp_nanos unix_timestamp_nanos: i128, - } - - /// Set the named component and return `self`. + /// Set the `year` component and return `self`. + pub const fn with_year(mut self, value: i32) -> Option<Self> { + self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value))); + Some(self) + } + + /// Set the `year_last_two` component and return `self`. + pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> { + self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `iso_year` component and return `self`. + pub const fn with_iso_year(mut self, value: i32) -> Option<Self> { + self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value))); + Some(self) + } + + /// Set the `iso_year_last_two` component and return `self`. + pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> { + self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `month` component and return `self`. + pub const fn with_month(mut self, value: Month) -> Option<Self> { + self.month = Some(value); + Some(self) + } + + /// Set the `sunday_week_number` component and return `self`. + pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> { + self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `monday_week_number` component and return `self`. + pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> { + self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `iso_week_number` component and return `self`. + pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> { + self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); + Some(self) + } + + /// Set the `weekday` component and return `self`. + pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> { + self.weekday = Some(value); + Some(self) + } + + /// Set the `ordinal` component and return `self`. + pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> { + self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get()))); + Some(self) + } + + /// Set the `day` component and return `self`. + pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> { + self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); + Some(self) + } + + /// Set the `hour_24` component and return `self`. + pub const fn with_hour_24(mut self, value: u8) -> Option<Self> { + self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `hour_12` component and return `self`. + pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> { + self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); + Some(self) + } + + /// Set the `hour_12_is_pm` component and return `self`. + pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> { + self.hour_12_is_pm = Some(value); + Some(self) + } + + /// Set the `minute` component and return `self`. + pub const fn with_minute(mut self, value: u8) -> Option<Self> { + self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `second` component and return `self`. + pub const fn with_second(mut self, value: u8) -> Option<Self> { + self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `subsecond` component and return `self`. + pub const fn with_subsecond(mut self, value: u32) -> Option<Self> { + self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value))); + Some(self) + } + + /// Set the `offset_hour` component and return `self`. + pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> { + self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); + Some(self) + } + + /// Set the `offset_minute` component and return `self`. #[doc(hidden)] #[deprecated( since = "0.3.8", @@ -634,12 +684,11 @@ impl Parsed { /// 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; + self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); Some(self) } - /// Set the named component and return `self`. + /// Set the `offset_minute` component and return `self`. #[doc(hidden)] #[deprecated( since = "0.3.8", @@ -655,8 +704,13 @@ impl Parsed { /// 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; + self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); + Some(self) + } + + /// Set the `unix_timestamp_nanos` component and return `self`. + pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> { + self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value))); Some(self) } } @@ -682,7 +736,8 @@ impl TryFrom<Parsed> for Date { /// 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() { + // Safety: `ordinal` is not zero. + match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() { Weekday::Monday => 7, Weekday::Tuesday => 1, Weekday::Wednesday => 2, @@ -733,6 +788,7 @@ impl TryFrom<Parsed> for Time { (_, 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() @@ -742,10 +798,17 @@ impl TryFrom<Parsed> for Time { { 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)?) + + // Reject combinations such as hour-second with minute omitted. + match (parsed.minute(), parsed.second(), parsed.subsecond()) { + (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?), + (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?), + (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?), + (Some(minute), Some(second), Some(subsecond)) => { + Ok(Self::from_hms_nano(hour, minute, second, subsecond)?) + } + _ => Err(InsufficientInformation), + } } } @@ -796,27 +859,31 @@ impl<O: MaybeOffset> TryFrom<Parsed> for DateTime<O> { if let Some(timestamp) = parsed.unix_timestamp_nanos() { let DateTime { date, time, offset } = DateTime::<offset_kind::Fixed>::from_unix_timestamp_nanos(timestamp)?; - return Ok(Self { + + let mut value = Self { date, time, offset: maybe_offset_from_offset::<O>(offset), - }); + }; + if let Some(subsecond) = parsed.subsecond() { + value = value.replace_nanosecond(subsecond)?; + } + return Ok(value); } } // 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 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 = Self { date: Date::try_from(parsed)?, diff --git a/vendor/time/src/primitive_date_time.rs b/vendor/time/src/primitive_date_time.rs index 9850f96d4..b985867d1 100644 --- a/vendor/time/src/primitive_date_time.rs +++ b/vendor/time/src/primitive_date_time.rs @@ -9,6 +9,7 @@ use std::io; use crate::date_time::offset_kind; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{const_try, const_try_opt}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::{error, Date, DateTime, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday}; diff --git a/vendor/time/src/quickcheck.rs b/vendor/time/src/quickcheck.rs index 4a788b517..02cc62282 100644 --- a/vendor/time/src/quickcheck.rs +++ b/vendor/time/src/quickcheck.rs @@ -2,7 +2,7 @@ //! //! This enables users to write tests such as this, and have test values provided automatically: //! -//! ``` +//! ```ignore //! # #![allow(dead_code)] //! use quickcheck::quickcheck; //! use time::Date; @@ -38,7 +38,6 @@ use alloc::boxed::Box; use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen}; -use crate::convert::*; use crate::date_time::{DateTime, MaybeOffset}; use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; @@ -73,25 +72,22 @@ impl Arbitrary for Date { impl Arbitrary for Duration { fn arbitrary(g: &mut Gen) -> Self { - Self::nanoseconds_i128(arbitrary_between!( - i128; - g, - Self::MIN.whole_nanoseconds(), - Self::MAX.whole_nanoseconds() - )) + Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g)) } fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { Box::new( - (self.subsec_nanoseconds(), self.whole_seconds()) + (self.subsec_nanoseconds_ranged(), self.whole_seconds()) .shrink() .map(|(mut nanoseconds, seconds)| { // Coerce the sign if necessary. - if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) { - nanoseconds *= -1; + if (seconds > 0 && nanoseconds.get() < 0) + || (seconds < 0 && nanoseconds.get() > 0) + { + nanoseconds = nanoseconds.neg(); } - Self::new_unchecked(seconds, nanoseconds) + Self::new_ranged_unchecked(seconds, nanoseconds) }), ) } @@ -99,20 +95,20 @@ impl Arbitrary for Duration { impl Arbitrary for Time { fn arbitrary(g: &mut Gen) -> Self { - Self::__from_hms_nanos_unchecked( - arbitrary_between!(u8; g, 0, Hour.per(Day) - 1), - arbitrary_between!(u8; g, 0, Minute.per(Hour) - 1), - arbitrary_between!(u8; g, 0, Second.per(Minute) - 1), - arbitrary_between!(u32; g, 0, Nanosecond.per(Second) - 1), + Self::from_hms_nanos_ranged( + <_>::arbitrary(g), + <_>::arbitrary(g), + <_>::arbitrary(g), + <_>::arbitrary(g), ) } fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { Box::new( - self.as_hms_nano() + self.as_hms_nano_ranged() .shrink() .map(|(hour, minute, second, nanosecond)| { - Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) + Self::from_hms_nanos_ranged(hour, minute, second, nanosecond) }), ) } @@ -130,20 +126,14 @@ impl Arbitrary for PrimitiveDateTime { impl Arbitrary for UtcOffset { fn arbitrary(g: &mut Gen) -> Self { - let seconds = - arbitrary_between!(i32; g, -(Second.per(Day) as i32 - 1), Second.per(Day) as i32 - 1); - Self::__from_hms_unchecked( - (seconds / Second.per(Hour) as i32) as _, - ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _, - (seconds % Second.per(Minute) as i32) as _, - ) + Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g)) } fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { Box::new( - self.as_hms().shrink().map(|(hours, minutes, seconds)| { - Self::__from_hms_unchecked(hours, minutes, seconds) - }), + self.as_hms_ranged() + .shrink() + .map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)), ) } } diff --git a/vendor/time/src/rand.rs b/vendor/time/src/rand.rs index e5181f9bd..eac6fe66d 100644 --- a/vendor/time/src/rand.rs +++ b/vendor/time/src/rand.rs @@ -3,17 +3,11 @@ use rand::distributions::{Distribution, Standard}; use rand::Rng; -use crate::convert::*; use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; impl Distribution<Time> for Standard { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Time { - Time::__from_hms_nanos_unchecked( - rng.gen_range(0..Hour.per(Day)), - rng.gen_range(0..Minute.per(Hour)), - rng.gen_range(0..Second.per(Minute)), - rng.gen_range(0..Nanosecond.per(Second)), - ) + Time::from_hms_nanos_ranged(rng.gen(), rng.gen(), rng.gen(), rng.gen()) } } @@ -27,12 +21,7 @@ impl Distribution<Date> for Standard { impl Distribution<UtcOffset> for Standard { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UtcOffset { - let seconds = rng.gen_range(-(Second.per(Day) as i32 - 1)..=(Second.per(Day) as i32 - 1)); - UtcOffset::__from_hms_unchecked( - (seconds / Second.per(Hour) as i32) as _, - ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _, - (seconds % Second.per(Minute) as i32) as _, - ) + UtcOffset::from_hms_ranged(rng.gen(), rng.gen(), rng.gen()) } } @@ -51,9 +40,7 @@ impl Distribution<OffsetDateTime> for Standard { impl Distribution<Duration> for Standard { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration { - Duration::nanoseconds_i128( - rng.gen_range(Duration::MIN.whole_nanoseconds()..=Duration::MAX.whole_nanoseconds()), - ) + Duration::new_ranged(rng.gen(), rng.gen()) } } diff --git a/vendor/time/src/serde/mod.rs b/vendor/time/src/serde/mod.rs index 844d7bdef..f94cf0445 100644 --- a/vendor/time/src/serde/mod.rs +++ b/vendor/time/src/serde/mod.rs @@ -403,11 +403,19 @@ impl<'a> Deserialize<'a> for Time { /// The format used when serializing and deserializing a human-readable `UtcOffset`. #[cfg(feature = "parsing")] const UTC_OFFSET_FORMAT: &[FormatItem<'_>] = &[ - FormatItem::Component(Component::OffsetHour(modifier::OffsetHour::default())), - FormatItem::Literal(b":"), - FormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())), - FormatItem::Literal(b":"), - FormatItem::Component(Component::OffsetSecond(modifier::OffsetSecond::default())), + FormatItem::Component(Component::OffsetHour({ + let mut m = modifier::OffsetHour::default(); + m.sign_is_mandatory = true; + m + })), + FormatItem::Optional(&FormatItem::Compound(&[ + FormatItem::Literal(b":"), + FormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())), + FormatItem::Optional(&FormatItem::Compound(&[ + FormatItem::Literal(b":"), + FormatItem::Component(Component::OffsetSecond(modifier::OffsetSecond::default())), + ])), + ])), ]; impl Serialize for UtcOffset { diff --git a/vendor/time/src/serde/visitor.rs b/vendor/time/src/serde/visitor.rs index e61989afd..3a4311ecb 100644 --- a/vendor/time/src/serde/visitor.rs +++ b/vendor/time/src/serde/visitor.rs @@ -167,14 +167,21 @@ impl<'a> de::Visitor<'a> for Visitor<UtcOffset> { fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<UtcOffset, A::Error> { let hours = item!(seq, "offset hours")?; - let minutes = item!(seq, "offset minutes")?; - let seconds = item!(seq, "offset seconds")?; + let mut minutes = 0; + let mut seconds = 0; + + if let Ok(Some(min)) = seq.next_element() { + minutes = min; + if let Ok(Some(sec)) = seq.next_element() { + seconds = sec; + } + }; UtcOffset::from_hms(hours, minutes, seconds).map_err(ComponentRange::into_de_error) } } -impl<'a> de::Visitor<'a> for Visitor<Weekday> { +impl de::Visitor<'_> for Visitor<Weekday> { type Value = Weekday; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -211,7 +218,7 @@ impl<'a> de::Visitor<'a> for Visitor<Weekday> { } } -impl<'a> de::Visitor<'a> for Visitor<Month> { +impl de::Visitor<'_> for Visitor<Month> { type Value = Month; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -262,7 +269,7 @@ impl<'a> de::Visitor<'a> for Visitor<Month> { macro_rules! well_known { ($article:literal, $name:literal, $($ty:tt)+) => { #[cfg(feature = "parsing")] - impl<'a> de::Visitor<'a> for Visitor<$($ty)+> { + impl de::Visitor<'_> for Visitor<$($ty)+> { type Value = OffsetDateTime; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/vendor/time/src/sys/local_offset_at/unix.rs b/vendor/time/src/sys/local_offset_at/unix.rs index f4a808932..eddd8e2a9 100644 --- a/vendor/time/src/sys/local_offset_at/unix.rs +++ b/vendor/time/src/sys/local_offset_at/unix.rs @@ -15,9 +15,6 @@ const OS_HAS_THREAD_SAFE_ENVIRONMENT: bool = match std::env::consts::OS.as_bytes // https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/getenv.c // https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/setenv.c | b"netbsd" - // https://github.com/apple-oss-distributions/Libc/blob/d526593760f0f79dfaeb8b96c3c8a42c791156ff/stdlib/FreeBSD/getenv.c - // https://github.com/apple-oss-distributions/Libc/blob/d526593760f0f79dfaeb8b96c3c8a42c791156ff/stdlib/FreeBSD/setenv.c - | b"macos" => true, _ => false, }; diff --git a/vendor/time/src/sys/local_offset_at/wasm_js.rs b/vendor/time/src/sys/local_offset_at/wasm_js.rs index dfbe063a2..a0b59ac86 100644 --- a/vendor/time/src/sys/local_offset_at/wasm_js.rs +++ b/vendor/time/src/sys/local_offset_at/wasm_js.rs @@ -7,7 +7,7 @@ pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { // The number of minutes returned by getTimezoneOffset() is positive if the local time zone // is behind UTC, and negative if the local time zone is ahead of UTC. For example, // for UTC+10, -600 will be returned. - let timezone_offset = (js_date.get_timezone_offset() as i32) * -(Minute.per(Hour) as i32); + let timezone_offset = (js_date.get_timezone_offset() as i32) * -(Minute::per(Hour) as i32); UtcOffset::from_whole_seconds(timezone_offset).ok() } diff --git a/vendor/time/src/sys/local_offset_at/windows.rs b/vendor/time/src/sys/local_offset_at/windows.rs index a4d5882d6..7fb41e02a 100644 --- a/vendor/time/src/sys/local_offset_at/windows.rs +++ b/vendor/time/src/sys/local_offset_at/windows.rs @@ -57,7 +57,7 @@ fn systemtime_to_filetime(systime: &SystemTime) -> Option<FileTime> { /// Convert a `FILETIME` to an `i64`, representing a number of seconds. fn filetime_to_secs(filetime: &FileTime) -> i64 { /// FILETIME represents 100-nanosecond intervals - const FT_TO_SECS: i64 = Nanosecond.per(Second) as i64 / 100; + const FT_TO_SECS: i64 = Nanosecond::per(Second) as i64 / 100; ((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) / FT_TO_SECS } diff --git a/vendor/time/src/tests.rs b/vendor/time/src/tests.rs index 030c473be..e637ba03d 100644 --- a/vendor/time/src/tests.rs +++ b/vendor/time/src/tests.rs @@ -72,14 +72,6 @@ fn digit_count() { } #[test] -fn default() { - assert_eq!( - duration::Padding::Optimize.clone(), - duration::Padding::default() - ); -} - -#[test] fn debug() { let _ = format!("{:?}", duration::Padding::Optimize); let _ = format!("{:?}", parsing::ParsedItem(b"", 0)); diff --git a/vendor/time/src/time.rs b/vendor/time/src/time.rs index 87b465bb9..74ab2a5ed 100644 --- a/vendor/time/src/time.rs +++ b/vendor/time/src/time.rs @@ -6,9 +6,12 @@ use core::time::Duration as StdDuration; #[cfg(feature = "formatting")] use std::io; +use deranged::{RangedU32, RangedU8}; + use crate::convert::*; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{cascade, ensure_ranged, impl_add_assign, impl_sub_assign}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::DateAdjustment; @@ -23,27 +26,118 @@ pub(crate) enum Padding { Optimize, } +/// The type of the `hour` field of `Time`. +type Hours = RangedU8<0, { Hour::per(Day) - 1 }>; +/// The type of the `minute` field of `Time`. +type Minutes = RangedU8<0, { Minute::per(Hour) - 1 }>; +/// The type of the `second` field of `Time`. +type Seconds = RangedU8<0, { Second::per(Minute) - 1 }>; +/// The type of the `nanosecond` field of `Time`. +type Nanoseconds = RangedU32<0, { Nanosecond::per(Second) - 1 }>; + /// The clock time within a given date. Nanosecond precision. /// /// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds /// (either positive or negative). /// /// When comparing two `Time`s, they are assumed to be in the same calendar date. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Copy, Eq)] +#[repr(C)] pub struct Time { + // The order of this struct's fields matter! + // Do not change them. + + // Little endian version + #[cfg(target_endian = "little")] #[allow(clippy::missing_docs_in_private_items)] - hour: u8, + nanosecond: Nanoseconds, + #[cfg(target_endian = "little")] #[allow(clippy::missing_docs_in_private_items)] - minute: u8, + second: Seconds, + #[cfg(target_endian = "little")] #[allow(clippy::missing_docs_in_private_items)] - second: u8, + minute: Minutes, + #[cfg(target_endian = "little")] #[allow(clippy::missing_docs_in_private_items)] - nanosecond: u32, + hour: Hours, + #[cfg(target_endian = "little")] #[allow(clippy::missing_docs_in_private_items)] padding: Padding, + + // Big endian version + #[cfg(target_endian = "big")] + #[allow(clippy::missing_docs_in_private_items)] + padding: Padding, + #[cfg(target_endian = "big")] + #[allow(clippy::missing_docs_in_private_items)] + hour: Hours, + #[cfg(target_endian = "big")] + #[allow(clippy::missing_docs_in_private_items)] + minute: Minutes, + #[cfg(target_endian = "big")] + #[allow(clippy::missing_docs_in_private_items)] + second: Seconds, + #[cfg(target_endian = "big")] + #[allow(clippy::missing_docs_in_private_items)] + nanosecond: Nanoseconds, +} + +impl core::hash::Hash for Time { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + self.as_u64().hash(state) + } +} + +impl PartialEq for Time { + fn eq(&self, other: &Self) -> bool { + self.as_u64().eq(&other.as_u64()) + } +} + +impl PartialOrd for Time { + fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { + self.as_u64().partial_cmp(&other.as_u64()) + } +} + +impl Ord for Time { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.as_u64().cmp(&other.as_u64()) + } } impl Time { + /// Provides an u64 based representation **of the correct endianness** + /// + /// This representation can be used to do comparisons equality testing or hashing. + const fn as_u64(self) -> u64 { + let nano_bytes = self.nanosecond.get().to_ne_bytes(); + + #[cfg(target_endian = "big")] + return u64::from_be_bytes([ + self.padding as u8, + self.hour.get(), + self.minute.get(), + self.second.get(), + nano_bytes[0], + nano_bytes[1], + nano_bytes[2], + nano_bytes[3], + ]); + + #[cfg(target_endian = "little")] + return u64::from_le_bytes([ + nano_bytes[0], + nano_bytes[1], + nano_bytes[2], + nano_bytes[3], + self.second.get(), + self.minute.get(), + self.hour.get(), + self.padding as u8, + ]); + } + /// Create a `Time` that is exactly midnight. /// /// ```rust @@ -51,38 +145,44 @@ impl Time { /// # use time_macros::time; /// assert_eq!(Time::MIDNIGHT, time!(0:00)); /// ``` - pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0); + pub const MIDNIGHT: Self = Self::MIN; /// The smallest value that can be represented by `Time`. /// /// `00:00:00.0` - pub(crate) const MIN: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0); + pub(crate) const MIN: Self = + Self::from_hms_nanos_ranged(Hours::MIN, Minutes::MIN, Seconds::MIN, Nanoseconds::MIN); /// The largest value that can be represented by `Time`. /// /// `23:59:59.999_999_999` - pub(crate) const MAX: Self = Self::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999); + pub(crate) const MAX: Self = + Self::from_hms_nanos_ranged(Hours::MAX, Minutes::MAX, Seconds::MAX, Nanoseconds::MAX); // region: constructors /// Create a `Time` from its components. + /// + /// # Safety + /// + /// - `hours` must be in the range `0..=23`. + /// - `minutes` must be in the range `0..=59`. + /// - `seconds` must be in the range `0..=59`. + /// - `nanoseconds` must be in the range `0..=999_999_999`. #[doc(hidden)] - pub const fn __from_hms_nanos_unchecked( + pub const unsafe fn __from_hms_nanos_unchecked( hour: u8, minute: u8, second: u8, nanosecond: u32, ) -> Self { - debug_assert!(hour < Hour.per(Day)); - debug_assert!(minute < Minute.per(Hour)); - debug_assert!(second < Second.per(Minute)); - debug_assert!(nanosecond < Nanosecond.per(Second)); - - Self { - hour, - minute, - second, - nanosecond, - padding: Padding::Optimize, + // Safety: The caller must uphold the safety invariants. + unsafe { + Self::from_hms_nanos_ranged( + Hours::new_unchecked(hour), + Minutes::new_unchecked(minute), + Seconds::new_unchecked(second), + Nanoseconds::new_unchecked(nanosecond), + ) } } @@ -100,10 +200,28 @@ impl Time { /// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second. /// ``` pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); - ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); - ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); - Ok(Self::__from_hms_nanos_unchecked(hour, minute, second, 0)) + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + Nanoseconds::MIN, + )) + } + + /// Create a `Time` from the hour, minute, second, and nanosecond. + pub(crate) const fn from_hms_nanos_ranged( + hour: Hours, + minute: Minutes, + second: Seconds, + nanosecond: Nanoseconds, + ) -> Self { + Self { + hour, + minute, + second, + nanosecond, + padding: Padding::Optimize, + } } /// Attempt to create a `Time` from the hour, minute, second, and millisecond. @@ -126,15 +244,11 @@ impl Time { second: u8, millisecond: u16, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); - ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); - ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); - ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1); - Ok(Self::__from_hms_nanos_unchecked( - hour, - minute, - second, - millisecond as u32 * Nanosecond.per(Millisecond), + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)), )) } @@ -158,15 +272,11 @@ impl Time { second: u8, microsecond: u32, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); - ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); - ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); - ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1); - Ok(Self::__from_hms_nanos_unchecked( - hour, - minute, - second, - microsecond * Nanosecond.per(Microsecond) as u32, + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32), )) } @@ -190,12 +300,11 @@ impl Time { second: u8, nanosecond: u32, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); - ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); - ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); - ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1); - Ok(Self::__from_hms_nanos_unchecked( - hour, minute, second, nanosecond, + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + ensure_ranged!(Nanoseconds: nanosecond), )) } // endregion constructors @@ -209,7 +318,7 @@ impl Time { /// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59)); /// ``` pub const fn as_hms(self) -> (u8, u8, u8) { - (self.hour, self.minute, self.second) + (self.hour.get(), self.minute.get(), self.second.get()) } /// Get the clock hour, minute, second, and millisecond. @@ -221,10 +330,10 @@ impl Time { /// ``` pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { ( - self.hour, - self.minute, - self.second, - (self.nanosecond / Nanosecond.per(Millisecond)) as u16, + self.hour.get(), + self.minute.get(), + self.second.get(), + (self.nanosecond.get() / Nanosecond::per(Millisecond)) as u16, ) } @@ -240,10 +349,10 @@ impl Time { /// ``` pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { ( - self.hour, - self.minute, - self.second, - self.nanosecond / Nanosecond.per(Microsecond) as u32, + self.hour.get(), + self.minute.get(), + self.second.get(), + self.nanosecond.get() / Nanosecond::per(Microsecond) as u32, ) } @@ -258,6 +367,17 @@ impl Time { /// ); /// ``` pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { + ( + self.hour.get(), + self.minute.get(), + self.second.get(), + self.nanosecond.get(), + ) + } + + /// Get the clock hour, minute, second, and nanosecond. + #[cfg(feature = "quickcheck")] + pub(crate) const fn as_hms_nano_ranged(self) -> (Hours, Minutes, Seconds, Nanoseconds) { (self.hour, self.minute, self.second, self.nanosecond) } @@ -271,7 +391,7 @@ impl Time { /// assert_eq!(time!(23:59:59).hour(), 23); /// ``` pub const fn hour(self) -> u8 { - self.hour + self.hour.get() } /// Get the minute within the hour. @@ -284,7 +404,7 @@ impl Time { /// assert_eq!(time!(23:59:59).minute(), 59); /// ``` pub const fn minute(self) -> u8 { - self.minute + self.minute.get() } /// Get the second within the minute. @@ -297,7 +417,7 @@ impl Time { /// assert_eq!(time!(23:59:59).second(), 59); /// ``` pub const fn second(self) -> u8 { - self.second + self.second.get() } /// Get the milliseconds within the second. @@ -310,7 +430,7 @@ impl Time { /// assert_eq!(time!(23:59:59.999).millisecond(), 999); /// ``` pub const fn millisecond(self) -> u16 { - (self.nanosecond / Nanosecond.per(Millisecond)) as _ + (self.nanosecond.get() / Nanosecond::per(Millisecond)) as _ } /// Get the microseconds within the second. @@ -323,7 +443,7 @@ impl Time { /// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999); /// ``` pub const fn microsecond(self) -> u32 { - self.nanosecond / Nanosecond.per(Microsecond) as u32 + self.nanosecond.get() / Nanosecond::per(Microsecond) as u32 } /// Get the nanoseconds within the second. @@ -336,7 +456,7 @@ impl Time { /// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999); /// ``` pub const fn nanosecond(self) -> u32 { - self.nanosecond + self.nanosecond.get() } // endregion getters @@ -344,116 +464,135 @@ impl Time { /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether /// the date is different. pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) { - let mut nanoseconds = self.nanosecond as i32 + duration.subsec_nanoseconds(); + let mut nanoseconds = self.nanosecond.get() as i32 + duration.subsec_nanoseconds(); let mut seconds = - self.second as i8 + (duration.whole_seconds() % Second.per(Minute) as i64) as i8; + self.second.get() as i8 + (duration.whole_seconds() % Second::per(Minute) as i64) as i8; let mut minutes = - self.minute as i8 + (duration.whole_minutes() % Minute.per(Hour) as i64) as i8; - let mut hours = self.hour as i8 + (duration.whole_hours() % Hour.per(Day) as i64) as i8; + self.minute.get() as i8 + (duration.whole_minutes() % Minute::per(Hour) as i64) as i8; + let mut hours = + self.hour.get() as i8 + (duration.whole_hours() % Hour::per(Day) as i64) as i8; let mut date_adjustment = DateAdjustment::None; - cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds); - cascade!(seconds in 0..Second.per(Minute) as _ => minutes); - cascade!(minutes in 0..Minute.per(Hour) as _ => hours); - if hours >= Hour.per(Day) as _ { - hours -= Hour.per(Day) as i8; + cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds); + cascade!(seconds in 0..Second::per(Minute) as _ => minutes); + cascade!(minutes in 0..Minute::per(Hour) as _ => hours); + if hours >= Hour::per(Day) as _ { + hours -= Hour::per(Day) as i8; date_adjustment = DateAdjustment::Next; } else if hours < 0 { - hours += Hour.per(Day) as i8; + hours += Hour::per(Day) as i8; date_adjustment = DateAdjustment::Previous; } ( date_adjustment, - Self::__from_hms_nanos_unchecked( - hours as _, - minutes as _, - seconds as _, - nanoseconds as _, - ), + // Safety: The cascades above ensure the values are in range. + unsafe { + Self::__from_hms_nanos_unchecked( + hours as _, + minutes as _, + seconds as _, + nanoseconds as _, + ) + }, ) } /// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning /// whether the date is different. pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) { - let mut nanoseconds = self.nanosecond as i32 - duration.subsec_nanoseconds(); + let mut nanoseconds = self.nanosecond.get() as i32 - duration.subsec_nanoseconds(); let mut seconds = - self.second as i8 - (duration.whole_seconds() % Second.per(Minute) as i64) as i8; + self.second.get() as i8 - (duration.whole_seconds() % Second::per(Minute) as i64) as i8; let mut minutes = - self.minute as i8 - (duration.whole_minutes() % Minute.per(Hour) as i64) as i8; - let mut hours = self.hour as i8 - (duration.whole_hours() % Hour.per(Day) as i64) as i8; + self.minute.get() as i8 - (duration.whole_minutes() % Minute::per(Hour) as i64) as i8; + let mut hours = + self.hour.get() as i8 - (duration.whole_hours() % Hour::per(Day) as i64) as i8; let mut date_adjustment = DateAdjustment::None; - cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds); - cascade!(seconds in 0..Second.per(Minute) as _ => minutes); - cascade!(minutes in 0..Minute.per(Hour) as _ => hours); - if hours >= Hour.per(Day) as _ { - hours -= Hour.per(Day) as i8; + cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds); + cascade!(seconds in 0..Second::per(Minute) as _ => minutes); + cascade!(minutes in 0..Minute::per(Hour) as _ => hours); + if hours >= Hour::per(Day) as _ { + hours -= Hour::per(Day) as i8; date_adjustment = DateAdjustment::Next; } else if hours < 0 { - hours += Hour.per(Day) as i8; + hours += Hour::per(Day) as i8; date_adjustment = DateAdjustment::Previous; } ( date_adjustment, - Self::__from_hms_nanos_unchecked( - hours as _, - minutes as _, - seconds as _, - nanoseconds as _, - ), + // Safety: The cascades above ensure the values are in range. + unsafe { + Self::__from_hms_nanos_unchecked( + hours as _, + minutes as _, + seconds as _, + nanoseconds as _, + ) + }, ) } /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, /// returning whether the date is the previous date as the first element of the tuple. pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) { - let mut nanosecond = self.nanosecond + duration.subsec_nanos(); - let mut second = self.second + (duration.as_secs() % Second.per(Minute) as u64) as u8; - let mut minute = self.minute - + ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as u8; - let mut hour = self.hour - + ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as u8; + let mut nanosecond = self.nanosecond.get() + duration.subsec_nanos(); + let mut second = + self.second.get() + (duration.as_secs() % Second::per(Minute) as u64) as u8; + let mut minute = self.minute.get() + + ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as u8; + let mut hour = self.hour.get() + + ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as u8; let mut is_next_day = false; - cascade!(nanosecond in 0..Nanosecond.per(Second) => second); - cascade!(second in 0..Second.per(Minute) => minute); - cascade!(minute in 0..Minute.per(Hour) => hour); - if hour >= Hour.per(Day) { - hour -= Hour.per(Day); + cascade!(nanosecond in 0..Nanosecond::per(Second) => second); + cascade!(second in 0..Second::per(Minute) => minute); + cascade!(minute in 0..Minute::per(Hour) => hour); + if hour >= Hour::per(Day) { + hour -= Hour::per(Day); is_next_day = true; } ( is_next_day, - Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond), + // Safety: The cascades above ensure the values are in range. + unsafe { Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) }, ) } /// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, /// returning whether the date is the previous date as the first element of the tuple. pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) { - let mut nanosecond = self.nanosecond as i32 - duration.subsec_nanos() as i32; - let mut second = self.second as i8 - (duration.as_secs() % Second.per(Minute) as u64) as i8; - let mut minute = self.minute as i8 - - ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as i8; - let mut hour = self.hour as i8 - - ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as i8; + let mut nanosecond = self.nanosecond.get() as i32 - duration.subsec_nanos() as i32; + let mut second = + self.second.get() as i8 - (duration.as_secs() % Second::per(Minute) as u64) as i8; + let mut minute = self.minute.get() as i8 + - ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as i8; + let mut hour = self.hour.get() as i8 + - ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as i8; let mut is_previous_day = false; - cascade!(nanosecond in 0..Nanosecond.per(Second) as _ => second); - cascade!(second in 0..Second.per(Minute) as _ => minute); - cascade!(minute in 0..Minute.per(Hour) as _ => hour); + cascade!(nanosecond in 0..Nanosecond::per(Second) as _ => second); + cascade!(second in 0..Second::per(Minute) as _ => minute); + cascade!(minute in 0..Minute::per(Hour) as _ => hour); if hour < 0 { - hour += Hour.per(Day) as i8; + hour += Hour::per(Day) as i8; is_previous_day = true; } ( is_previous_day, - Self::__from_hms_nanos_unchecked(hour as _, minute as _, second as _, nanosecond as _), + // Safety: The cascades above ensure the values are in range. + unsafe { + Self::__from_hms_nanos_unchecked( + hour as _, + minute as _, + second as _, + nanosecond as _, + ) + }, ) } // endregion arithmetic helpers @@ -470,14 +609,9 @@ impl Time { /// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour /// ``` #[must_use = "This method does not mutate the original `Time`."] - pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); - Ok(Self::__from_hms_nanos_unchecked( - hour, - self.minute, - self.second, - self.nanosecond, - )) + pub const fn replace_hour(mut self, hour: u8) -> Result<Self, error::ComponentRange> { + self.hour = ensure_ranged!(Hours: hour); + Ok(self) } /// Replace the minutes within the hour. @@ -491,14 +625,9 @@ impl Time { /// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute /// ``` #[must_use = "This method does not mutate the original `Time`."] - pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); - Ok(Self::__from_hms_nanos_unchecked( - self.hour, - minute, - self.second, - self.nanosecond, - )) + pub const fn replace_minute(mut self, minute: u8) -> Result<Self, error::ComponentRange> { + self.minute = ensure_ranged!(Minutes: minute); + Ok(self) } /// Replace the seconds within the minute. @@ -512,14 +641,9 @@ impl Time { /// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second /// ``` #[must_use = "This method does not mutate the original `Time`."] - pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); - Ok(Self::__from_hms_nanos_unchecked( - self.hour, - self.minute, - second, - self.nanosecond, - )) + pub const fn replace_second(mut self, second: u8) -> Result<Self, error::ComponentRange> { + self.second = ensure_ranged!(Seconds: second); + Ok(self) } /// Replace the milliseconds within the second. @@ -534,16 +658,12 @@ impl Time { /// ``` #[must_use = "This method does not mutate the original `Time`."] pub const fn replace_millisecond( - self, + mut self, millisecond: u16, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1); - Ok(Self::__from_hms_nanos_unchecked( - self.hour, - self.minute, - self.second, - millisecond as u32 * Nanosecond.per(Millisecond), - )) + self.nanosecond = + ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)); + Ok(self) } /// Replace the microseconds within the second. @@ -558,16 +678,12 @@ impl Time { /// ``` #[must_use = "This method does not mutate the original `Time`."] pub const fn replace_microsecond( - self, + mut self, microsecond: u32, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1); - Ok(Self::__from_hms_nanos_unchecked( - self.hour, - self.minute, - self.second, - microsecond * Nanosecond.per(Microsecond) as u32, - )) + self.nanosecond = + ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32); + Ok(self) } /// Replace the nanoseconds within the second. @@ -581,14 +697,12 @@ impl Time { /// assert!(time!(01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond /// ``` #[must_use = "This method does not mutate the original `Time`."] - pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1); - Ok(Self::__from_hms_nanos_unchecked( - self.hour, - self.minute, - self.second, - nanosecond, - )) + pub const fn replace_nanosecond( + mut self, + nanosecond: u32, + ) -> Result<Self, error::ComponentRange> { + self.nanosecond = ensure_ranged!(Nanoseconds: nanosecond); + Ok(self) } // endregion replacement } @@ -754,24 +868,31 @@ impl Sub for Time { /// assert_eq!(time!(0:00) - time!(23:00), (-23).hours()); /// ``` fn sub(self, rhs: Self) -> Self::Output { - let hour_diff = (self.hour as i8) - (rhs.hour as i8); - let minute_diff = (self.minute as i8) - (rhs.minute as i8); - let second_diff = (self.second as i8) - (rhs.second as i8); - let nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32); + let hour_diff = (self.hour.get() as i8) - (rhs.hour.get() as i8); + let minute_diff = (self.minute.get() as i8) - (rhs.minute.get() as i8); + let second_diff = (self.second.get() as i8) - (rhs.second.get() as i8); + let nanosecond_diff = (self.nanosecond.get() as i32) - (rhs.nanosecond.get() as i32); - let seconds = hour_diff as i64 * Second.per(Hour) as i64 - + minute_diff as i64 * Second.per(Minute) as i64 + let seconds = hour_diff as i64 * Second::per(Hour) as i64 + + minute_diff as i64 * Second::per(Minute) as i64 + second_diff as i64; let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 { - (seconds - 1, nanosecond_diff + Nanosecond.per(Second) as i32) + ( + seconds - 1, + nanosecond_diff + Nanosecond::per(Second) as i32, + ) } else if seconds < 0 && nanosecond_diff > 0 { - (seconds + 1, nanosecond_diff - Nanosecond.per(Second) as i32) + ( + seconds + 1, + nanosecond_diff - Nanosecond::per(Second) as i32, + ) } else { (seconds, nanosecond_diff) }; - Duration::new_unchecked(seconds, nanoseconds) + // Safety: `nanoseconds` is in range due to the overflow handling. + unsafe { Duration::new_unchecked(seconds, nanoseconds) } } } // endregion trait impls diff --git a/vendor/time/src/utc_offset.rs b/vendor/time/src/utc_offset.rs index af7fd1bf7..86a937d7a 100644 --- a/vendor/time/src/utc_offset.rs +++ b/vendor/time/src/utc_offset.rs @@ -5,10 +5,13 @@ use core::ops::Neg; #[cfg(feature = "formatting")] use std::io; +use deranged::{RangedI32, RangedI8}; + use crate::convert::*; use crate::error; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::ensure_ranged; #[cfg(feature = "parsing")] use crate::parsing::Parsable; #[cfg(feature = "local-offset")] @@ -16,6 +19,13 @@ use crate::sys::local_offset_at; #[cfg(feature = "local-offset")] use crate::OffsetDateTime; +/// The type of the `hours` field of `UtcOffset`. +type Hours = RangedI8<{ -(Hour::per(Day) as i8 - 1) }, { Hour::per(Day) as i8 - 1 }>; +/// The type of the `minutes` field of `UtcOffset`. +type Minutes = RangedI8<{ -(Minute::per(Hour) as i8 - 1) }, { Minute::per(Hour) as i8 - 1 }>; +/// The type of the `seconds` field of `UtcOffset`. +type Seconds = RangedI8<{ -(Second::per(Minute) as i8 - 1) }, { Second::per(Minute) as i8 - 1 }>; + /// An offset from UTC. /// /// This struct can store values up to ±23:59:59. If you need support outside this range, please @@ -24,11 +34,11 @@ use crate::OffsetDateTime; #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct UtcOffset { #[allow(clippy::missing_docs_in_private_items)] - hours: i8, + hours: Hours, #[allow(clippy::missing_docs_in_private_items)] - minutes: i8, + minutes: Minutes, #[allow(clippy::missing_docs_in_private_items)] - seconds: i8, + seconds: Seconds, } impl UtcOffset { @@ -39,34 +49,32 @@ impl UtcOffset { /// # use time_macros::offset; /// assert_eq!(UtcOffset::UTC, offset!(UTC)); /// ``` - pub const UTC: Self = Self::__from_hms_unchecked(0, 0, 0); + #[allow(clippy::undocumented_unsafe_blocks)] // rust-lang/rust-clippy#11246 + // Safety: All values are in range. + pub const UTC: Self = unsafe { Self::__from_hms_unchecked(0, 0, 0) }; // region: constructors /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided, the /// validity of which must be guaranteed by the caller. All three parameters must have the same /// sign. + /// + /// # Safety + /// + /// - Hours must be in the range `-23..=23`. + /// - Minutes must be in the range `-59..=59`. + /// - Seconds must be in the range `-59..=59`. + /// + /// While the signs of the parameters are required to match to avoid bugs, this is not a safety + /// invariant. #[doc(hidden)] - pub const fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self { - if hours < 0 { - debug_assert!(minutes <= 0); - debug_assert!(seconds <= 0); - } else if hours > 0 { - debug_assert!(minutes >= 0); - debug_assert!(seconds >= 0); - } - if minutes < 0 { - debug_assert!(seconds <= 0); - } else if minutes > 0 { - debug_assert!(seconds >= 0); - } - debug_assert!(hours.unsigned_abs() < 24); - debug_assert!(minutes.unsigned_abs() < Minute.per(Hour)); - debug_assert!(seconds.unsigned_abs() < Second.per(Minute)); - - Self { - hours, - minutes, - seconds, + pub const unsafe fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self { + // Safety: The caller must uphold the safety invariants. + unsafe { + Self::from_hms_ranged_unchecked( + Hours::new_unchecked(hours), + Minutes::new_unchecked(minutes), + Seconds::new_unchecked(seconds), + ) } } @@ -84,29 +92,71 @@ impl UtcOffset { /// ``` pub const fn from_hms( hours: i8, - mut minutes: i8, - mut seconds: i8, + minutes: i8, + seconds: i8, ) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!(hours in -23 => 23); - ensure_value_in_range!( - minutes in -(Minute.per(Hour) as i8 - 1) => Minute.per(Hour) as i8 - 1 - ); - ensure_value_in_range!( - seconds in -(Second.per(Minute) as i8 - 1) => Second.per(Minute) as i8 - 1 - ); - - if (hours > 0 && minutes < 0) || (hours < 0 && minutes > 0) { - minutes *= -1; + Ok(Self::from_hms_ranged( + ensure_ranged!(Hours: hours), + ensure_ranged!(Minutes: minutes), + ensure_ranged!(Seconds: seconds), + )) + } + + /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided. All + /// three parameters must have the same sign. + /// + /// While the signs of the parameters are required to match, this is not a safety invariant. + pub(crate) const fn from_hms_ranged_unchecked( + hours: Hours, + minutes: Minutes, + seconds: Seconds, + ) -> Self { + if hours.get() < 0 { + debug_assert!(minutes.get() <= 0); + debug_assert!(seconds.get() <= 0); + } else if hours.get() > 0 { + debug_assert!(minutes.get() >= 0); + debug_assert!(seconds.get() >= 0); + } + if minutes.get() < 0 { + debug_assert!(seconds.get() <= 0); + } else if minutes.get() > 0 { + debug_assert!(seconds.get() >= 0); + } + + Self { + hours, + minutes, + seconds, } - if (hours > 0 && seconds < 0) - || (hours < 0 && seconds > 0) - || (minutes > 0 && seconds < 0) - || (minutes < 0 && seconds > 0) + } + + /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds + /// provided. + /// + /// The sign of all three components should match. If they do not, all smaller components will + /// have their signs flipped. + pub(crate) const fn from_hms_ranged( + hours: Hours, + mut minutes: Minutes, + mut seconds: Seconds, + ) -> Self { + if (hours.get() > 0 && minutes.get() < 0) || (hours.get() < 0 && minutes.get() > 0) { + minutes = minutes.neg(); + } + if (hours.get() > 0 && seconds.get() < 0) + || (hours.get() < 0 && seconds.get() > 0) + || (minutes.get() > 0 && seconds.get() < 0) + || (minutes.get() < 0 && seconds.get() > 0) { - seconds *= -1; + seconds = seconds.neg(); } - Ok(Self::__from_hms_unchecked(hours, minutes, seconds)) + Self { + hours, + minutes, + seconds, + } } /// Create a `UtcOffset` representing an offset by the number of seconds provided. @@ -117,15 +167,28 @@ impl UtcOffset { /// # Ok::<_, time::Error>(()) /// ``` pub const fn from_whole_seconds(seconds: i32) -> Result<Self, error::ComponentRange> { - ensure_value_in_range!( - seconds in -24 * Second.per(Hour) as i32 - 1 => 24 * Second.per(Hour) as i32 - 1 - ); - - Ok(Self::__from_hms_unchecked( - (seconds / Second.per(Hour) as i32) as _, - ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _, - (seconds % Second.per(Minute) as i32) as _, - )) + type WholeSeconds = RangedI32< + { + Hours::MIN.get() as i32 * Second::per(Hour) as i32 + + Minutes::MIN.get() as i32 * Second::per(Minute) as i32 + + Seconds::MIN.get() as i32 + }, + { + Hours::MAX.get() as i32 * Second::per(Hour) as i32 + + Minutes::MAX.get() as i32 * Second::per(Minute) as i32 + + Seconds::MAX.get() as i32 + }, + >; + ensure_ranged!(WholeSeconds: seconds); + + // Safety: The value was checked to be in range. + Ok(unsafe { + Self::__from_hms_unchecked( + (seconds / Second::per(Hour) as i32) as _, + ((seconds % Second::per(Hour) as i32) / Minute::per(Hour) as i32) as _, + (seconds % Second::per(Minute) as i32) as _, + ) + }) } // endregion constructors @@ -139,6 +202,13 @@ impl UtcOffset { /// assert_eq!(offset!(-1:02:03).as_hms(), (-1, -2, -3)); /// ``` pub const fn as_hms(self) -> (i8, i8, i8) { + (self.hours.get(), self.minutes.get(), self.seconds.get()) + } + + /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components + /// will always match. A positive value indicates an offset to the east; a negative to the west. + #[cfg(feature = "quickcheck")] + pub(crate) const fn as_hms_ranged(self) -> (Hours, Minutes, Seconds) { (self.hours, self.minutes, self.seconds) } @@ -151,7 +221,7 @@ impl UtcOffset { /// assert_eq!(offset!(-1:02:03).whole_hours(), -1); /// ``` pub const fn whole_hours(self) -> i8 { - self.hours + self.hours.get() } /// Obtain the number of whole minutes the offset is from UTC. A positive value indicates an @@ -163,7 +233,7 @@ impl UtcOffset { /// assert_eq!(offset!(-1:02:03).whole_minutes(), -62); /// ``` pub const fn whole_minutes(self) -> i16 { - self.hours as i16 * Minute.per(Hour) as i16 + self.minutes as i16 + self.hours.get() as i16 * Minute::per(Hour) as i16 + self.minutes.get() as i16 } /// Obtain the number of minutes past the hour the offset is from UTC. A positive value @@ -175,7 +245,7 @@ impl UtcOffset { /// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2); /// ``` pub const fn minutes_past_hour(self) -> i8 { - self.minutes + self.minutes.get() } /// Obtain the number of whole seconds the offset is from UTC. A positive value indicates an @@ -189,9 +259,9 @@ impl UtcOffset { // This may be useful for anyone manually implementing arithmetic, as it // would let them construct a `Duration` directly. pub const fn whole_seconds(self) -> i32 { - self.hours as i32 * Second.per(Hour) as i32 - + self.minutes as i32 * Second.per(Minute) as i32 - + self.seconds as i32 + self.hours.get() as i32 * Second::per(Hour) as i32 + + self.minutes.get() as i32 * Second::per(Minute) as i32 + + self.seconds.get() as i32 } /// Obtain the number of seconds past the minute the offset is from UTC. A positive value @@ -203,7 +273,7 @@ impl UtcOffset { /// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3); /// ``` pub const fn seconds_past_minute(self) -> i8 { - self.seconds + self.seconds.get() } // endregion getters @@ -218,7 +288,7 @@ impl UtcOffset { /// assert!(offset!(UTC).is_utc()); /// ``` pub const fn is_utc(self) -> bool { - self.hours == 0 && self.minutes == 0 && self.seconds == 0 + self.hours.get() == 0 && self.minutes.get() == 0 && self.seconds.get() == 0 } /// Check if the offset is positive, or east of UTC. @@ -230,7 +300,7 @@ impl UtcOffset { /// assert!(!offset!(UTC).is_positive()); /// ``` pub const fn is_positive(self) -> bool { - self.hours > 0 || self.minutes > 0 || self.seconds > 0 + self.hours.get() > 0 || self.minutes.get() > 0 || self.seconds.get() > 0 } /// Check if the offset is negative, or west of UTC. @@ -242,7 +312,7 @@ impl UtcOffset { /// assert!(!offset!(UTC).is_negative()); /// ``` pub const fn is_negative(self) -> bool { - self.hours < 0 || self.minutes < 0 || self.seconds < 0 + self.hours.get() < 0 || self.minutes.get() < 0 || self.seconds.get() < 0 } // endregion is_{sign} @@ -334,7 +404,7 @@ impl fmt::Display for UtcOffset { if self.is_negative() { '-' } else { '+' }, self.hours.abs(), self.minutes.abs(), - self.seconds.abs() + self.seconds.abs(), ) } } @@ -350,6 +420,6 @@ impl Neg for UtcOffset { type Output = Self; fn neg(self) -> Self::Output { - Self::__from_hms_unchecked(-self.hours, -self.minutes, -self.seconds) + Self::from_hms_ranged(self.hours.neg(), self.minutes.neg(), self.seconds.neg()) } } diff --git a/vendor/time/src/weekday.rs b/vendor/time/src/weekday.rs index d530a2e4d..07642498d 100644 --- a/vendor/time/src/weekday.rs +++ b/vendor/time/src/weekday.rs @@ -13,19 +13,19 @@ use crate::error; /// Friday), this type does not implement `PartialOrd` or `Ord`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Weekday { - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Monday, - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Tuesday, - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Wednesday, - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Thursday, - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Friday, - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Saturday, - #[allow(clippy::missing_docs_in_private_items)] + #[allow(missing_docs)] Sunday, } @@ -88,6 +88,28 @@ impl Weekday { } } + /// Get n-th previous day. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.nth_prev(1), Weekday::Sunday); + /// assert_eq!(Weekday::Sunday.nth_prev(10), Weekday::Thursday); + /// ``` + pub const fn nth_prev(self, n: u8) -> Self { + match self.number_days_from_monday() as i8 - (n % 7) as i8 { + 1 | -6 => Tuesday, + 2 | -5 => Wednesday, + 3 | -4 => Thursday, + 4 | -3 => Friday, + 5 | -2 => Saturday, + 6 | -1 => Sunday, + val => { + debug_assert!(val == 0); + Monday + } + } + } + /// Get the one-indexed number of days from Monday. /// /// ```rust |