From 8cdfa445d6629ffef4cb84967ff7017654045bc2 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 2 Jul 2025 18:36:06 -0600 Subject: chore: add vendor directory --- vendor/time/src/date.rs | 1490 ++++++++++++++++++ vendor/time/src/duration.rs | 1598 ++++++++++++++++++++ vendor/time/src/error/component_range.rs | 114 ++ vendor/time/src/error/conversion_range.rs | 36 + vendor/time/src/error/different_variant.rs | 34 + vendor/time/src/error/format.rs | 113 ++ vendor/time/src/error/indeterminate_offset.rs | 35 + .../time/src/error/invalid_format_description.rs | 132 ++ vendor/time/src/error/invalid_variant.rs | 34 + vendor/time/src/error/mod.rs | 131 ++ vendor/time/src/error/parse.rs | 109 ++ vendor/time/src/error/parse_from_description.rs | 53 + vendor/time/src/error/try_from_parsed.rs | 71 + vendor/time/src/ext/digit_count.rs | 26 + vendor/time/src/ext/instant.rs | 98 ++ vendor/time/src/ext/mod.rs | 13 + vendor/time/src/ext/numerical_duration.rs | 140 ++ vendor/time/src/ext/numerical_std_duration.rs | 192 +++ .../src/format_description/borrowed_format_item.rs | 106 ++ vendor/time/src/format_description/component.rs | 44 + vendor/time/src/format_description/mod.rs | 41 + vendor/time/src/format_description/modifier.rs | 434 ++++++ .../src/format_description/owned_format_item.rs | 158 ++ vendor/time/src/format_description/parse/ast.rs | 384 +++++ .../src/format_description/parse/format_item.rs | 549 +++++++ vendor/time/src/format_description/parse/lexer.rs | 284 ++++ vendor/time/src/format_description/parse/mod.rs | 262 ++++ .../time/src/format_description/parse/strftime.rs | 487 ++++++ .../src/format_description/well_known/iso8601.rs | 257 ++++ .../well_known/iso8601/adt_hack.rs | 247 +++ .../src/format_description/well_known/rfc2822.rs | 30 + .../src/format_description/well_known/rfc3339.rs | 30 + vendor/time/src/formatting/formattable.rs | 312 ++++ vendor/time/src/formatting/iso8601.rs | 142 ++ vendor/time/src/formatting/mod.rs | 517 +++++++ vendor/time/src/hint.rs | 26 + vendor/time/src/instant.rs | 287 ++++ vendor/time/src/internal_macros.rs | 209 +++ .../time/src/interop/js_sys_date_offsetdatetime.rs | 27 + vendor/time/src/interop/js_sys_date_utcdatetime.rs | 26 + vendor/time/src/interop/mod.rs | 28 + .../time/src/interop/offsetdatetime_systemtime.rs | 75 + .../time/src/interop/offsetdatetime_utcdatetime.rs | 68 + vendor/time/src/interop/utcdatetime_systemtime.rs | 75 + vendor/time/src/lib.rs | 153 ++ vendor/time/src/macros.rs | 149 ++ vendor/time/src/month.rs | 273 ++++ vendor/time/src/offset_date_time.rs | 1511 ++++++++++++++++++ vendor/time/src/parsing/combinator/mod.rs | 194 +++ vendor/time/src/parsing/combinator/rfc/iso8601.rs | 177 +++ vendor/time/src/parsing/combinator/rfc/mod.rs | 10 + vendor/time/src/parsing/combinator/rfc/rfc2234.rs | 13 + vendor/time/src/parsing/combinator/rfc/rfc2822.rs | 108 ++ vendor/time/src/parsing/component.rs | 379 +++++ vendor/time/src/parsing/iso8601.rs | 332 ++++ vendor/time/src/parsing/mod.rs | 57 + vendor/time/src/parsing/parsable.rs | 800 ++++++++++ vendor/time/src/parsing/parsed.rs | 1118 ++++++++++++++ vendor/time/src/parsing/shim.rs | 50 + vendor/time/src/primitive_date_time.rs | 1050 +++++++++++++ vendor/time/src/quickcheck.rs | 230 +++ vendor/time/src/rand.rs | 90 ++ vendor/time/src/serde/iso8601.rs | 76 + vendor/time/src/serde/mod.rs | 535 +++++++ vendor/time/src/serde/rfc2822.rs | 71 + vendor/time/src/serde/rfc3339.rs | 71 + vendor/time/src/serde/timestamp/microseconds.rs | 63 + vendor/time/src/serde/timestamp/milliseconds.rs | 63 + .../time/src/serde/timestamp/milliseconds_i64.rs | 66 + vendor/time/src/serde/timestamp/mod.rs | 65 + vendor/time/src/serde/timestamp/nanoseconds.rs | 61 + vendor/time/src/serde/visitor.rs | 355 +++++ vendor/time/src/sys/local_offset_at/imp.rs | 8 + vendor/time/src/sys/local_offset_at/mod.rs | 23 + vendor/time/src/sys/local_offset_at/unix.rs | 101 ++ vendor/time/src/sys/local_offset_at/wasm_js.rs | 16 + vendor/time/src/sys/local_offset_at/windows.rs | 113 ++ vendor/time/src/sys/mod.rs | 11 + vendor/time/src/sys/refresh_tz/imp.rs | 9 + vendor/time/src/sys/refresh_tz/mod.rs | 17 + vendor/time/src/sys/refresh_tz/unix.rs | 48 + vendor/time/src/tests.rs | 104 ++ vendor/time/src/time.rs | 937 ++++++++++++ vendor/time/src/utc_date_time.rs | 1223 +++++++++++++++ vendor/time/src/utc_offset.rs | 464 ++++++ vendor/time/src/util.rs | 105 ++ vendor/time/src/weekday.rs | 220 +++ 87 files changed, 21043 insertions(+) create mode 100644 vendor/time/src/date.rs create mode 100644 vendor/time/src/duration.rs create mode 100644 vendor/time/src/error/component_range.rs create mode 100644 vendor/time/src/error/conversion_range.rs create mode 100644 vendor/time/src/error/different_variant.rs create mode 100644 vendor/time/src/error/format.rs create mode 100644 vendor/time/src/error/indeterminate_offset.rs create mode 100644 vendor/time/src/error/invalid_format_description.rs create mode 100644 vendor/time/src/error/invalid_variant.rs create mode 100644 vendor/time/src/error/mod.rs create mode 100644 vendor/time/src/error/parse.rs create mode 100644 vendor/time/src/error/parse_from_description.rs create mode 100644 vendor/time/src/error/try_from_parsed.rs create mode 100644 vendor/time/src/ext/digit_count.rs create mode 100644 vendor/time/src/ext/instant.rs create mode 100644 vendor/time/src/ext/mod.rs create mode 100644 vendor/time/src/ext/numerical_duration.rs create mode 100644 vendor/time/src/ext/numerical_std_duration.rs create mode 100644 vendor/time/src/format_description/borrowed_format_item.rs create mode 100644 vendor/time/src/format_description/component.rs create mode 100644 vendor/time/src/format_description/mod.rs create mode 100644 vendor/time/src/format_description/modifier.rs create mode 100644 vendor/time/src/format_description/owned_format_item.rs create mode 100644 vendor/time/src/format_description/parse/ast.rs create mode 100644 vendor/time/src/format_description/parse/format_item.rs create mode 100644 vendor/time/src/format_description/parse/lexer.rs create mode 100644 vendor/time/src/format_description/parse/mod.rs create mode 100644 vendor/time/src/format_description/parse/strftime.rs create mode 100644 vendor/time/src/format_description/well_known/iso8601.rs create mode 100644 vendor/time/src/format_description/well_known/iso8601/adt_hack.rs create mode 100644 vendor/time/src/format_description/well_known/rfc2822.rs create mode 100644 vendor/time/src/format_description/well_known/rfc3339.rs create mode 100644 vendor/time/src/formatting/formattable.rs create mode 100644 vendor/time/src/formatting/iso8601.rs create mode 100644 vendor/time/src/formatting/mod.rs create mode 100644 vendor/time/src/hint.rs create mode 100644 vendor/time/src/instant.rs create mode 100644 vendor/time/src/internal_macros.rs create mode 100644 vendor/time/src/interop/js_sys_date_offsetdatetime.rs create mode 100644 vendor/time/src/interop/js_sys_date_utcdatetime.rs create mode 100644 vendor/time/src/interop/mod.rs create mode 100644 vendor/time/src/interop/offsetdatetime_systemtime.rs create mode 100644 vendor/time/src/interop/offsetdatetime_utcdatetime.rs create mode 100644 vendor/time/src/interop/utcdatetime_systemtime.rs create mode 100644 vendor/time/src/lib.rs create mode 100644 vendor/time/src/macros.rs create mode 100644 vendor/time/src/month.rs create mode 100644 vendor/time/src/offset_date_time.rs create mode 100644 vendor/time/src/parsing/combinator/mod.rs create mode 100644 vendor/time/src/parsing/combinator/rfc/iso8601.rs create mode 100644 vendor/time/src/parsing/combinator/rfc/mod.rs create mode 100644 vendor/time/src/parsing/combinator/rfc/rfc2234.rs create mode 100644 vendor/time/src/parsing/combinator/rfc/rfc2822.rs create mode 100644 vendor/time/src/parsing/component.rs create mode 100644 vendor/time/src/parsing/iso8601.rs create mode 100644 vendor/time/src/parsing/mod.rs create mode 100644 vendor/time/src/parsing/parsable.rs create mode 100644 vendor/time/src/parsing/parsed.rs create mode 100644 vendor/time/src/parsing/shim.rs create mode 100644 vendor/time/src/primitive_date_time.rs create mode 100644 vendor/time/src/quickcheck.rs create mode 100644 vendor/time/src/rand.rs create mode 100644 vendor/time/src/serde/iso8601.rs create mode 100644 vendor/time/src/serde/mod.rs create mode 100644 vendor/time/src/serde/rfc2822.rs create mode 100644 vendor/time/src/serde/rfc3339.rs create mode 100644 vendor/time/src/serde/timestamp/microseconds.rs create mode 100644 vendor/time/src/serde/timestamp/milliseconds.rs create mode 100644 vendor/time/src/serde/timestamp/milliseconds_i64.rs create mode 100644 vendor/time/src/serde/timestamp/mod.rs create mode 100644 vendor/time/src/serde/timestamp/nanoseconds.rs create mode 100644 vendor/time/src/serde/visitor.rs create mode 100644 vendor/time/src/sys/local_offset_at/imp.rs create mode 100644 vendor/time/src/sys/local_offset_at/mod.rs create mode 100644 vendor/time/src/sys/local_offset_at/unix.rs create mode 100644 vendor/time/src/sys/local_offset_at/wasm_js.rs create mode 100644 vendor/time/src/sys/local_offset_at/windows.rs create mode 100644 vendor/time/src/sys/mod.rs create mode 100644 vendor/time/src/sys/refresh_tz/imp.rs create mode 100644 vendor/time/src/sys/refresh_tz/mod.rs create mode 100644 vendor/time/src/sys/refresh_tz/unix.rs create mode 100644 vendor/time/src/tests.rs create mode 100644 vendor/time/src/time.rs create mode 100644 vendor/time/src/utc_date_time.rs create mode 100644 vendor/time/src/utc_offset.rs create mode 100644 vendor/time/src/util.rs create mode 100644 vendor/time/src/weekday.rs (limited to 'vendor/time/src') diff --git a/vendor/time/src/date.rs b/vendor/time/src/date.rs new file mode 100644 index 00000000..3772c2da --- /dev/null +++ b/vendor/time/src/date.rs @@ -0,0 +1,1490 @@ +//! The [`Date`] struct and its associated `impl`s. + +#[cfg(feature = "formatting")] +use alloc::string::String; +use core::num::{NonZeroI32, NonZeroU8}; +use core::ops::{Add, Sub}; +use core::time::Duration as StdDuration; +use core::{cmp, fmt}; +#[cfg(feature = "formatting")] +use std::io; + +use deranged::RangedI32; +use num_conv::prelude::*; +use powerfmt::ext::FormatterExt; +use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; + +use crate::convert::*; +use crate::ext::DigitCount; +#[cfg(feature = "formatting")] +use crate::formatting::Formattable; +use crate::internal_macros::{ + 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, is_leap_year, weeks_in_year}; +use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday}; + +type Year = RangedI32; + +/// The minimum valid year. +pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") { + -999_999 +} else { + -9999 +}; +/// The maximum valid year. +pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") { + 999_999 +} else { + 9999 +}; + +/// Date in the proleptic Gregorian calendar. +/// +/// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999 +/// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications +/// and introduces some ambiguities when parsing. +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Date { + /// Bitpacked field containing the year, ordinal, and whether the year is a leap year. + // | x | xxxxxxxxxxxxxxxxxxxxx | x | xxxxxxxxx | + // | 1 bit | 21 bits | 1 bit | 9 bits | + // | unassigned | year | is leap year? | ordinal | + // The year is 15 bits when `large-dates` is not enabled. + value: NonZeroI32, +} + +impl Date { + /// The minimum valid `Date`. + /// + /// The value of this may vary depending on the feature flags enabled. + // 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. + // 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)) }; + + /// Construct a `Date` from its internal representation, the validity of which must be + /// guaranteed by the caller. + /// + /// # Safety + /// + /// - `ordinal` must be non-zero and at most the number of days in `year` + /// - `is_leap_year` must be `true` if and only if `year` is a leap year + const unsafe fn from_parts(year: i32, is_leap_year: bool, ordinal: u16) -> Self { + debug_assert!(year >= MIN_YEAR); + debug_assert!(year <= MAX_YEAR); + debug_assert!(ordinal != 0); + debug_assert!(ordinal <= days_in_year(year)); + debug_assert!(crate::util::is_leap_year(year) == is_leap_year); + + Self { + // Safety: `ordinal` is not zero. + value: unsafe { + NonZeroI32::new_unchecked( + (year << 10) | ((is_leap_year as i32) << 9) | ordinal as i32, + ) + }, + } + } + + /// Construct a `Date` from the year and ordinal values, the validity of which must be + /// guaranteed by the caller. + /// + /// # Safety + /// + /// `ordinal` must be non-zero and at most the number of days in `year`. `year` should be in the + /// range `MIN_YEAR..=MAX_YEAR`, but this is not a safety invariant. + #[doc(hidden)] + pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { + // Safety: The caller must guarantee that `ordinal` is not zero. + unsafe { Self::from_parts(year, is_leap_year(year), ordinal) } + } + + /// Attempt to create a `Date` from the year, month, and day. + /// + /// ```rust + /// # use time::{Date, Month}; + /// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok()); + /// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::{Date, Month}; + /// assert!(Date::from_calendar_date(2019, Month::February, 29).is_err()); // 2019 isn't a leap year. + /// ``` + pub const fn from_calendar_date( + year: i32, + month: Month, + day: u8, + ) -> Result { + /// Cumulative days through the beginning of a month in both common and leap years. + const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [ + [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], + [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], + ]; + + ensure_ranged!(Year: year); + match day { + 1..=28 => {} + 29..=31 if day <= month.length(year) => {} + _ => { + return Err(error::ComponentRange { + name: "day", + minimum: 1, + maximum: month.length(year) as i64, + value: day as i64, + conditional_message: Some("for the given month and year"), + }); + } + } + + // 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. + /// + /// ```rust + /// # use time::Date; + /// assert!(Date::from_ordinal_date(2019, 1).is_ok()); + /// assert!(Date::from_ordinal_date(2019, 365).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::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 { + ensure_ranged!(Year: year); + match ordinal { + 1..=365 => {} + 366 if is_leap_year(year) => {} + _ => { + return Err(error::ComponentRange { + name: "ordinal", + minimum: 1, + maximum: days_in_year(year) as i64, + value: ordinal as i64, + conditional_message: Some("for the given year"), + }); + } + } + + // 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. + /// + /// ```rust + /// # use time::{Date, Weekday::*}; + /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok()); + /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok()); + /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::{Date, Weekday::*}; + /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks. + /// ``` + pub const fn from_iso_week_date( + year: i32, + week: u8, + weekday: Weekday, + ) -> Result { + ensure_ranged!(Year: year); + match week { + 1..=52 => {} + 53 if week <= weeks_in_year(year) => {} + _ => { + return Err(error::ComponentRange { + name: "week", + minimum: 1, + maximum: weeks_in_year(year) as i64, + value: week as i64, + conditional_message: Some("for the given year"), + }); + } + } + + let adj_year = year - 1; + let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) + + div_floor!(adj_year, 400); + let jan_4 = match (raw % 7) as i8 { + -6 | 1 => 8, + -5 | 2 => 9, + -4 | 3 => 10, + -3 | 4 => 4, + -2 | 5 => 5, + -1 | 6 => 6, + _ => 7, + }; + let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4; + + Ok(if ordinal <= 0 { + // 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 { + // Safety: `ordinal` is not zero. + unsafe { + Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year)) + } + } else { + // Safety: `ordinal` is not zero. + unsafe { Self::__from_ordinal_date_unchecked(year, ordinal as u16) } + }) + } + + /// Create a `Date` from the Julian day. + /// + /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is + /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). + /// + /// ```rust + /// # use time::Date; + /// # use time_macros::date; + /// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24))); + /// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000-01-01))); + /// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019-01-01))); + /// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019-12-31))); + /// ``` + #[doc(alias = "from_julian_date")] + pub const fn from_julian_day(julian_day: i32) -> Result { + type JulianDay = RangedI32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>; + ensure_ranged!(JulianDay: julian_day); + // Safety: The Julian day number is in range. + Ok(unsafe { Self::from_julian_day_unchecked(julian_day) }) + } + + /// Create a `Date` from the Julian day. + /// + /// # Safety + /// + /// The provided Julian day number must be between `Date::MIN.to_julian_day()` and + /// `Date::MAX.to_julian_day()` inclusive. + pub(crate) const unsafe fn from_julian_day_unchecked(julian_day: i32) -> Self { + debug_assert!(julian_day >= Self::MIN.to_julian_day()); + debug_assert!(julian_day <= Self::MAX.to_julian_day()); + + const S: i32 = 2_500; + const K: i32 = 719_468 + 146_097 * S; + const L: i32 = 400 * S; + + let julian_day = julian_day - 2_440_588; + let n = (julian_day + K) as u32; + + let n_1 = 4 * n + 3; + let c = n_1 / 146_097; + let n_c = n_1 % 146_097 / 4; + + let n_2 = 4 * n_c + 3; + let p_2 = 2_939_745 * n_2 as u64; + let z = (p_2 >> 32) as u32; + let n_y = p_2 as u32 / 2_939_745 / 4; + let y = 100 * c + z; + + let j = n_y >= 306; + let y_g = y as i32 - L + j as i32; + + let is_leap_year = is_leap_year(y_g); + let ordinal = if j { + n_y - 305 + } else { + n_y + 60 + is_leap_year as u32 + }; + + // Safety: `ordinal` is not zero and `is_leap_year` is correct, so long as the Julian day + // number is in range. + unsafe { Self::from_parts(y_g, is_leap_year, ordinal as u16) } + } + + /// Whether `is_leap_year(self.year())` is `true`. + /// + /// This method is optimized to take advantage of the fact that the value is pre-computed upon + /// construction and stored in the bitpacked struct. + const fn is_in_leap_year(self) -> bool { + (self.value.get() >> 9) & 1 == 1 + } + + /// Get the year of the date. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).year(), 2019); + /// assert_eq!(date!(2019-12-31).year(), 2019); + /// assert_eq!(date!(2020-01-01).year(), 2020); + /// ``` + pub const fn year(self) -> i32 { + self.value.get() >> 10 + } + + /// Get the month. + /// + /// ```rust + /// # use time::Month; + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).month(), Month::January); + /// assert_eq!(date!(2019-12-31).month(), Month::December); + /// ``` + pub const fn month(self) -> Month { + let ordinal = self.ordinal() as u32; + let jan_feb_len = 59 + self.is_in_leap_year() as u32; + + let (month_adj, ordinal_adj) = if ordinal <= jan_feb_len { + (0, 0) + } else { + (2, jan_feb_len) + }; + + let ordinal = ordinal - ordinal_adj; + let month = ((ordinal * 268 + 8031) >> 13) + month_adj; + + // Safety: `month` is guaranteed to be between 1 and 12 inclusive. + unsafe { + match Month::from_number(NonZeroU8::new_unchecked(month as u8)) { + Ok(month) => month, + Err(_) => core::hint::unreachable_unchecked(), + } + } + } + + /// Get the day of the month. + /// + /// The returned value will always be in the range `1..=31`. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).day(), 1); + /// assert_eq!(date!(2019-12-31).day(), 31); + /// ``` + pub const fn day(self) -> u8 { + let ordinal = self.ordinal() as u32; + let jan_feb_len = 59 + self.is_in_leap_year() as u32; + + let ordinal_adj = if ordinal <= jan_feb_len { + 0 + } else { + jan_feb_len + }; + + let ordinal = ordinal - ordinal_adj; + let month = (ordinal * 268 + 8031) >> 13; + let days_in_preceding_months = (month * 3917 - 3866) >> 7; + (ordinal - days_in_preceding_months) as u8 + } + + /// Get the day of the year. + /// + /// The returned value will always be in the range `1..=366` (`1..=365` for common years). + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).ordinal(), 1); + /// assert_eq!(date!(2019-12-31).ordinal(), 365); + /// ``` + pub const fn ordinal(self) -> u16 { + (self.value.get() & 0x1FF) as u16 + } + + /// Get the ISO 8601 year and week number. + pub(crate) const fn iso_year_week(self) -> (i32, u8) { + let (year, ordinal) = self.to_ordinal_date(); + + match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as u8 { + 0 => (year - 1, weeks_in_year(year - 1)), + 53 if weeks_in_year(year) == 52 => (year + 1, 1), + week => (year, week), + } + } + + /// Get the ISO week number. + /// + /// The returned value will always be in the range `1..=53`. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).iso_week(), 1); + /// assert_eq!(date!(2019-10-04).iso_week(), 40); + /// assert_eq!(date!(2020-01-01).iso_week(), 1); + /// assert_eq!(date!(2020-12-31).iso_week(), 53); + /// assert_eq!(date!(2021-01-01).iso_week(), 53); + /// ``` + pub const fn iso_week(self) -> u8 { + self.iso_year_week().1 + } + + /// Get the week number where week 1 begins on the first Sunday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).sunday_based_week(), 0); + /// assert_eq!(date!(2020-01-01).sunday_based_week(), 0); + /// assert_eq!(date!(2020-12-31).sunday_based_week(), 52); + /// assert_eq!(date!(2021-01-01).sunday_based_week(), 0); + /// ``` + pub const fn sunday_based_week(self) -> u8 { + ((self.ordinal() as i16 - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as u8 + } + + /// Get the week number where week 1 begins on the first Monday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).monday_based_week(), 0); + /// assert_eq!(date!(2020-01-01).monday_based_week(), 0); + /// assert_eq!(date!(2020-12-31).monday_based_week(), 52); + /// assert_eq!(date!(2021-01-01).monday_based_week(), 0); + /// ``` + pub const fn monday_based_week(self) -> u8 { + ((self.ordinal() as i16 - self.weekday().number_days_from_monday() as i16 + 6) / 7) as u8 + } + + /// Get the year, month, and day. + /// + /// ```rust + /// # use time::Month; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2019-01-01).to_calendar_date(), + /// (2019, Month::January, 1) + /// ); + /// ``` + pub const fn to_calendar_date(self) -> (i32, Month, u8) { + let (year, ordinal) = self.to_ordinal_date(); + let ordinal = ordinal as u32; + let jan_feb_len = 59 + self.is_in_leap_year() as u32; + + let (month_adj, ordinal_adj) = if ordinal <= jan_feb_len { + (0, 0) + } else { + (2, jan_feb_len) + }; + + let ordinal = ordinal - ordinal_adj; + let month = (ordinal * 268 + 8031) >> 13; + let days_in_preceding_months = (month * 3917 - 3866) >> 7; + let day = ordinal - days_in_preceding_months; + let month = month + month_adj; + + ( + year, + // Safety: `month` is guaranteed to be between 1 and 12 inclusive. + unsafe { + match Month::from_number(NonZeroU8::new_unchecked(month as u8)) { + Ok(month) => month, + Err(_) => core::hint::unreachable_unchecked(), + } + }, + day as u8, + ) + } + + /// Get the year and ordinal day number. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).to_ordinal_date(), (2019, 1)); + /// ``` + pub const fn to_ordinal_date(self) -> (i32, u16) { + (self.year(), self.ordinal()) + } + + /// Get the ISO 8601 year, week number, and weekday. + /// + /// ```rust + /// # use time::Weekday::*; + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).to_iso_week_date(), (2019, 1, Tuesday)); + /// assert_eq!(date!(2019-10-04).to_iso_week_date(), (2019, 40, Friday)); + /// assert_eq!(date!(2020-01-01).to_iso_week_date(), (2020, 1, Wednesday)); + /// assert_eq!(date!(2020-12-31).to_iso_week_date(), (2020, 53, Thursday)); + /// assert_eq!(date!(2021-01-01).to_iso_week_date(), (2020, 53, Friday)); + /// ``` + pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { + let (year, ordinal) = self.to_ordinal_date(); + let weekday = self.weekday(); + + match ((ordinal + 10 - weekday.number_from_monday() as u16) / 7) as u8 { + 0 => (year - 1, weeks_in_year(year - 1), weekday), + 53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday), + week => (year, week, weekday), + } + } + + /// Get the weekday. + /// + /// ```rust + /// # use time::Weekday::*; + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).weekday(), Tuesday); + /// assert_eq!(date!(2019-02-01).weekday(), Friday); + /// assert_eq!(date!(2019-03-01).weekday(), Friday); + /// assert_eq!(date!(2019-04-01).weekday(), Monday); + /// assert_eq!(date!(2019-05-01).weekday(), Wednesday); + /// assert_eq!(date!(2019-06-01).weekday(), Saturday); + /// assert_eq!(date!(2019-07-01).weekday(), Monday); + /// assert_eq!(date!(2019-08-01).weekday(), Thursday); + /// assert_eq!(date!(2019-09-01).weekday(), Sunday); + /// assert_eq!(date!(2019-10-01).weekday(), Tuesday); + /// assert_eq!(date!(2019-11-01).weekday(), Friday); + /// assert_eq!(date!(2019-12-01).weekday(), Sunday); + /// ``` + pub const fn weekday(self) -> Weekday { + match self.to_julian_day() % 7 { + -6 | 1 => Weekday::Tuesday, + -5 | 2 => Weekday::Wednesday, + -4 | 3 => Weekday::Thursday, + -3 | 4 => Weekday::Friday, + -2 | 5 => Weekday::Saturday, + -1 | 6 => Weekday::Sunday, + val => { + debug_assert!(val == 0); + Weekday::Monday + } + } + } + + /// Get the next calendar date. + /// + /// ```rust + /// # use time::Date; + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).next_day(), Some(date!(2019-01-02))); + /// assert_eq!(date!(2019-01-31).next_day(), Some(date!(2019-02-01))); + /// assert_eq!(date!(2019-12-31).next_day(), Some(date!(2020-01-01))); + /// assert_eq!(Date::MAX.next_day(), None); + /// ``` + pub const fn next_day(self) -> Option { + if self.ordinal() == 366 || (self.ordinal() == 365 && !self.is_in_leap_year()) { + if self.value.get() == Self::MAX.value.get() { + None + } else { + // Safety: `ordinal` is not zero. + unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) } + } + } else { + Some(Self { + // Safety: `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked(self.value.get() + 1) }, + }) + } + } + + /// Get the previous calendar date. + /// + /// ```rust + /// # use time::Date; + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-02).previous_day(), Some(date!(2019-01-01))); + /// assert_eq!(date!(2019-02-01).previous_day(), Some(date!(2019-01-31))); + /// assert_eq!(date!(2020-01-01).previous_day(), Some(date!(2019-12-31))); + /// assert_eq!(Date::MIN.previous_day(), None); + /// ``` + pub const fn previous_day(self) -> Option { + if self.ordinal() != 1 { + Some(Self { + // Safety: `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) }, + }) + } else if self.value.get() == Self::MIN.value.get() { + None + } else { + // 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. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0); + /// assert_eq!(date!(2000-01-01).to_julian_day(), 2_451_545); + /// assert_eq!(date!(2019-01-01).to_julian_day(), 2_458_485); + /// assert_eq!(date!(2019-12-31).to_julian_day(), 2_458_849); + /// ``` + pub const fn to_julian_day(self) -> i32 { + let (year, ordinal) = self.to_ordinal_date(); + + // The algorithm requires a non-negative year. Add the lowest value to make it so. This is + // adjusted for at the end with the final subtraction. + let adj_year = year + 999_999; + let century = adj_year / 100; + + let days_before_year = (1461 * adj_year as i64 / 4) as i32 - century + century / 4; + days_before_year + ordinal as i32 - 363_521_075 + } + + /// Computes `self + duration`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.checked_add(1.days()), None); + /// assert_eq!(Date::MIN.checked_add((-2).days()), None); + /// assert_eq!( + /// date!(2020-12-31).checked_add(2.days()), + /// Some(date!(2021-01-02)) + /// ); + /// ``` + /// + /// # Note + /// + /// This function only takes whole days into account. + /// + /// ```rust + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX)); + /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN)); + /// assert_eq!( + /// date!(2020-12-31).checked_add(23.hours()), + /// Some(date!(2020-12-31)) + /// ); + /// assert_eq!( + /// date!(2020-12-31).checked_add(47.hours()), + /// Some(date!(2021-01-01)) + /// ); + /// ``` + pub const fn checked_add(self, duration: Duration) -> Option { + let whole_days = duration.whole_days(); + if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { + return None; + } + + let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as i32)); + if let Ok(date) = Self::from_julian_day(julian_day) { + Some(date) + } else { + None + } + } + + /// 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 { + 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 i32)); + if let Ok(date) = Self::from_julian_day(julian_day) { + Some(date) + } else { + None + } + } + + /// Computes `self - duration`, returning `None` if an overflow occurred. + /// + /// ``` + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.checked_sub((-2).days()), None); + /// assert_eq!(Date::MIN.checked_sub(1.days()), None); + /// assert_eq!( + /// date!(2020-12-31).checked_sub(2.days()), + /// Some(date!(2020-12-29)) + /// ); + /// ``` + /// + /// # Note + /// + /// This function only takes whole days into account. + /// + /// ``` + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX)); + /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN)); + /// assert_eq!( + /// date!(2020-12-31).checked_sub(23.hours()), + /// Some(date!(2020-12-31)) + /// ); + /// assert_eq!( + /// date!(2020-12-31).checked_sub(47.hours()), + /// Some(date!(2020-12-30)) + /// ); + /// ``` + pub const fn checked_sub(self, duration: Duration) -> Option { + let whole_days = duration.whole_days(); + if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { + return None; + } + + let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as i32)); + if let Ok(date) = Self::from_julian_day(julian_day) { + Some(date) + } else { + 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 { + 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 i32)); + 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 { + 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 { + 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 { + 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 { + if n == 0 { + return None; + } + + const_try_opt!(self.checked_prev_occurrence(weekday)) + .checked_sub(Duration::weeks(n as i64 - 1)) + } + + /// Computes `self + duration`, saturating value on overflow. + /// + /// ```rust + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX); + /// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN); + /// assert_eq!( + /// date!(2020-12-31).saturating_add(2.days()), + /// date!(2021-01-02) + /// ); + /// ``` + /// + /// # Note + /// + /// This function only takes whole days into account. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2020-12-31).saturating_add(23.hours()), + /// date!(2020-12-31) + /// ); + /// assert_eq!( + /// date!(2020-12-31).saturating_add(47.hours()), + /// date!(2021-01-01) + /// ); + /// ``` + pub const fn saturating_add(self, duration: Duration) -> Self { + if let Some(datetime) = self.checked_add(duration) { + datetime + } else if duration.is_negative() { + Self::MIN + } else { + debug_assert!(duration.is_positive()); + Self::MAX + } + } + + /// Computes `self - duration`, saturating value on overflow. + /// + /// ``` + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::date; + /// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX); + /// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN); + /// assert_eq!( + /// date!(2020-12-31).saturating_sub(2.days()), + /// date!(2020-12-29) + /// ); + /// ``` + /// + /// # Note + /// + /// This function only takes whole days into account. + /// + /// ``` + /// # use time::ext::NumericalDuration; + /// # use time_macros::date; + /// assert_eq!( + /// date!(2020-12-31).saturating_sub(23.hours()), + /// date!(2020-12-31) + /// ); + /// assert_eq!( + /// date!(2020-12-31).saturating_sub(47.hours()), + /// date!(2020-12-30) + /// ); + /// ``` + pub const fn saturating_sub(self, duration: Duration) -> Self { + if let Some(datetime) = self.checked_sub(duration) { + datetime + } else if duration.is_negative() { + Self::MAX + } else { + debug_assert!(duration.is_positive()); + Self::MIN + } + } + + /// Replace the year. The month and day will be unchanged. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!( + /// date!(2022-02-18).replace_year(2019), + /// Ok(date!(2019-02-18)) + /// ); + /// assert!(date!(2022-02-18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year + /// assert!(date!(2022-02-18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year + /// ``` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_year(self, year: i32) -> Result { + ensure_ranged!(Year: year); + + let ordinal = self.ordinal(); + + // Dates in January and February are unaffected by leap years. + if ordinal <= 59 { + // Safety: `ordinal` is not zero. + return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }); + } + + match (self.is_in_leap_year(), is_leap_year(year)) { + (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", + value: 29, + minimum: 1, + maximum: 28, + conditional_message: Some("for the given month and year"), + }), + // We're going from a common year to a leap year. Shift dates in March and later by + // one day. + // 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. + // Safety: `ordinal` is not zero. + (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal - 1) }), + } + } + + /// Replace the month of the year. + /// + /// ```rust + /// # use time_macros::date; + /// # use time::Month; + /// assert_eq!( + /// date!(2022-02-18).replace_month(Month::January), + /// Ok(date!(2022-01-18)) + /// ); + /// assert!(date!(2022-01-30) + /// .replace_month(Month::February) + /// .is_err()); // 30 isn't a valid day in February + /// ``` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_month(self, month: Month) -> Result { + let (year, _, day) = self.to_calendar_date(); + Self::from_calendar_date(year, month, day) + } + + /// Replace the day of the month. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2022-02-18).replace_day(1), Ok(date!(2022-02-01))); + /// assert!(date!(2022-02-18).replace_day(0).is_err()); // 0 isn't a valid day + /// assert!(date!(2022-02-18).replace_day(30).is_err()); // 30 isn't a valid day in February + /// ``` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_day(self, day: u8) -> Result { + match day { + 1..=28 => {} + 29..=31 if day <= self.month().length(self.year()) => {} + _ => { + return Err(error::ComponentRange { + name: "day", + minimum: 1, + maximum: self.month().length(self.year()) as i64, + value: day as i64, + conditional_message: Some("for the given month and year"), + }); + } + } + + // 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 u16, + ) + }) + } + + /// Replace the day of the year. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2022-049).replace_ordinal(1), Ok(date!(2022-001))); + /// assert!(date!(2022-049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal + /// assert!(date!(2022-049).replace_ordinal(366).is_err()); // 2022 isn't a leap year + /// ```` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result { + match ordinal { + 1..=365 => {} + 366 if self.is_in_leap_year() => {} + _ => { + return Err(error::ComponentRange { + name: "ordinal", + minimum: 1, + maximum: days_in_year(self.year()) as i64, + value: ordinal as i64, + conditional_message: Some("for the given year"), + }); + } + } + + // Safety: `ordinal` is in range. + Ok(unsafe { Self::__from_ordinal_date_unchecked(self.year(), ordinal) }) + } +} + +/// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`]. +impl Date { + /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set + /// to midnight. + /// + /// ```rust + /// # use time_macros::{date, datetime}; + /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00)); + /// ``` + pub const fn midnight(self) -> PrimitiveDateTime { + PrimitiveDateTime::new(self, Time::MIDNIGHT) + } + + /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`]. + /// + /// ```rust + /// # use time_macros::{date, datetime, time}; + /// assert_eq!( + /// date!(1970-01-01).with_time(time!(0:00)), + /// datetime!(1970-01-01 0:00), + /// ); + /// ``` + pub const fn with_time(self, time: Time) -> PrimitiveDateTime { + PrimitiveDateTime::new(self, time) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms(0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms(24, 0, 0).is_err()); + /// ``` + pub const fn with_hms( + self, + hour: u8, + minute: u8, + second: u8, + ) -> Result { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms(hour, minute, second)), + )) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms_milli(0, 0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms_milli(24, 0, 0, 0).is_err()); + /// ``` + pub const fn with_hms_milli( + self, + hour: u8, + minute: u8, + second: u8, + millisecond: u16, + ) -> Result { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms_milli(hour, minute, second, millisecond)), + )) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms_micro(0, 0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms_micro(24, 0, 0, 0).is_err()); + /// ``` + pub const fn with_hms_micro( + self, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + ) -> Result { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms_micro(hour, minute, second, microsecond)), + )) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms_nano(0, 0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms_nano(24, 0, 0, 0).is_err()); + /// ``` + pub const fn with_hms_nano( + self, + hour: u8, + minute: u8, + second: u8, + nanosecond: u32, + ) -> Result { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)), + )) + } +} + +#[cfg(feature = "formatting")] +impl Date { + /// Format the `Date` using the provided [format description](crate::format_description). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result { + format.format_into(output, Some(self), None, None) + } + + /// Format the `Date` using the provided [format description](crate::format_description). + /// + /// ```rust + /// # use time::{format_description}; + /// # use time_macros::date; + /// let format = format_description::parse("[year]-[month]-[day]")?; + /// assert_eq!(date!(2020-01-02).format(&format)?, "2020-01-02"); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result { + format.format(Some(self), None, None) + } +} + +#[cfg(feature = "parsing")] +impl Date { + /// Parse a `Date` from the input using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::Date; + /// # use time_macros::{date, format_description}; + /// let format = format_description!("[year]-[month]-[day]"); + /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020-01-02)); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result { + description.parse_date(input.as_bytes()) + } +} + +mod private { + #[non_exhaustive] + #[derive(Debug, Clone, Copy)] + pub struct DateMetadata { + /// The width of the year component, including the sign. + pub(super) year_width: u8, + /// Whether the sign should be displayed. + pub(super) display_sign: bool, + pub(super) year: i32, + pub(super) month: u8, + pub(super) day: u8, + } +} +use private::DateMetadata; + +impl SmartDisplay for Date { + type Metadata = DateMetadata; + + fn metadata(&self, _: FormatterOptions) -> Metadata { + let (year, month, day) = self.to_calendar_date(); + + // There is a minimum of four digits for any year. + let mut year_width = cmp::max(year.unsigned_abs().num_digits(), 4); + let display_sign = if !(0..10_000).contains(&year) { + // An extra character is required for the sign. + year_width += 1; + true + } else { + false + }; + + let formatted_width = year_width.extend::() + + smart_display::padded_width_of!( + "-", + u8::from(month) => width(2), + "-", + day => width(2), + ); + + Metadata::new( + formatted_width, + self, + DateMetadata { + year_width, + display_sign, + year, + month: u8::from(month), + day, + }, + ) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata, + ) -> fmt::Result { + let DateMetadata { + year_width, + display_sign, + year, + month, + day, + } = *metadata; + let year_width = year_width.extend(); + + if display_sign { + f.pad_with_width( + metadata.unpadded_width(), + format_args!("{year:+0year_width$}-{month:02}-{day:02}"), + ) + } else { + f.pad_with_width( + metadata.unpadded_width(), + format_args!("{year:0year_width$}-{month:02}-{day:02}"), + ) + } + } +} + +impl fmt::Display for Date { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for Date { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fmt::Display::fmt(self, f) + } +} + +impl Add for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: Duration) -> Self::Output { + self.checked_add(duration) + .expect("overflow adding duration to date") + } +} + +impl Add for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: StdDuration) -> Self::Output { + self.checked_add_std(duration) + .expect("overflow adding duration to date") + } +} + +impl_add_assign!(Date: Duration, StdDuration); + +impl Sub for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, duration: Duration) -> Self::Output { + self.checked_sub(duration) + .expect("overflow subtracting duration from date") + } +} + +impl Sub for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, duration: StdDuration) -> Self::Output { + self.checked_sub_std(duration) + .expect("overflow subtracting duration from date") + } +} + +impl_sub_assign!(Date: Duration, StdDuration); + +impl Sub for Date { + type Output = Duration; + + fn sub(self, other: Self) -> Self::Output { + Duration::days((self.to_julian_day() - other.to_julian_day()).extend()) + } +} diff --git a/vendor/time/src/duration.rs b/vendor/time/src/duration.rs new file mode 100644 index 00000000..b86bfe7c --- /dev/null +++ b/vendor/time/src/duration.rs @@ -0,0 +1,1598 @@ +//! The [`Duration`] struct and its associated `impl`s. + +use core::cmp::Ordering; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; +use core::time::Duration as StdDuration; +#[cfg(feature = "std")] +use std::time::SystemTime; + +use deranged::RangedI32; +use num_conv::prelude::*; + +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")] +#[allow(deprecated)] +use crate::Instant; + +/// By explicitly inserting this enum where padding is expected, the compiler is able to better +/// perform niche value optimization. +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) enum Padding { + #[allow(clippy::missing_docs_in_private_items)] + 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. +/// +/// Each `Duration` is composed of a whole number of seconds and a fractional part represented in +/// nanoseconds. +/// +/// This implementation allows for negative durations, unlike [`core::time::Duration`]. +#[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. + // Sign must match that of `seconds` (though this is not a safety requirement). + nanoseconds: Nanoseconds, + padding: Padding, +} + +impl fmt::Debug for Duration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Duration") + .field("seconds", &self.seconds) + .field("nanoseconds", &self.nanoseconds) + .finish() + } +} + +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: +/// +/// 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 { + ( + secs = $secs: expr, + mantissa_bits = $mant_bits: literal, + exponent_bits = $exp_bits: literal, + offset = $offset: literal, + bits_ty = $bits_ty:ty, + bits_ty_signed = $bits_ty_signed:ty, + double_ty = $double_ty:ty, + float_ty = $float_ty:ty, + is_nan = $is_nan:expr, + is_overflow = $is_overflow:expr, + ) => {{ + 'value: { + const MIN_EXP: i16 = 1 - (1i16 << $exp_bits) / 2; + const MANT_MASK: $bits_ty = (1 << $mant_bits) - 1; + const EXP_MASK: $bits_ty = (1 << $exp_bits) - 1; + + // Change from std: No error check for negative values necessary. + + let bits = $secs.to_bits(); + let mant = (bits & MANT_MASK) | (MANT_MASK + 1); + let exp = ((bits >> $mant_bits) & EXP_MASK) as i16 + MIN_EXP; + + let (secs, nanos) = if exp < -31 { + // the input represents less than 1ns and can not be rounded to it + (0u64, 0u32) + } else if exp < 0 { + // 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 = (nanos_tmp >> nanos_offset) as u32; + + let rem_mask = (1 << nanos_offset) - 1; + let rem_msb_mask = 1 << (nanos_offset - 1); + let rem = nanos_tmp & rem_mask; + let is_tie = rem == rem_msb_mask; + let is_even = (nanos & 1) == 0; + let rem_msb = nanos_tmp & rem_msb_mask == 0; + let add_ns = !(rem_msb || (is_even && is_tie)); + + // 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)) { + (0, nanos) + } else { + (1, 0) + } + } else if exp < $mant_bits { + 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 = (nanos_tmp >> nanos_offset) as u32; + + let rem_mask = (1 << nanos_offset) - 1; + let rem_msb_mask = 1 << (nanos_offset - 1); + let rem = nanos_tmp & rem_mask; + let is_tie = rem == rem_msb_mask; + let is_even = (nanos & 1) == 0; + let rem_msb = nanos_tmp & rem_msb_mask == 0; + let add_ns = !(rem_msb || (is_even && is_tie)); + + // f32 does not have enough precision to trigger the second branch. + // For example, it can not represent numbers between 1.999_999_880... + // 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)) { + (secs, nanos) + } else { + (secs + 1, 0) + } + } else if exp < 63 { + // Change from std: The exponent here is 63 instead of 64, + // because i64::MAX + 1 is 2^63. + + // the input has no fractional part + let secs = u64::from(mant) << (exp - $mant_bits); + (secs, 0) + } else if bits == (i64::MIN as $float_ty).to_bits() { + // Change from std: Signed integers are asymmetrical in that + // iN::MIN is -iN::MAX - 1. So for example i8 covers the + // 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_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>()); + } else if $secs.is_nan() { + // Change from std: std doesn't differentiate between the error + // cases. + $is_nan + } else { + $is_overflow + }; + + // Change from std: All the code is mostly unmodified in that it + // simply calculates an unsigned integer. Here we extract the sign + // bit and assign it to the number. We basically manually do two's + // complement here, we could also use an if and just negate the + // numbers based on the sign, but it turns out to be quite a bit + // slower. + let mask = (bits as $bits_ty_signed) >> ($mant_bits + $exp_bits); + #[allow(trivial_numeric_casts)] + 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); + // Safety: `nanos_signed` is in range. + unsafe { Self::new_unchecked(secs_signed, nanos_signed) } + } + }}; +} + +impl Duration { + /// Equivalent to `0.seconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::ZERO, 0.seconds()); + /// ``` + pub const ZERO: Self = Self::seconds(0); + + /// Equivalent to `1.nanoseconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::NANOSECOND, 1.nanoseconds()); + /// ``` + pub const NANOSECOND: Self = Self::nanoseconds(1); + + /// Equivalent to `1.microseconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::MICROSECOND, 1.microseconds()); + /// ``` + pub const MICROSECOND: Self = Self::microseconds(1); + + /// Equivalent to `1.milliseconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::MILLISECOND, 1.milliseconds()); + /// ``` + pub const MILLISECOND: Self = Self::milliseconds(1); + + /// Equivalent to `1.seconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::SECOND, 1.seconds()); + /// ``` + pub const SECOND: Self = Self::seconds(1); + + /// Equivalent to `1.minutes()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::MINUTE, 1.minutes()); + /// ``` + pub const MINUTE: Self = Self::minutes(1); + + /// Equivalent to `1.hours()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::HOUR, 1.hours()); + /// ``` + pub const HOUR: Self = Self::hours(1); + + /// Equivalent to `1.days()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::DAY, 1.days()); + /// ``` + pub const DAY: Self = Self::days(1); + + /// Equivalent to `1.weeks()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::WEEK, 1.weeks()); + /// ``` + 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_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_ranged(i64::MAX, Nanoseconds::MAX); + + /// Check if a duration is exactly zero. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert!(0.seconds().is_zero()); + /// assert!(!1.nanoseconds().is_zero()); + /// ``` + pub const fn is_zero(self) -> bool { + self.seconds == 0 && self.nanoseconds.get() == 0 + } + + /// Check if a duration is negative. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert!((-1).seconds().is_negative()); + /// assert!(!0.seconds().is_negative()); + /// assert!(!1.seconds().is_negative()); + /// ``` + pub const fn is_negative(self) -> bool { + self.seconds < 0 || self.nanoseconds.get() < 0 + } + + /// Check if a duration is positive. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert!(1.seconds().is_positive()); + /// assert!(!0.seconds().is_positive()); + /// assert!(!(-1).seconds().is_positive()); + /// ``` + pub const fn is_positive(self) -> bool { + self.seconds > 0 || self.nanoseconds.get() > 0 + } + + /// Get the absolute value of the duration. + /// + /// This method saturates the returned value if it would otherwise overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().abs(), 1.seconds()); + /// assert_eq!(0.seconds().abs(), 0.seconds()); + /// assert_eq!((-1).seconds().abs(), 1.seconds()); + /// ``` + pub const fn abs(self) -> Self { + 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 + /// [`std::time::Duration`] and does not saturate the returned value (unlike [`Duration::abs`]). + /// + /// ```rust + /// # use time::ext::{NumericalDuration, NumericalStdDuration}; + /// assert_eq!(1.seconds().unsigned_abs(), 1.std_seconds()); + /// assert_eq!(0.seconds().unsigned_abs(), 0.std_seconds()); + /// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds()); + /// ``` + pub const fn unsigned_abs(self) -> StdDuration { + StdDuration::new( + self.seconds.unsigned_abs(), + self.nanoseconds.get().unsigned_abs(), + ) + } + + /// Create a new `Duration` without checking the validity of the components. + /// + /// # 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.get() <= 0); + } else if seconds > 0 { + debug_assert!(nanoseconds.get() >= 0); + } + + Self { + seconds, + nanoseconds, + padding: Padding::Optimize, + } + } + + /// Create a new `Duration` with the provided seconds and nanoseconds. If nanoseconds is at + /// least ±109, it will wrap to the number of seconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::new(1, 0), 1.seconds()); + /// assert_eq!(Duration::new(-1, 0), (-1).seconds()); + /// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds()); + /// ``` + /// + /// # Panics + /// + /// This may panic if an overflow occurs. + 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), + "overflow constructing `time::Duration`" + ); + 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; + } else if seconds < 0 && nanoseconds > 0 { + // `seconds` cannot overflow here because it is negative. + seconds += 1; + 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_ranged_unchecked(seconds, nanoseconds) + } + + /// Create a new `Duration` with the given number of weeks. Equivalent to + /// `Duration::seconds(weeks * 604_800)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::weeks(1), 604_800.seconds()); + /// ``` + /// + /// # Panics + /// + /// This may panic if an overflow occurs. + pub const fn weeks(weeks: i64) -> Self { + Self::seconds(expect_opt!( + weeks.checked_mul(Second::per(Week) as i64), + "overflow constructing `time::Duration`" + )) + } + + /// Create a new `Duration` with the given number of days. Equivalent to + /// `Duration::seconds(days * 86_400)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::days(1), 86_400.seconds()); + /// ``` + /// + /// # Panics + /// + /// This may panic if an overflow occurs. + pub const fn days(days: i64) -> Self { + Self::seconds(expect_opt!( + days.checked_mul(Second::per(Day) as i64), + "overflow constructing `time::Duration`" + )) + } + + /// Create a new `Duration` with the given number of hours. Equivalent to + /// `Duration::seconds(hours * 3_600)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::hours(1), 3_600.seconds()); + /// ``` + /// + /// # Panics + /// + /// This may panic if an overflow occurs. + pub const fn hours(hours: i64) -> Self { + Self::seconds(expect_opt!( + hours.checked_mul(Second::per(Hour) as i64), + "overflow constructing `time::Duration`" + )) + } + + /// Create a new `Duration` with the given number of minutes. Equivalent to + /// `Duration::seconds(minutes * 60)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::minutes(1), 60.seconds()); + /// ``` + /// + /// # Panics + /// + /// This may panic if an overflow occurs. + pub const fn minutes(minutes: i64) -> Self { + Self::seconds(expect_opt!( + minutes.checked_mul(Second::per(Minute) as i64), + "overflow constructing `time::Duration`" + )) + } + + /// Create a new `Duration` with the given number of seconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds(1), 1_000.milliseconds()); + /// ``` + pub const fn seconds(seconds: i64) -> Self { + Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>()) + } + + /// Creates a new `Duration` from the specified number of seconds represented as `f64`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds()); + /// assert_eq!(Duration::seconds_f64(-0.5), (-0.5).seconds()); + /// ``` + pub fn seconds_f64(seconds: f64) -> Self { + try_from_secs!( + secs = seconds, + mantissa_bits = 52, + exponent_bits = 11, + offset = 44, + bits_ty = u64, + bits_ty_signed = i64, + double_ty = u128, + float_ty = f64, + is_nan = crate::expect_failed("passed NaN to `time::Duration::seconds_f64`"), + is_overflow = crate::expect_failed("overflow constructing `time::Duration`"), + ) + } + + /// Creates a new `Duration` from the specified number of seconds represented as `f32`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds_f32(0.5), 0.5.seconds()); + /// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds()); + /// ``` + pub fn seconds_f32(seconds: f32) -> Self { + try_from_secs!( + secs = seconds, + mantissa_bits = 23, + exponent_bits = 8, + offset = 41, + bits_ty = u32, + bits_ty_signed = i32, + double_ty = u64, + float_ty = f32, + is_nan = crate::expect_failed("passed NaN to `time::Duration::seconds_f32`"), + is_overflow = crate::expect_failed("overflow constructing `time::Duration`"), + ) + } + + /// Creates a new `Duration` from the specified number of seconds + /// represented as `f64`. Any values that are out of bounds are saturated at + /// the minimum or maximum respectively. `NaN` gets turned into a `Duration` + /// of 0 seconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::saturating_seconds_f64(0.5), 0.5.seconds()); + /// assert_eq!(Duration::saturating_seconds_f64(-0.5), (-0.5).seconds()); + /// assert_eq!( + /// Duration::saturating_seconds_f64(f64::NAN), + /// Duration::new(0, 0), + /// ); + /// assert_eq!( + /// Duration::saturating_seconds_f64(f64::NEG_INFINITY), + /// Duration::MIN, + /// ); + /// assert_eq!( + /// Duration::saturating_seconds_f64(f64::INFINITY), + /// Duration::MAX, + /// ); + /// ``` + pub fn saturating_seconds_f64(seconds: f64) -> Self { + try_from_secs!( + secs = seconds, + mantissa_bits = 52, + exponent_bits = 11, + offset = 44, + bits_ty = u64, + bits_ty_signed = i64, + double_ty = u128, + float_ty = f64, + is_nan = return Self::ZERO, + is_overflow = return if seconds < 0.0 { Self::MIN } else { Self::MAX }, + ) + } + + /// Creates a new `Duration` from the specified number of seconds + /// represented as `f32`. Any values that are out of bounds are saturated at + /// the minimum or maximum respectively. `NaN` gets turned into a `Duration` + /// of 0 seconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::saturating_seconds_f32(0.5), 0.5.seconds()); + /// assert_eq!(Duration::saturating_seconds_f32(-0.5), (-0.5).seconds()); + /// assert_eq!( + /// Duration::saturating_seconds_f32(f32::NAN), + /// Duration::new(0, 0), + /// ); + /// assert_eq!( + /// Duration::saturating_seconds_f32(f32::NEG_INFINITY), + /// Duration::MIN, + /// ); + /// assert_eq!( + /// Duration::saturating_seconds_f32(f32::INFINITY), + /// Duration::MAX, + /// ); + /// ``` + pub fn saturating_seconds_f32(seconds: f32) -> Self { + try_from_secs!( + secs = seconds, + mantissa_bits = 23, + exponent_bits = 8, + offset = 41, + bits_ty = u32, + bits_ty_signed = i32, + double_ty = u64, + float_ty = f32, + is_nan = return Self::ZERO, + is_overflow = return if seconds < 0.0 { Self::MIN } else { Self::MAX }, + ) + } + + /// Creates a new `Duration` from the specified number of seconds + /// represented as `f64`. Returns `None` if the `Duration` can't be + /// represented. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::checked_seconds_f64(0.5), Some(0.5.seconds())); + /// assert_eq!(Duration::checked_seconds_f64(-0.5), Some((-0.5).seconds())); + /// assert_eq!(Duration::checked_seconds_f64(f64::NAN), None); + /// assert_eq!(Duration::checked_seconds_f64(f64::NEG_INFINITY), None); + /// assert_eq!(Duration::checked_seconds_f64(f64::INFINITY), None); + /// ``` + pub fn checked_seconds_f64(seconds: f64) -> Option { + Some(try_from_secs!( + secs = seconds, + mantissa_bits = 52, + exponent_bits = 11, + offset = 44, + bits_ty = u64, + bits_ty_signed = i64, + double_ty = u128, + float_ty = f64, + is_nan = return None, + is_overflow = return None, + )) + } + + /// Creates a new `Duration` from the specified number of seconds + /// represented as `f32`. Returns `None` if the `Duration` can't be + /// represented. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::checked_seconds_f32(0.5), Some(0.5.seconds())); + /// assert_eq!(Duration::checked_seconds_f32(-0.5), Some((-0.5).seconds())); + /// assert_eq!(Duration::checked_seconds_f32(f32::NAN), None); + /// assert_eq!(Duration::checked_seconds_f32(f32::NEG_INFINITY), None); + /// assert_eq!(Duration::checked_seconds_f32(f32::INFINITY), None); + /// ``` + pub fn checked_seconds_f32(seconds: f32) -> Option { + Some(try_from_secs!( + secs = seconds, + mantissa_bits = 23, + exponent_bits = 8, + offset = 41, + bits_ty = u32, + bits_ty_signed = i32, + double_ty = u64, + float_ty = f32, + is_nan = return None, + is_overflow = return None, + )) + } + + /// Create a new `Duration` with the given number of milliseconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::milliseconds(1), 1_000.microseconds()); + /// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds()); + /// ``` + pub const fn milliseconds(milliseconds: i64) -> Self { + // 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 i32, + ) + } + } + + /// Create a new `Duration` with the given number of microseconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::microseconds(1), 1_000.nanoseconds()); + /// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds()); + /// ``` + pub const fn microseconds(microseconds: i64) -> Self { + // 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 i32, + ) + } + } + + /// Create a new `Duration` with the given number of nanoseconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::nanoseconds(1), 1.microseconds() / 1_000); + /// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000); + /// ``` + pub const fn nanoseconds(nanoseconds: i64) -> Self { + // 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 i32, + ) + } + } + + /// Create a new `Duration` with the given number of nanoseconds. + /// + /// 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; + + if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 { + crate::expect_failed("overflow constructing `time::Duration`"); + } + + // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. + unsafe { Self::new_unchecked(seconds as i64, nanoseconds as i32) } + } + + /// Get the number of whole weeks in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.weeks().whole_weeks(), 1); + /// assert_eq!((-1).weeks().whole_weeks(), -1); + /// assert_eq!(6.days().whole_weeks(), 0); + /// assert_eq!((-6).days().whole_weeks(), 0); + /// ``` + pub const fn whole_weeks(self) -> i64 { + self.whole_seconds() / Second::per(Week) as i64 + } + + /// Get the number of whole days in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.days().whole_days(), 1); + /// assert_eq!((-1).days().whole_days(), -1); + /// assert_eq!(23.hours().whole_days(), 0); + /// assert_eq!((-23).hours().whole_days(), 0); + /// ``` + pub const fn whole_days(self) -> i64 { + self.whole_seconds() / Second::per(Day) as i64 + } + + /// Get the number of whole hours in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.hours().whole_hours(), 1); + /// assert_eq!((-1).hours().whole_hours(), -1); + /// assert_eq!(59.minutes().whole_hours(), 0); + /// assert_eq!((-59).minutes().whole_hours(), 0); + /// ``` + pub const fn whole_hours(self) -> i64 { + self.whole_seconds() / Second::per(Hour) as i64 + } + + /// Get the number of whole minutes in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.minutes().whole_minutes(), 1); + /// assert_eq!((-1).minutes().whole_minutes(), -1); + /// assert_eq!(59.seconds().whole_minutes(), 0); + /// assert_eq!((-59).seconds().whole_minutes(), 0); + /// ``` + pub const fn whole_minutes(self) -> i64 { + self.whole_seconds() / Second::per(Minute) as i64 + } + + /// Get the number of whole seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().whole_seconds(), 1); + /// assert_eq!((-1).seconds().whole_seconds(), -1); + /// assert_eq!(1.minutes().whole_seconds(), 60); + /// assert_eq!((-1).minutes().whole_seconds(), -60); + /// ``` + pub const fn whole_seconds(self) -> i64 { + self.seconds + } + + /// Get the number of fractional seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.5.seconds().as_seconds_f64(), 1.5); + /// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5); + /// ``` + pub fn as_seconds_f64(self) -> f64 { + self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per(Second) as f64 + } + + /// Get the number of fractional seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.5.seconds().as_seconds_f32(), 1.5); + /// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5); + /// ``` + pub fn as_seconds_f32(self) -> f32 { + self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per(Second) as f32 + } + + /// Get the number of whole milliseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().whole_milliseconds(), 1_000); + /// assert_eq!((-1).seconds().whole_milliseconds(), -1_000); + /// assert_eq!(1.milliseconds().whole_milliseconds(), 1); + /// 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.get() as i128 / Nanosecond::per(Millisecond) as i128 + } + + /// Get the number of milliseconds past the number of whole seconds. + /// + /// Always in the range `-999..=999`. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.4.seconds().subsec_milliseconds(), 400); + /// assert_eq!((-1.4).seconds().subsec_milliseconds(), -400); + /// ``` + // Allow the lint, as the value is guaranteed to be less than 1000. + pub const fn subsec_milliseconds(self) -> i16 { + (self.nanoseconds.get() / Nanosecond::per(Millisecond) as i32) as i16 + } + + /// Get the number of whole microseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.milliseconds().whole_microseconds(), 1_000); + /// assert_eq!((-1).milliseconds().whole_microseconds(), -1_000); + /// assert_eq!(1.microseconds().whole_microseconds(), 1); + /// 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.get() as i128 / Nanosecond::per(Microsecond) as i128 + } + + /// Get the number of microseconds past the number of whole seconds. + /// + /// Always in the range `-999_999..=999_999`. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.0004.seconds().subsec_microseconds(), 400); + /// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400); + /// ``` + pub const fn subsec_microseconds(self) -> i32 { + self.nanoseconds.get() / Nanosecond::per(Microsecond) as i32 + } + + /// Get the number of nanoseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.microseconds().whole_nanoseconds(), 1_000); + /// assert_eq!((-1).microseconds().whole_nanoseconds(), -1_000); + /// assert_eq!(1.nanoseconds().whole_nanoseconds(), 1); + /// 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.get() as i128 + } + + /// Get the number of nanoseconds past the number of whole seconds. + /// + /// The returned value will always be in the range `-999_999_999..=999_999_999`. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.000_000_400.seconds().subsec_nanoseconds(), 400); + /// 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 + } + + /// Computes `self + rhs`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().checked_add(5.seconds()), Some(10.seconds())); + /// assert_eq!(Duration::MAX.checked_add(1.nanoseconds()), None); + /// assert_eq!((-5).seconds().checked_add(5.seconds()), Some(0.seconds())); + /// ``` + pub const fn checked_add(self, rhs: Self) -> Option { + let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds)); + let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get(); + + if nanoseconds >= Nanosecond::per(Second) as i32 || 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 + { + nanoseconds += Nanosecond::per(Second) as i32; + seconds = const_try_opt!(seconds.checked_sub(1)); + } + + // 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. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().checked_sub(5.seconds()), Some(Duration::ZERO)); + /// assert_eq!(Duration::MIN.checked_sub(1.nanoseconds()), None); + /// assert_eq!(5.seconds().checked_sub(10.seconds()), Some((-5).seconds())); + /// ``` + pub const fn checked_sub(self, rhs: Self) -> Option { + let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds)); + let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); + + if nanoseconds >= Nanosecond::per(Second) as i32 || 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 + { + nanoseconds += Nanosecond::per(Second) as i32; + seconds = const_try_opt!(seconds.checked_sub(1)); + } + + // 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. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().checked_mul(2), Some(10.seconds())); + /// assert_eq!(5.seconds().checked_mul(-2), Some((-10).seconds())); + /// assert_eq!(5.seconds().checked_mul(0), Some(0.seconds())); + /// assert_eq!(Duration::MAX.checked_mul(2), None); + /// assert_eq!(Duration::MIN.checked_mul(2), None); + /// ``` + pub const fn checked_mul(self, rhs: i32) -> Option { + // Multiply nanoseconds as i64, because it cannot overflow that way. + 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 i32; + let seconds = const_try_opt!( + const_try_opt!(self.seconds.checked_mul(rhs as i64)).checked_add(extra_secs) + ); + + // 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. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(10.seconds().checked_div(2), Some(5.seconds())); + /// assert_eq!(10.seconds().checked_div(-2), Some((-5).seconds())); + /// assert_eq!(1.seconds().checked_div(0), None); + /// ``` + pub const fn checked_div(self, rhs: i32) -> Option { + 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; + + // Safety: `nanoseconds` is in range. + unsafe { Some(Self::new_unchecked(secs, nanos)) } + } + + /// Computes `-self`, returning `None` if the result would overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time::Duration; + /// assert_eq!(5.seconds().checked_neg(), Some((-5).seconds())); + /// assert_eq!(Duration::MIN.checked_neg(), None); + /// ``` + pub const fn checked_neg(self) -> Option { + if self.seconds == i64::MIN { + None + } else { + Some(Self::new_ranged_unchecked( + -self.seconds, + self.nanoseconds.neg(), + )) + } + } + + /// Computes `self + rhs`, saturating if an overflow occurred. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().saturating_add(5.seconds()), 10.seconds()); + /// assert_eq!(Duration::MAX.saturating_add(1.nanoseconds()), Duration::MAX); + /// assert_eq!( + /// Duration::MIN.saturating_add((-1).nanoseconds()), + /// Duration::MIN + /// ); + /// assert_eq!((-5).seconds().saturating_add(5.seconds()), Duration::ZERO); + /// ``` + pub const fn saturating_add(self, rhs: Self) -> Self { + let (mut seconds, overflow) = self.seconds.overflowing_add(rhs.seconds); + if overflow { + if self.seconds > 0 { + return Self::MAX; + } + return Self::MIN; + } + let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get(); + + if nanoseconds >= Nanosecond::per(Second) as i32 || 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 + { + nanoseconds += Nanosecond::per(Second) as i32; + seconds = match seconds.checked_sub(1) { + Some(seconds) => seconds, + None => return Self::MIN, + }; + } + + // 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. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().saturating_sub(5.seconds()), Duration::ZERO); + /// assert_eq!(Duration::MIN.saturating_sub(1.nanoseconds()), Duration::MIN); + /// assert_eq!( + /// Duration::MAX.saturating_sub((-1).nanoseconds()), + /// Duration::MAX + /// ); + /// assert_eq!(5.seconds().saturating_sub(10.seconds()), (-5).seconds()); + /// ``` + pub const fn saturating_sub(self, rhs: Self) -> Self { + let (mut seconds, overflow) = self.seconds.overflowing_sub(rhs.seconds); + if overflow { + if self.seconds > 0 { + return Self::MAX; + } + return Self::MIN; + } + let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); + + if nanoseconds >= Nanosecond::per(Second) as i32 || 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 + { + nanoseconds += Nanosecond::per(Second) as i32; + seconds = match seconds.checked_sub(1) { + Some(seconds) => seconds, + None => return Self::MIN, + }; + } + + // 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. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().saturating_mul(2), 10.seconds()); + /// assert_eq!(5.seconds().saturating_mul(-2), (-10).seconds()); + /// assert_eq!(5.seconds().saturating_mul(0), Duration::ZERO); + /// assert_eq!(Duration::MAX.saturating_mul(2), Duration::MAX); + /// assert_eq!(Duration::MIN.saturating_mul(2), Duration::MIN); + /// assert_eq!(Duration::MAX.saturating_mul(-2), Duration::MIN); + /// assert_eq!(Duration::MIN.saturating_mul(-2), Duration::MAX); + /// ``` + pub const fn saturating_mul(self, rhs: i32) -> Self { + // Multiply nanoseconds as i64, because it cannot overflow that way. + 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 i32; + let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as i64); + if overflow1 { + if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 { + return Self::MAX; + } + return Self::MIN; + } + let (seconds, overflow2) = seconds.overflowing_add(extra_secs); + if overflow2 { + if self.seconds > 0 && rhs > 0 { + return Self::MAX; + } + return Self::MIN; + } + + // Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above. + unsafe { Self::new_unchecked(seconds, nanoseconds) } + } + + /// Runs a closure, returning the duration of time it took to run. The return value of the + /// closure is provided in the second part of the tuple. + #[doc(hidden)] + #[cfg(feature = "std")] + #[deprecated( + since = "0.3.32", + note = "extremely limited use case, not intended for benchmarking" + )] + #[allow(deprecated)] + pub fn time_fn(f: impl FnOnce() -> T) -> (Self, T) { + let start = Instant::now(); + let return_value = f(); + let end = Instant::now(); + + (end - start, return_value) + } +} + +/// The format returned by this implementation is not stable and must not be relied upon. +/// +/// By default this produces an exact, full-precision printout of the duration. +/// For a concise, rounded printout instead, you can use the `.N` format specifier: +/// +/// ``` +/// # use time::Duration; +/// # +/// let duration = Duration::new(123456, 789011223); +/// println!("{duration:.3}"); +/// ``` +/// +/// For the purposes of this implementation, a day is exactly 24 hours and a minute is exactly 60 +/// seconds. +impl fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_negative() { + f.write_str("-")?; + } + + if let Some(_precision) = f.precision() { + // Concise, rounded representation. + + if self.is_zero() { + // Write a zero value with the requested precision. + return (0.).fmt(f).and_then(|_| f.write_str("s")); + } + + /// Format the first item that produces a value greater than 1 and then break. + macro_rules! item { + ($name:literal, $value:expr) => { + let value = $value; + if value >= 1.0 { + return value.fmt(f).and_then(|_| f.write_str($name)); + } + }; + } + + // 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!("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); + } else { + // Precise, but verbose representation. + + if self.is_zero() { + return f.write_str("0s"); + } + + /// Format a single item. + macro_rules! item { + ($name:literal, $value:expr) => { + match $value { + 0 => Ok(()), + value => value.fmt(f).and_then(|_| f.write_str($name)), + } + }; + } + + let seconds = self.seconds.unsigned_abs(); + let nanoseconds = self.nanoseconds.get().unsigned_abs(); + + item!("d", seconds / Second::per(Day).extend::())?; + item!( + "h", + seconds / Second::per(Hour).extend::() % Hour::per(Day).extend::() + )?; + item!( + "m", + seconds / Second::per(Minute).extend::() % Minute::per(Hour).extend::() + )?; + item!("s", seconds % Second::per(Minute).extend::())?; + item!("ms", nanoseconds / Nanosecond::per(Millisecond))?; + item!( + "µs", + nanoseconds / Nanosecond::per(Microsecond).extend::() + % Microsecond::per(Millisecond).extend::() + )?; + item!( + "ns", + nanoseconds % Nanosecond::per(Microsecond).extend::() + )?; + } + + Ok(()) + } +} + +impl TryFrom for Duration { + type Error = error::ConversionRange; + + fn try_from(original: StdDuration) -> Result { + Ok(Self::new( + original + .as_secs() + .try_into() + .map_err(|_| error::ConversionRange)?, + original.subsec_nanos().cast_signed(), + )) + } +} + +impl TryFrom for StdDuration { + type Error = error::ConversionRange; + + fn try_from(duration: Duration) -> Result { + Ok(Self::new( + duration + .seconds + .try_into() + .map_err(|_| error::ConversionRange)?, + duration + .nanoseconds + .get() + .try_into() + .map_err(|_| error::ConversionRange)?, + )) + } +} + +impl Add for Duration { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, rhs: Self) -> Self::Output { + self.checked_add(rhs) + .expect("overflow when adding durations") + } +} + +impl Add for Duration { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, std_duration: StdDuration) -> Self::Output { + self + Self::try_from(std_duration) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + } +} + +impl Add for StdDuration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Self::Output { + rhs + self + } +} + +impl_add_assign!(Duration: Self, StdDuration); + +impl AddAssign for StdDuration { + /// # Panics + /// + /// This may panic if the resulting addition cannot be represented. + fn add_assign(&mut self, rhs: Duration) { + *self = (*self + rhs).try_into().expect( + "Cannot represent a resulting duration in std. Try `let x = x + rhs;`, which will \ + change the type.", + ); + } +} + +impl Neg for Duration { + type Output = Self; + + fn neg(self) -> Self::Output { + self.checked_neg().expect("overflow when negating duration") + } +} + +impl Sub for Duration { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: Self) -> Self::Output { + self.checked_sub(rhs) + .expect("overflow when subtracting durations") + } +} + +impl Sub for Duration { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: StdDuration) -> Self::Output { + self - Self::try_from(rhs) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + } +} + +impl Sub for StdDuration { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: Duration) -> Self::Output { + Duration::try_from(self) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + - rhs + } +} + +impl_sub_assign!(Duration: Self, StdDuration); + +impl SubAssign for StdDuration { + /// # Panics + /// + /// This may panic if the resulting subtraction can not be represented. + fn sub_assign(&mut self, rhs: Duration) { + *self = (*self - rhs).try_into().expect( + "Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \ + change the type.", + ); + } +} + +/// Implement `Mul` (reflexively) and `Div` for `Duration` for various types. +macro_rules! duration_mul_div_int { + ($($type:ty),+) => {$( + impl Mul<$type> for Duration { + type Output = Self; + + fn mul(self, rhs: $type) -> Self::Output { + Self::nanoseconds_i128( + self.whole_nanoseconds() + .checked_mul(rhs.cast_signed().extend::()) + .expect("overflow when multiplying duration") + ) + } + } + + impl Mul for $type { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } + } + + impl Div<$type> for Duration { + type Output = Self; + + fn div(self, rhs: $type) -> Self::Output { + Self::nanoseconds_i128( + self.whole_nanoseconds() / rhs.cast_signed().extend::() + ) + } + } + )+}; +} +duration_mul_div_int![i8, i16, i32, u8, u16, u32]; + +impl Mul for Duration { + type Output = Self; + + fn mul(self, rhs: f32) -> Self::Output { + Self::seconds_f32(self.as_seconds_f32() * rhs) + } +} + +impl Mul for f32 { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } +} + +impl Mul for Duration { + type Output = Self; + + fn mul(self, rhs: f64) -> Self::Output { + Self::seconds_f64(self.as_seconds_f64() * rhs) + } +} + +impl Mul for f64 { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } +} + +impl_mul_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); + +impl Div for Duration { + type Output = Self; + + fn div(self, rhs: f32) -> Self::Output { + Self::seconds_f32(self.as_seconds_f32() / rhs) + } +} + +impl Div for Duration { + type Output = Self; + + fn div(self, rhs: f64) -> Self::Output { + Self::seconds_f64(self.as_seconds_f64() / rhs) + } +} + +impl_div_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); + +impl Div for Duration { + type Output = f64; + + fn div(self, rhs: Self) -> Self::Output { + self.as_seconds_f64() / rhs.as_seconds_f64() + } +} + +impl Div for Duration { + type Output = f64; + + fn div(self, rhs: StdDuration) -> Self::Output { + self.as_seconds_f64() / rhs.as_secs_f64() + } +} + +impl Div for StdDuration { + type Output = f64; + + fn div(self, rhs: Duration) -> Self::Output { + self.as_secs_f64() / rhs.as_seconds_f64() + } +} + +impl PartialEq for Duration { + fn eq(&self, rhs: &StdDuration) -> bool { + Ok(*self) == Self::try_from(*rhs) + } +} + +impl PartialEq for StdDuration { + fn eq(&self, rhs: &Duration) -> bool { + rhs == self + } +} + +impl PartialOrd for Duration { + fn partial_cmp(&self, rhs: &StdDuration) -> Option { + if rhs.as_secs() > i64::MAX.cast_unsigned() { + return Some(Ordering::Less); + } + + Some( + self.seconds + .cmp(&rhs.as_secs().cast_signed()) + .then_with(|| { + self.nanoseconds + .get() + .cmp(&rhs.subsec_nanos().cast_signed()) + }), + ) + } +} + +impl PartialOrd for StdDuration { + fn partial_cmp(&self, rhs: &Duration) -> Option { + rhs.partial_cmp(self).map(Ordering::reverse) + } +} + +impl Sum for Duration { + fn sum>(iter: I) -> Self { + iter.reduce(|a, b| a + b).unwrap_or_default() + } +} + +impl<'a> Sum<&'a Self> for Duration { + fn sum>(iter: I) -> Self { + iter.copied().sum() + } +} + +#[cfg(feature = "std")] +impl Add for SystemTime { + type Output = Self; + + fn add(self, duration: Duration) -> Self::Output { + if duration.is_zero() { + self + } else if duration.is_positive() { + self + duration.unsigned_abs() + } else { + debug_assert!(duration.is_negative()); + self - duration.unsigned_abs() + } + } +} + +impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration); + +#[cfg(feature = "std")] +impl Sub for SystemTime { + type Output = Self; + + fn sub(self, duration: Duration) -> Self::Output { + if duration.is_zero() { + self + } else if duration.is_positive() { + self - duration.unsigned_abs() + } else { + debug_assert!(duration.is_negative()); + self + duration.unsigned_abs() + } + } +} + +impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration); diff --git a/vendor/time/src/error/component_range.rs b/vendor/time/src/error/component_range.rs new file mode 100644 index 00000000..dc980234 --- /dev/null +++ b/vendor/time/src/error/component_range.rs @@ -0,0 +1,114 @@ +//! Component range error + +use core::{fmt, hash}; + +use crate::error; + +/// An error type indicating that a component provided to a method was out of range, causing a +/// failure. +// i64 is the narrowest type fitting all use cases. This eliminates the need for a type parameter. +#[derive(Debug, Clone, Copy, Eq)] +pub struct ComponentRange { + /// Name of the component. + pub(crate) name: &'static str, + /// Minimum allowed value, inclusive. + pub(crate) minimum: i64, + /// Maximum allowed value, inclusive. + pub(crate) maximum: i64, + /// Value that was provided. + pub(crate) value: i64, + /// The minimum and/or maximum value is conditional on the value of other + /// parameters. + pub(crate) conditional_message: Option<&'static str>, +} + +impl ComponentRange { + /// Obtain the name of the component whose value was out of range. + pub const fn name(self) -> &'static str { + self.name + } + + /// Whether the value's permitted range is conditional, i.e. whether an input with this + /// value could have succeeded if the values of other components were different. + pub const fn is_conditional(self) -> bool { + self.conditional_message.is_some() + } +} + +impl PartialEq for ComponentRange { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + && self.minimum == other.minimum + && self.maximum == other.maximum + && self.value == other.value + // Skip the contents of the message when comparing for equality. + && self.conditional_message.is_some() == other.conditional_message.is_some() + } +} + +impl hash::Hash for ComponentRange { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.minimum.hash(state); + self.maximum.hash(state); + self.value.hash(state); + // Skip the contents of the message when comparing for equality. + self.conditional_message.is_some().hash(state); + } +} + +impl fmt::Display for ComponentRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} must be in the range {}..={}", + self.name, self.minimum, self.maximum + )?; + + if let Some(message) = self.conditional_message { + write!(f, " {message}")?; + } + + Ok(()) + } +} + +impl From for crate::Error { + fn from(original: ComponentRange) -> Self { + Self::ComponentRange(original) + } +} + +impl TryFrom for ComponentRange { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::ComponentRange(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +/// **This trait implementation is deprecated and will be removed in a future breaking release.** +#[cfg(feature = "serde")] +impl serde::de::Expected for ComponentRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "a value in the range {}..={}", + self.minimum, self.maximum + ) + } +} + +#[cfg(feature = "serde")] +impl ComponentRange { + /// Convert the error to a deserialization error. + pub(crate) fn into_de_error(self) -> E { + E::invalid_value(serde::de::Unexpected::Signed(self.value), &self) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ComponentRange {} diff --git a/vendor/time/src/error/conversion_range.rs b/vendor/time/src/error/conversion_range.rs new file mode 100644 index 00000000..d6d9243e --- /dev/null +++ b/vendor/time/src/error/conversion_range.rs @@ -0,0 +1,36 @@ +//! Conversion range error + +use core::fmt; + +use crate::error; + +/// An error type indicating that a conversion failed because the target type could not store the +/// initial value. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ConversionRange; + +impl fmt::Display for ConversionRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Source value is out of range for the target type") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ConversionRange {} + +impl From for crate::Error { + fn from(err: ConversionRange) -> Self { + Self::ConversionRange(err) + } +} + +impl TryFrom for ConversionRange { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::ConversionRange(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/different_variant.rs b/vendor/time/src/error/different_variant.rs new file mode 100644 index 00000000..22e21cb0 --- /dev/null +++ b/vendor/time/src/error/different_variant.rs @@ -0,0 +1,34 @@ +//! Different variant error + +use core::fmt; + +/// An error type indicating that a [`TryFrom`](core::convert::TryFrom) call failed because the +/// original value was of a different variant. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DifferentVariant; + +impl fmt::Display for DifferentVariant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "value was of a different variant than required") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DifferentVariant {} + +impl From for crate::Error { + fn from(err: DifferentVariant) -> Self { + Self::DifferentVariant(err) + } +} + +impl TryFrom for DifferentVariant { + type Error = Self; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::DifferentVariant(err) => Ok(err), + _ => Err(Self), + } + } +} diff --git a/vendor/time/src/error/format.rs b/vendor/time/src/error/format.rs new file mode 100644 index 00000000..906a58c0 --- /dev/null +++ b/vendor/time/src/error/format.rs @@ -0,0 +1,113 @@ +//! Error formatting a struct + +use alloc::boxed::Box; +use core::fmt; +use std::io; + +use crate::error; + +/// An error occurred when formatting. +#[non_exhaustive] +#[derive(Debug)] +pub enum Format { + /// The type being formatted does not contain sufficient information to format a component. + #[non_exhaustive] + InsufficientTypeInformation, + /// The component named has a value that cannot be formatted into the requested format. + /// + /// This variant is only returned when using well-known formats. + InvalidComponent(&'static str), + /// A component provided was out of range. + ComponentRange(Box), + /// A value of `std::io::Error` was returned internally. + StdIo(io::Error), +} + +impl fmt::Display for Format { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsufficientTypeInformation => f.write_str( + "The type being formatted does not contain sufficient information to format a \ + component.", + ), + Self::InvalidComponent(component) => write!( + f, + "The {component} component cannot be formatted into the requested format." + ), + Self::ComponentRange(err) => err.fmt(f), + Self::StdIo(err) => err.fmt(f), + } + } +} + +impl From for Format { + fn from(err: error::ComponentRange) -> Self { + Self::ComponentRange(Box::new(err)) + } +} + +impl From for Format { + fn from(err: io::Error) -> Self { + Self::StdIo(err) + } +} + +impl TryFrom for error::ComponentRange { + type Error = error::DifferentVariant; + + fn try_from(err: Format) -> Result { + match err { + Format::ComponentRange(err) => Ok(*err), + _ => Err(error::DifferentVariant), + } + } +} + +impl TryFrom for io::Error { + type Error = error::DifferentVariant; + + fn try_from(err: Format) -> Result { + match err { + Format::StdIo(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Format { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InsufficientTypeInformation | Self::InvalidComponent(_) => None, + Self::ComponentRange(err) => Some(&**err), + Self::StdIo(err) => Some(err), + } + } +} + +impl From for crate::Error { + fn from(original: Format) -> Self { + Self::Format(original) + } +} + +impl TryFrom for Format { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::Format(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +#[cfg(feature = "serde")] +impl Format { + /// Obtain an error type for the serializer. + #[doc(hidden)] // Exposed only for the `declare_format_string` macro + pub fn into_invalid_serde_value(self) -> S::Error { + use serde::ser::Error; + S::Error::custom(self) + } +} diff --git a/vendor/time/src/error/indeterminate_offset.rs b/vendor/time/src/error/indeterminate_offset.rs new file mode 100644 index 00000000..d25d4164 --- /dev/null +++ b/vendor/time/src/error/indeterminate_offset.rs @@ -0,0 +1,35 @@ +//! Indeterminate offset + +use core::fmt; + +use crate::error; + +/// The system's UTC offset could not be determined at the given datetime. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IndeterminateOffset; + +impl fmt::Display for IndeterminateOffset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("The system's UTC offset could not be determined") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for IndeterminateOffset {} + +impl From for crate::Error { + fn from(err: IndeterminateOffset) -> Self { + Self::IndeterminateOffset(err) + } +} + +impl TryFrom for IndeterminateOffset { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::IndeterminateOffset(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/invalid_format_description.rs b/vendor/time/src/error/invalid_format_description.rs new file mode 100644 index 00000000..635fee75 --- /dev/null +++ b/vendor/time/src/error/invalid_format_description.rs @@ -0,0 +1,132 @@ +//! Invalid format description + +use alloc::string::String; +use core::fmt; + +use crate::error; + +/// The format description provided was not valid. +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InvalidFormatDescription { + /// There was a bracket pair that was opened but not closed. + #[non_exhaustive] + UnclosedOpeningBracket { + /// The zero-based index of the opening bracket. + index: usize, + }, + /// A component name is not valid. + #[non_exhaustive] + InvalidComponentName { + /// The name of the invalid component name. + name: String, + /// The zero-based index the component name starts at. + index: usize, + }, + /// A modifier is not valid. + #[non_exhaustive] + InvalidModifier { + /// The value of the invalid modifier. + value: String, + /// The zero-based index the modifier starts at. + index: usize, + }, + /// A component name is missing. + #[non_exhaustive] + MissingComponentName { + /// The zero-based index where the component name should start. + index: usize, + }, + /// A required modifier is missing. + #[non_exhaustive] + MissingRequiredModifier { + /// The name of the modifier that is missing. + name: &'static str, + /// The zero-based index of the component. + index: usize, + }, + /// Something was expected, but not found. + #[non_exhaustive] + Expected { + /// What was expected to be present, but wasn't. + what: &'static str, + /// The zero-based index the item was expected to be found at. + index: usize, + }, + /// Certain behavior is not supported in the given context. + #[non_exhaustive] + NotSupported { + /// The behavior that is not supported. + what: &'static str, + /// The context in which the behavior is not supported. + context: &'static str, + /// The zero-based index the error occurred at. + index: usize, + }, +} + +impl From for crate::Error { + fn from(original: InvalidFormatDescription) -> Self { + Self::InvalidFormatDescription(original) + } +} + +impl TryFrom for InvalidFormatDescription { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::InvalidFormatDescription(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +impl fmt::Display for InvalidFormatDescription { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InvalidFormatDescription::*; + match self { + UnclosedOpeningBracket { index } => { + write!(f, "unclosed opening bracket at byte index {index}") + } + InvalidComponentName { name, index } => { + write!(f, "invalid component name `{name}` at byte index {index}") + } + InvalidModifier { value, index } => { + write!(f, "invalid modifier `{value}` at byte index {index}") + } + MissingComponentName { index } => { + write!(f, "missing component name at byte index {index}") + } + MissingRequiredModifier { name, index } => { + write!( + f, + "missing required modifier `{name}` for component at byte index {index}" + ) + } + Expected { + what: expected, + index, + } => { + write!(f, "expected {expected} at byte index {index}") + } + NotSupported { + what, + context, + index, + } => { + if context.is_empty() { + write!(f, "{what} is not supported at byte index {index}") + } else { + write!( + f, + "{what} is not supported in {context} at byte index {index}" + ) + } + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidFormatDescription {} diff --git a/vendor/time/src/error/invalid_variant.rs b/vendor/time/src/error/invalid_variant.rs new file mode 100644 index 00000000..396a749a --- /dev/null +++ b/vendor/time/src/error/invalid_variant.rs @@ -0,0 +1,34 @@ +//! Invalid variant error + +use core::fmt; + +/// An error type indicating that a [`FromStr`](core::str::FromStr) call failed because the value +/// was not a valid variant. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InvalidVariant; + +impl fmt::Display for InvalidVariant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "value was not a valid variant") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidVariant {} + +impl From for crate::Error { + fn from(err: InvalidVariant) -> Self { + Self::InvalidVariant(err) + } +} + +impl TryFrom for InvalidVariant { + type Error = crate::error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::InvalidVariant(err) => Ok(err), + _ => Err(crate::error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/mod.rs b/vendor/time/src/error/mod.rs new file mode 100644 index 00000000..78b15380 --- /dev/null +++ b/vendor/time/src/error/mod.rs @@ -0,0 +1,131 @@ +//! Various error types returned by methods in the time crate. + +mod component_range; +mod conversion_range; +mod different_variant; +#[cfg(feature = "formatting")] +mod format; +#[cfg(feature = "local-offset")] +mod indeterminate_offset; +#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] +mod invalid_format_description; +mod invalid_variant; +#[cfg(feature = "parsing")] +mod parse; +#[cfg(feature = "parsing")] +mod parse_from_description; +#[cfg(feature = "parsing")] +mod try_from_parsed; + +#[cfg(feature = "parsing")] +use core::convert::Infallible; +use core::fmt; + +pub use component_range::ComponentRange; +pub use conversion_range::ConversionRange; +pub use different_variant::DifferentVariant; +#[cfg(feature = "formatting")] +pub use format::Format; +#[cfg(feature = "local-offset")] +pub use indeterminate_offset::IndeterminateOffset; +#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] +pub use invalid_format_description::InvalidFormatDescription; +pub use invalid_variant::InvalidVariant; +#[cfg(feature = "parsing")] +pub use parse::Parse; +#[cfg(feature = "parsing")] +pub use parse_from_description::ParseFromDescription; +#[cfg(feature = "parsing")] +pub use try_from_parsed::TryFromParsed; + +/// 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. +#[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 { + #[doc(hidden)] + never: Infallible, + }, + #[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), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ConversionRange(e) => e.fmt(f), + Self::ComponentRange(e) => e.fmt(f), + #[cfg(feature = "local-offset")] + Self::IndeterminateOffset(e) => e.fmt(f), + #[cfg(feature = "formatting")] + Self::Format(e) => e.fmt(f), + #[cfg(feature = "parsing")] + Self::ParseFromDescription(e) => e.fmt(f), + #[cfg(feature = "parsing")] + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters { never } => match *never {}, + #[cfg(feature = "parsing")] + Self::TryFromParsed(e) => e.fmt(f), + #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] + Self::InvalidFormatDescription(e) => e.fmt(f), + Self::DifferentVariant(e) => e.fmt(f), + Self::InvalidVariant(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::ConversionRange(err) => Some(err), + Self::ComponentRange(err) => Some(err), + #[cfg(feature = "local-offset")] + Self::IndeterminateOffset(err) => Some(err), + #[cfg(feature = "formatting")] + Self::Format(err) => Some(err), + #[cfg(feature = "parsing")] + Self::ParseFromDescription(err) => Some(err), + #[cfg(feature = "parsing")] + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters { never } => match *never {}, + #[cfg(feature = "parsing")] + Self::TryFromParsed(err) => Some(err), + #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] + Self::InvalidFormatDescription(err) => Some(err), + Self::DifferentVariant(err) => Some(err), + Self::InvalidVariant(err) => Some(err), + } + } +} diff --git a/vendor/time/src/error/parse.rs b/vendor/time/src/error/parse.rs new file mode 100644 index 00000000..ee3f87b7 --- /dev/null +++ b/vendor/time/src/error/parse.rs @@ -0,0 +1,109 @@ +//! Error that occurred at some stage of parsing + +use core::convert::Infallible; +use core::fmt; + +use crate::error::{self, ParseFromDescription, TryFromParsed}; + +/// An error that occurred at some stage of parsing. +#[non_exhaustive] +#[allow(variant_size_differences)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Parse { + #[allow(missing_docs)] + TryFromParsed(TryFromParsed), + #[allow(missing_docs)] + ParseFromDescription(ParseFromDescription), + #[allow(missing_docs)] + #[non_exhaustive] + #[deprecated( + since = "0.3.28", + note = "no longer output. moved to the `ParseFromDescription` variant" + )] + UnexpectedTrailingCharacters { + #[doc(hidden)] + never: Infallible, + }, +} + +impl fmt::Display for Parse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::TryFromParsed(err) => err.fmt(f), + Self::ParseFromDescription(err) => err.fmt(f), + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters { never } => match *never {}, + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Parse { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::TryFromParsed(err) => Some(err), + Self::ParseFromDescription(err) => Some(err), + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters { never } => match *never {}, + } + } +} + +impl From for Parse { + fn from(err: TryFromParsed) -> Self { + Self::TryFromParsed(err) + } +} + +impl TryFrom for TryFromParsed { + type Error = error::DifferentVariant; + + fn try_from(err: Parse) -> Result { + match err { + Parse::TryFromParsed(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +impl From for Parse { + fn from(err: ParseFromDescription) -> Self { + Self::ParseFromDescription(err) + } +} + +impl TryFrom for ParseFromDescription { + type Error = error::DifferentVariant; + + fn try_from(err: Parse) -> Result { + match err { + Parse::ParseFromDescription(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +impl From for crate::Error { + fn from(err: Parse) -> Self { + match err { + Parse::TryFromParsed(err) => Self::TryFromParsed(err), + Parse::ParseFromDescription(err) => Self::ParseFromDescription(err), + #[allow(deprecated)] + Parse::UnexpectedTrailingCharacters { never } => match never {}, + } + } +} + +impl TryFrom for Parse { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)), + #[allow(deprecated)] + crate::Error::UnexpectedTrailingCharacters { never } => match never {}, + 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 new file mode 100644 index 00000000..40124dce --- /dev/null +++ b/vendor/time/src/error/parse_from_description.rs @@ -0,0 +1,53 @@ +//! Error parsing an input into a [`Parsed`](crate::parsing::Parsed) struct + +use core::fmt; + +use crate::error; + +/// An error that occurred while parsing the input into a [`Parsed`](crate::parsing::Parsed) struct. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParseFromDescription { + /// A string literal was not what was expected. + #[non_exhaustive] + 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidLiteral => f.write_str("a character literal was not valid"), + 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") + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseFromDescription {} + +impl From for crate::Error { + fn from(original: ParseFromDescription) -> Self { + Self::ParseFromDescription(original) + } +} + +impl TryFrom for ParseFromDescription { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::ParseFromDescription(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/try_from_parsed.rs b/vendor/time/src/error/try_from_parsed.rs new file mode 100644 index 00000000..da8e6947 --- /dev/null +++ b/vendor/time/src/error/try_from_parsed.rs @@ -0,0 +1,71 @@ +//! Error converting a [`Parsed`](crate::parsing::Parsed) struct to another type + +use core::fmt; + +use crate::error; + +/// An error that occurred when converting a [`Parsed`](crate::parsing::Parsed) to another type. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryFromParsed { + /// The [`Parsed`](crate::parsing::Parsed) did not include enough information to construct the + /// type. + InsufficientInformation, + /// Some component contained an invalid value for the type. + ComponentRange(error::ComponentRange), +} + +impl fmt::Display for TryFromParsed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsufficientInformation => f.write_str( + "the `Parsed` struct did not include enough information to construct the type", + ), + Self::ComponentRange(err) => err.fmt(f), + } + } +} + +impl From for TryFromParsed { + fn from(v: error::ComponentRange) -> Self { + Self::ComponentRange(v) + } +} + +impl TryFrom for error::ComponentRange { + type Error = error::DifferentVariant; + + fn try_from(err: TryFromParsed) -> Result { + match err { + TryFromParsed::ComponentRange(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryFromParsed { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InsufficientInformation => None, + Self::ComponentRange(err) => Some(err), + } + } +} + +impl From for crate::Error { + fn from(original: TryFromParsed) -> Self { + Self::TryFromParsed(original) + } +} + +impl TryFrom for TryFromParsed { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result { + match err { + crate::Error::TryFromParsed(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/ext/digit_count.rs b/vendor/time/src/ext/digit_count.rs new file mode 100644 index 00000000..fb42ce9e --- /dev/null +++ b/vendor/time/src/ext/digit_count.rs @@ -0,0 +1,26 @@ +use num_conv::prelude::*; + +/// A trait that indicates the formatted width of the value can be determined. +/// +/// Note that this should not be implemented for any signed integers. This forces the caller to +/// write the sign if desired. +pub(crate) trait DigitCount { + /// The number of digits in the stringified value. + fn num_digits(self) -> u8; +} + +/// 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.truncate::() + 1, + None => 1, + } + } + })* + }; +} + +impl_digit_count!(u8, u16, u32); diff --git a/vendor/time/src/ext/instant.rs b/vendor/time/src/ext/instant.rs new file mode 100644 index 00000000..313ad5b1 --- /dev/null +++ b/vendor/time/src/ext/instant.rs @@ -0,0 +1,98 @@ +use std::time::Instant as StdInstant; + +use crate::Duration; + +/// Sealed trait to prevent downstream implementations. +mod sealed { + /// A trait that cannot be implemented by downstream users. + pub trait Sealed: Sized {} + impl Sealed for std::time::Instant {} +} + +/// An extension trait for [`std::time::Instant`] that adds methods for +/// [`time::Duration`](Duration)s. +pub trait InstantExt: sealed::Sealed { + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`InstantExt::checked_add_signed`] for a non-panicking + /// version. + fn add_signed(self, duration: Duration) -> Self { + self.checked_add_signed(duration) + .expect("overflow when adding duration to instant") + } + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`InstantExt::checked_sub_signed`] for a non-panicking + /// version. + fn sub_signed(self, duration: Duration) -> Self { + self.checked_sub_signed(duration) + .expect("overflow when subtracting duration from instant") + } + + /// Returns `Some(t)` where `t` is the time `self.checked_add_signed(duration)` if `t` can be + /// represented as `Instant` (which means it's inside the bounds of the underlying data + /// structure), `None` otherwise. + fn checked_add_signed(&self, duration: Duration) -> Option; + + /// Returns `Some(t)` where `t` is the time `self.checked_sub_signed(duration)` if `t` can be + /// represented as `Instant` (which means it's inside the bounds of the underlying data + /// structure), `None` otherwise. + fn checked_sub_signed(&self, duration: Duration) -> Option; + + /// Returns the amount of time elapsed from another instant to this one. This will be negative + /// if `earlier` is later than `self`. + /// + /// # Example + /// + /// ```rust + /// # use std::thread::sleep; + /// # use std::time::{Duration, Instant}; + /// # use time::ext::InstantExt; + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.signed_duration_since(now)); // positive + /// println!("{:?}", now.signed_duration_since(new_now)); // negative + /// ``` + fn signed_duration_since(&self, earlier: Self) -> Duration; +} + +impl InstantExt for StdInstant { + fn checked_add_signed(&self, duration: Duration) -> Option { + if duration.is_positive() { + self.checked_add(duration.unsigned_abs()) + } else if duration.is_negative() { + self.checked_sub(duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + Some(*self) + } + } + + fn checked_sub_signed(&self, duration: Duration) -> Option { + if duration.is_positive() { + self.checked_sub(duration.unsigned_abs()) + } else if duration.is_negative() { + self.checked_add(duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + Some(*self) + } + } + + fn signed_duration_since(&self, earlier: Self) -> Duration { + if *self > earlier { + self.saturating_duration_since(earlier) + .try_into() + .unwrap_or(Duration::MAX) + } else { + earlier + .saturating_duration_since(*self) + .try_into() + .map_or(Duration::MIN, |d: Duration| -d) + } + } +} diff --git a/vendor/time/src/ext/mod.rs b/vendor/time/src/ext/mod.rs new file mode 100644 index 00000000..7cc2d0d9 --- /dev/null +++ b/vendor/time/src/ext/mod.rs @@ -0,0 +1,13 @@ +//! Extension traits. + +mod digit_count; +#[cfg(feature = "std")] +mod instant; +mod numerical_duration; +mod numerical_std_duration; + +pub(crate) use self::digit_count::DigitCount; +#[cfg(feature = "std")] +pub use self::instant::InstantExt; +pub use self::numerical_duration::NumericalDuration; +pub use self::numerical_std_duration::NumericalStdDuration; diff --git a/vendor/time/src/ext/numerical_duration.rs b/vendor/time/src/ext/numerical_duration.rs new file mode 100644 index 00000000..a1e93cc4 --- /dev/null +++ b/vendor/time/src/ext/numerical_duration.rs @@ -0,0 +1,140 @@ +use crate::convert::*; +use crate::Duration; + +/// Sealed trait to prevent downstream implementations. +mod sealed { + /// A trait that cannot be implemented by downstream users. + pub trait Sealed {} + impl Sealed for i64 {} + impl Sealed for f64 {} +} + +/// Create [`Duration`]s from numeric literals. +/// +/// # Examples +/// +/// Basic construction of [`Duration`]s. +/// +/// ```rust +/// # use time::{Duration, ext::NumericalDuration}; +/// assert_eq!(5.nanoseconds(), Duration::nanoseconds(5)); +/// assert_eq!(5.microseconds(), Duration::microseconds(5)); +/// assert_eq!(5.milliseconds(), Duration::milliseconds(5)); +/// assert_eq!(5.seconds(), Duration::seconds(5)); +/// assert_eq!(5.minutes(), Duration::minutes(5)); +/// assert_eq!(5.hours(), Duration::hours(5)); +/// assert_eq!(5.days(), Duration::days(5)); +/// assert_eq!(5.weeks(), Duration::weeks(5)); +/// ``` +/// +/// Signed integers work as well! +/// +/// ```rust +/// # use time::{Duration, ext::NumericalDuration}; +/// assert_eq!((-5).nanoseconds(), Duration::nanoseconds(-5)); +/// assert_eq!((-5).microseconds(), Duration::microseconds(-5)); +/// assert_eq!((-5).milliseconds(), Duration::milliseconds(-5)); +/// assert_eq!((-5).seconds(), Duration::seconds(-5)); +/// assert_eq!((-5).minutes(), Duration::minutes(-5)); +/// assert_eq!((-5).hours(), Duration::hours(-5)); +/// assert_eq!((-5).days(), Duration::days(-5)); +/// assert_eq!((-5).weeks(), Duration::weeks(-5)); +/// ``` +/// +/// Just like any other [`Duration`], they can be added, subtracted, etc. +/// +/// ```rust +/// # use time::ext::NumericalDuration; +/// assert_eq!(2.seconds() + 500.milliseconds(), 2_500.milliseconds()); +/// assert_eq!(2.seconds() - 500.milliseconds(), 1_500.milliseconds()); +/// ``` +/// +/// When called on floating point values, any remainder of the floating point value will be +/// truncated. Keep in mind that floating point numbers are inherently imprecise and have +/// limited capacity. +pub trait NumericalDuration: sealed::Sealed { + /// Create a [`Duration`] from the number of nanoseconds. + fn nanoseconds(self) -> Duration; + /// Create a [`Duration`] from the number of microseconds. + fn microseconds(self) -> Duration; + /// Create a [`Duration`] from the number of milliseconds. + fn milliseconds(self) -> Duration; + /// Create a [`Duration`] from the number of seconds. + fn seconds(self) -> Duration; + /// Create a [`Duration`] from the number of minutes. + fn minutes(self) -> Duration; + /// Create a [`Duration`] from the number of hours. + fn hours(self) -> Duration; + /// Create a [`Duration`] from the number of days. + fn days(self) -> Duration; + /// Create a [`Duration`] from the number of weeks. + fn weeks(self) -> Duration; +} + +impl NumericalDuration for i64 { + fn nanoseconds(self) -> Duration { + Duration::nanoseconds(self) + } + + fn microseconds(self) -> Duration { + Duration::microseconds(self) + } + + fn milliseconds(self) -> Duration { + Duration::milliseconds(self) + } + + fn seconds(self) -> Duration { + Duration::seconds(self) + } + + fn minutes(self) -> Duration { + Duration::minutes(self) + } + + fn hours(self) -> Duration { + Duration::hours(self) + } + + fn days(self) -> Duration { + Duration::days(self) + } + + fn weeks(self) -> Duration { + Duration::weeks(self) + } +} + +impl NumericalDuration for f64 { + fn nanoseconds(self) -> Duration { + Duration::nanoseconds(self as i64) + } + + fn microseconds(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Microsecond) as Self) as i64) + } + + fn milliseconds(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Millisecond) as Self) as i64) + } + + fn seconds(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Second) as Self) as i64) + } + + fn minutes(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Minute) as Self) as i64) + } + + fn hours(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Hour) as Self) as i64) + } + + fn days(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Day) as Self) as i64) + } + + fn weeks(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Week) as Self) as i64) + } +} diff --git a/vendor/time/src/ext/numerical_std_duration.rs b/vendor/time/src/ext/numerical_std_duration.rs new file mode 100644 index 00000000..d6bc7bab --- /dev/null +++ b/vendor/time/src/ext/numerical_std_duration.rs @@ -0,0 +1,192 @@ +use core::time::Duration as StdDuration; + +use num_conv::prelude::*; + +use crate::convert::*; + +/// Sealed trait to prevent downstream implementations. +mod sealed { + /// A trait that cannot be implemented by downstream users. + pub trait Sealed {} + impl Sealed for u64 {} + impl Sealed for f64 {} +} + +/// Create [`std::time::Duration`]s from numeric literals. +/// +/// # Examples +/// +/// Basic construction of [`std::time::Duration`]s. +/// +/// ```rust +/// # use time::ext::NumericalStdDuration; +/// # use core::time::Duration; +/// assert_eq!(5.std_nanoseconds(), Duration::from_nanos(5)); +/// assert_eq!(5.std_microseconds(), Duration::from_micros(5)); +/// assert_eq!(5.std_milliseconds(), Duration::from_millis(5)); +/// assert_eq!(5.std_seconds(), Duration::from_secs(5)); +/// assert_eq!(5.std_minutes(), Duration::from_secs(5 * 60)); +/// assert_eq!(5.std_hours(), Duration::from_secs(5 * 3_600)); +/// assert_eq!(5.std_days(), Duration::from_secs(5 * 86_400)); +/// assert_eq!(5.std_weeks(), Duration::from_secs(5 * 604_800)); +/// ``` +/// +/// Just like any other [`std::time::Duration`], they can be added, subtracted, etc. +/// +/// ```rust +/// # use time::ext::NumericalStdDuration; +/// assert_eq!( +/// 2.std_seconds() + 500.std_milliseconds(), +/// 2_500.std_milliseconds() +/// ); +/// assert_eq!( +/// 2.std_seconds() - 500.std_milliseconds(), +/// 1_500.std_milliseconds() +/// ); +/// ``` +/// +/// When called on floating point values, any remainder of the floating point value will be +/// truncated. Keep in mind that floating point numbers are inherently imprecise and have +/// limited capacity. +pub trait NumericalStdDuration: sealed::Sealed { + /// Create a [`std::time::Duration`] from the number of nanoseconds. + fn std_nanoseconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of microseconds. + fn std_microseconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of milliseconds. + fn std_milliseconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of seconds. + fn std_seconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of minutes. + fn std_minutes(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of hours. + fn std_hours(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of days. + fn std_days(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of weeks. + fn std_weeks(self) -> StdDuration; +} + +impl NumericalStdDuration for u64 { + fn std_nanoseconds(self) -> StdDuration { + StdDuration::from_nanos(self) + } + + fn std_microseconds(self) -> StdDuration { + StdDuration::from_micros(self) + } + + fn std_milliseconds(self) -> StdDuration { + StdDuration::from_millis(self) + } + + fn std_seconds(self) -> StdDuration { + StdDuration::from_secs(self) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_minutes(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Minute).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_hours(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Hour).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_days(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Day).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_weeks(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Week).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } +} + +impl NumericalStdDuration for f64 { + /// # Panics + /// + /// This will panic if self is negative. + fn std_nanoseconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos(self as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_microseconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Microsecond) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_milliseconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Millisecond) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_seconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Second) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_minutes(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Minute) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_hours(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Hour) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_days(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Day) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_weeks(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Week) as Self) as u64) + } +} diff --git a/vendor/time/src/format_description/borrowed_format_item.rs b/vendor/time/src/format_description/borrowed_format_item.rs new file mode 100644 index 00000000..2e97efd4 --- /dev/null +++ b/vendor/time/src/format_description/borrowed_format_item.rs @@ -0,0 +1,106 @@ +//! A format item with borrowed data. + +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use core::fmt; + +use crate::error; +use crate::format_description::Component; + +/// A complete description of how to format and parse a type. +#[non_exhaustive] +#[cfg_attr(not(feature = "alloc"), derive(Debug))] +#[derive(Clone, PartialEq, Eq)] +pub enum BorrowedFormatItem<'a> { + /// Bytes that are formatted as-is. + /// + /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed + /// through `String::from_utf8_lossy` when necessary. + Literal(&'a [u8]), + /// A minimal representation of a single non-literal item. + Component(Component), + /// A series of literals or components that collectively form a partial or complete + /// description. + Compound(&'a [Self]), + /// A `FormatItem` that may or may not be present when parsing. If parsing fails, there + /// will be no effect on the resulting `struct`. + /// + /// This variant has no effect on formatting, as the value is guaranteed to be present. + Optional(&'a Self), + /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When + /// formatting, the first element of the slice is used. An empty slice is a no-op when + /// formatting or parsing. + First(&'a [Self]), +} + +#[cfg(feature = "alloc")] +impl fmt::Debug for BorrowedFormatItem<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), + Self::Component(component) => component.fmt(f), + Self::Compound(compound) => compound.fmt(f), + Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(), + Self::First(items) => f.debug_tuple("First").field(items).finish(), + } + } +} + +impl From for BorrowedFormatItem<'_> { + fn from(component: Component) -> Self { + Self::Component(component) + } +} + +impl TryFrom> for Component { + type Error = error::DifferentVariant; + + fn try_from(value: BorrowedFormatItem<'_>) -> Result { + match value { + BorrowedFormatItem::Component(component) => Ok(component), + _ => Err(error::DifferentVariant), + } + } +} + +impl<'a> From<&'a [BorrowedFormatItem<'_>]> for BorrowedFormatItem<'a> { + fn from(items: &'a [BorrowedFormatItem<'_>]) -> Self { + Self::Compound(items) + } +} + +impl<'a> TryFrom> for &[BorrowedFormatItem<'a>] { + type Error = error::DifferentVariant; + + fn try_from(value: BorrowedFormatItem<'a>) -> Result { + match value { + BorrowedFormatItem::Compound(items) => Ok(items), + _ => Err(error::DifferentVariant), + } + } +} + +impl PartialEq for BorrowedFormatItem<'_> { + fn eq(&self, rhs: &Component) -> bool { + matches!(self, Self::Component(component) if component == rhs) + } +} + +impl PartialEq> for Component { + fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { + rhs == self + } +} + +impl PartialEq<&[Self]> for BorrowedFormatItem<'_> { + fn eq(&self, rhs: &&[Self]) -> bool { + matches!(self, Self::Compound(compound) if compound == rhs) + } +} + +impl PartialEq> for &[BorrowedFormatItem<'_>] { + fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { + rhs == self + } +} diff --git a/vendor/time/src/format_description/component.rs b/vendor/time/src/format_description/component.rs new file mode 100644 index 00000000..9119c090 --- /dev/null +++ b/vendor/time/src/format_description/component.rs @@ -0,0 +1,44 @@ +//! Part of a format description. + +use crate::format_description::modifier; + +/// A component of a larger format description. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Component { + /// Day of the month. + Day(modifier::Day), + /// Month of the year. + Month(modifier::Month), + /// Ordinal day of the year. + Ordinal(modifier::Ordinal), + /// Day of the week. + Weekday(modifier::Weekday), + /// Week within the year. + WeekNumber(modifier::WeekNumber), + /// Year of the date. + Year(modifier::Year), + /// Hour of the day. + Hour(modifier::Hour), + /// Minute within the hour. + Minute(modifier::Minute), + /// AM/PM part of the time. + Period(modifier::Period), + /// Second within the minute. + Second(modifier::Second), + /// Subsecond within the second. + Subsecond(modifier::Subsecond), + /// Hour of the UTC offset. + OffsetHour(modifier::OffsetHour), + /// Minute within the hour of the UTC offset. + OffsetMinute(modifier::OffsetMinute), + /// Second within the minute of the UTC offset. + OffsetSecond(modifier::OffsetSecond), + /// A number of bytes to ignore when parsing. This has no effect on formatting. + 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/mod.rs b/vendor/time/src/format_description/mod.rs new file mode 100644 index 00000000..9e7d5202 --- /dev/null +++ b/vendor/time/src/format_description/mod.rs @@ -0,0 +1,41 @@ +//! Description of how types should be formatted and parsed. +//! +//! The formatted value will be output to the provided writer. Format descriptions can be +//! [well-known](crate::format_description::well_known) or obtained by using the +//! [`format_description!`](crate::macros::format_description) macro or a function listed below. +//! +//! For examples, see the implementors of [Formattable](crate::formatting::Formattable), +//! e.g. [`well_known::Rfc3339`]. + +mod borrowed_format_item; +mod component; +pub mod modifier; +#[cfg(feature = "alloc")] +mod owned_format_item; +#[cfg(feature = "alloc")] +mod parse; + +pub use borrowed_format_item::BorrowedFormatItem; +#[doc(hidden)] +#[deprecated(since = "0.3.37", note = "use `BorrowedFormatItem` for clarity")] +pub use borrowed_format_item::BorrowedFormatItem as FormatItem; +#[cfg(feature = "alloc")] +pub use owned_format_item::OwnedFormatItem; + +pub use self::component::Component; +#[cfg(feature = "alloc")] +pub use self::parse::{ + parse, parse_borrowed, parse_owned, parse_strftime_borrowed, parse_strftime_owned, +}; + +/// Well-known formats, typically standards. +pub mod well_known { + pub mod iso8601; + mod rfc2822; + mod rfc3339; + + #[doc(inline)] + pub use iso8601::Iso8601; + pub use rfc2822::Rfc2822; + pub use rfc3339::Rfc3339; +} diff --git a/vendor/time/src/format_description/modifier.rs b/vendor/time/src/format_description/modifier.rs new file mode 100644 index 00000000..82fca805 --- /dev/null +++ b/vendor/time/src/format_description/modifier.rs @@ -0,0 +1,434 @@ +//! Various modifiers for components. + +use core::num::NonZeroU16; + +/// Day of the month. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Day { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// The representation of a month. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MonthRepr { + /// The number of the month (January is 1, December is 12). + Numerical, + /// The long form of the month name (e.g. "January"). + Long, + /// The short form of the month name (e.g. "Jan"). + Short, +} + +/// Month of the year. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Month { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// What form of representation should be used? + pub repr: MonthRepr, + /// Is the value case sensitive when parsing? + pub case_sensitive: bool, +} + +/// Ordinal day of the year. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Ordinal { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// The representation used for the day of the week. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WeekdayRepr { + /// The short form of the weekday (e.g. "Mon"). + Short, + /// The long form of the weekday (e.g. "Monday"). + Long, + /// A numerical representation using Sunday as the first day of the week. + /// + /// Sunday is either 0 or 1, depending on the other modifier's value. + Sunday, + /// A numerical representation using Monday as the first day of the week. + /// + /// Monday is either 0 or 1, depending on the other modifier's value. + Monday, +} + +/// Day of the week. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Weekday { + /// What form of representation should be used? + pub repr: WeekdayRepr, + /// When using a numerical representation, should it be zero or one-indexed? + pub one_indexed: bool, + /// Is the value case sensitive when parsing? + pub case_sensitive: bool, +} + +/// The representation used for the week number. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WeekNumberRepr { + /// Week 1 is the week that contains January 4. + Iso, + /// Week 1 begins on the first Sunday of the calendar year. + Sunday, + /// Week 1 begins on the first Monday of the calendar year. + Monday, +} + +/// Week within the year. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct WeekNumber { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// What kind of representation should be used? + pub repr: WeekNumberRepr, +} + +/// The representation used for a year value. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum YearRepr { + /// The full value of the year. + Full, + /// All digits except the last two. Includes the sign, if any. + Century, + /// Only the last two digits of the year. + LastTwo, +} + +/// The range of years that are supported. +/// +/// This modifier has no effect when the year repr is [`LastTwo`](YearRepr::LastTwo). +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum YearRange { + /// Years between -9999 and 9999 are supported. + Standard, + /// Years between -999_999 and 999_999 are supported, with the sign being required if the year + /// contains more than four digits. + /// + /// If the `large-dates` feature is not enabled, this variant is equivalent to `Standard`. + Extended, +} + +/// Year of the date. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Year { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// What kind of representation should be used? + pub repr: YearRepr, + /// What range of years is supported? + pub range: YearRange, + /// Whether the value is based on the ISO week number or the Gregorian calendar. + pub iso_week_based: bool, + /// Whether the `+` sign is present when a positive year contains fewer than five digits. + pub sign_is_mandatory: bool, +} + +/// Hour of the day. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Hour { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// Is the hour displayed using a 12 or 24-hour clock? + pub is_12_hour_clock: bool, +} + +/// Minute within the hour. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Minute { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// AM/PM part of the time. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Period { + /// Is the period uppercase or lowercase? + pub is_uppercase: bool, + /// Is the value case sensitive when parsing? + /// + /// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior. + pub case_sensitive: bool, +} + +/// Second within the minute. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Second { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// The number of digits present in a subsecond representation. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SubsecondDigits { + /// Exactly one digit. + One, + /// Exactly two digits. + Two, + /// Exactly three digits. + Three, + /// Exactly four digits. + Four, + /// Exactly five digits. + Five, + /// Exactly six digits. + Six, + /// Exactly seven digits. + Seven, + /// Exactly eight digits. + Eight, + /// Exactly nine digits. + Nine, + /// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits + /// necessary will be used. + OneOrMore, +} + +/// Subsecond within the second. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Subsecond { + /// How many digits are present in the component? + pub digits: SubsecondDigits, +} + +/// Hour of the UTC offset. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OffsetHour { + /// Whether the `+` sign is present on positive values. + pub sign_is_mandatory: bool, + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// Minute within the hour of the UTC offset. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OffsetMinute { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// Second within the minute of the UTC offset. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OffsetSecond { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// Type of padding to ensure a minimum width. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Padding { + /// A space character (` `) should be used as padding. + Space, + /// A zero character (`0`) should be used as padding. + Zero, + /// There is no padding. This can result in a width below the otherwise minimum number of + /// characters. + None, +} + +/// Ignore some number of bytes. +/// +/// This has no effect when formatting. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Ignore { + /// The number of bytes to ignore. + pub count: NonZeroU16, +} + +// Needed as `Default` is deliberately not implemented for `Ignore`. The number of bytes to ignore +// must be explicitly provided. +impl Ignore { + /// Create an instance of `Ignore` with the provided number of bytes to ignore. + pub const fn count(count: NonZeroU16) -> Self { + Self { count } + } +} + +/// The precision of a Unix timestamp. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UnixTimestampPrecision { + /// Seconds since the Unix epoch. + Second, + /// Milliseconds since the Unix epoch. + Millisecond, + /// Microseconds since the Unix epoch. + Microsecond, + /// Nanoseconds since the Unix epoch. + Nanosecond, +} + +/// A Unix timestamp. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct UnixTimestamp { + /// The precision of the timestamp. + pub precision: UnixTimestampPrecision, + /// Whether the `+` sign must be present for a non-negative timestamp. + 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)*) => { + $(#[$attr])* + /// + /// This function exists since [`Default::default()`] cannot be used in a `const` context. + /// It may be removed once that becomes possible. As the [`Default`] trait is in the + /// prelude, removing this function in the future will not cause any resolution failures for + /// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be + /// affected. As such it will not be considered a breaking change. + $($x)* + }; + ($($_:tt)*) => {}; +} + +/// Implement `Default` for the given type. This also generates an inherent implementation of a +/// `default` method that is `const fn`, permitting the default value to be used in const contexts. +// Every modifier should use this macro rather than a derived `Default`. +macro_rules! impl_const_default { + ($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$( + impl $type { + if_pub! { + $($pub)? + $(#[$doc])*; + pub const fn default() -> Self { + $default + } + } + } + + $(#[$doc])* + impl Default for $type { + fn default() -> Self { + $default + } + } + )*}; +} + +impl_const_default! { + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub Day => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value uses the + /// [`Numerical`](Self::Numerical) representation. + MonthRepr => Self::Numerical; + /// Creates an instance of this type that indicates the value uses the + /// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero), + /// and is case-sensitive when parsing. + @pub Month => Self { + padding: Padding::Zero, + repr: MonthRepr::Numerical, + case_sensitive: true, + }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub Ordinal => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation. + WeekdayRepr => Self::Long; + /// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long) + /// representation and is case-sensitive when parsing. If the representation is changed to a + /// numerical one, the instance defaults to one-based indexing. + @pub Weekday => Self { + repr: WeekdayRepr::Long, + one_indexed: true, + case_sensitive: true, + }; + /// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation. + WeekNumberRepr => Self::Iso; + /// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero) + /// and uses the [`Iso`](WeekNumberRepr::Iso) representation. + @pub WeekNumber => Self { + padding: Padding::Zero, + repr: WeekNumberRepr::Iso, + }; + /// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation. + YearRepr => Self::Full; + /// Creates a modifier that indicates the value uses the [`Extended`](Self::Extended) range. + YearRange => Self::Extended; + /// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full) + /// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its + /// base, and only includes the year's sign if necessary. + @pub Year => Self { + padding: Padding::Zero, + repr: YearRepr::Full, + range: YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and + /// has the 24-hour representation. + @pub Hour => Self { + padding: Padding::Zero, + is_12_hour_clock: false, + }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub Minute => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value uses the upper-case representation and is + /// case-sensitive when parsing. + @pub Period => Self { + is_uppercase: true, + case_sensitive: true, + }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub Second => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the stringified value contains [one or more + /// digits](Self::OneOrMore). + SubsecondDigits => Self::OneOrMore; + /// 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 only uses a sign for negative values and is + /// [padded with zeroes](Padding::Zero). + @pub OffsetHour => Self { + sign_is_mandatory: false, + padding: Padding::Zero, + }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub OffsetMinute => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub OffsetSecond => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero). + Padding => Self::Zero; + /// Creates a modifier that indicates the value represents the [number of seconds](Self::Second) + /// since the Unix epoch. + UnixTimestampPrecision => Self::Second; + /// Creates a modifier that indicates the value represents the [number of + /// seconds](UnixTimestampPrecision::Second) since the Unix epoch. The sign is not mandatory. + @pub UnixTimestamp => Self { + 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/owned_format_item.rs b/vendor/time/src/format_description/owned_format_item.rs new file mode 100644 index 00000000..391e76f0 --- /dev/null +++ b/vendor/time/src/format_description/owned_format_item.rs @@ -0,0 +1,158 @@ +//! A format item with owned data. + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +use crate::error; +use crate::format_description::{BorrowedFormatItem, Component}; + +/// A complete description of how to format and parse a type. +#[non_exhaustive] +#[derive(Clone, PartialEq, Eq)] +pub enum OwnedFormatItem { + /// Bytes that are formatted as-is. + /// + /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed + /// through `String::from_utf8_lossy` when necessary. + Literal(Box<[u8]>), + /// A minimal representation of a single non-literal item. + Component(Component), + /// A series of literals or components that collectively form a partial or complete + /// description. + Compound(Box<[Self]>), + /// A `FormatItem` that may or may not be present when parsing. If parsing fails, there + /// will be no effect on the resulting `struct`. + /// + /// This variant has no effect on formatting, as the value is guaranteed to be present. + Optional(Box), + /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When + /// formatting, the first element of the [`Vec`] is used. An empty [`Vec`] is a no-op when + /// formatting or parsing. + First(Box<[Self]>), +} + +impl fmt::Debug for OwnedFormatItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), + Self::Component(component) => component.fmt(f), + Self::Compound(compound) => compound.fmt(f), + Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(), + Self::First(items) => f.debug_tuple("First").field(items).finish(), + } + } +} + +impl From> for OwnedFormatItem { + fn from(item: BorrowedFormatItem<'_>) -> Self { + (&item).into() + } +} + +impl From<&BorrowedFormatItem<'_>> for OwnedFormatItem { + fn from(item: &BorrowedFormatItem<'_>) -> Self { + match item { + BorrowedFormatItem::Literal(literal) => { + Self::Literal(literal.to_vec().into_boxed_slice()) + } + BorrowedFormatItem::Component(component) => Self::Component(*component), + BorrowedFormatItem::Compound(compound) => Self::Compound( + compound + .iter() + .cloned() + .map(Into::into) + .collect::>() + .into_boxed_slice(), + ), + BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())), + BorrowedFormatItem::First(items) => Self::First( + items + .iter() + .cloned() + .map(Into::into) + .collect::>() + .into_boxed_slice(), + ), + } + } +} + +impl From>> for OwnedFormatItem { + fn from(items: Vec>) -> Self { + items.as_slice().into() + } +} + +impl<'a, T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem { + fn from(items: &T) -> Self { + Self::Compound( + items + .as_ref() + .iter() + .cloned() + .map(Into::into) + .collect::>() + .into_boxed_slice(), + ) + } +} + +impl From for OwnedFormatItem { + fn from(component: Component) -> Self { + Self::Component(component) + } +} + +impl TryFrom for Component { + type Error = error::DifferentVariant; + + fn try_from(value: OwnedFormatItem) -> Result { + match value { + OwnedFormatItem::Component(component) => Ok(component), + _ => Err(error::DifferentVariant), + } + } +} + +impl From> for OwnedFormatItem { + fn from(items: Vec) -> Self { + Self::Compound(items.into_boxed_slice()) + } +} + +impl TryFrom for Vec { + type Error = error::DifferentVariant; + + fn try_from(value: OwnedFormatItem) -> Result { + match value { + OwnedFormatItem::Compound(items) => Ok(items.into_vec()), + _ => Err(error::DifferentVariant), + } + } +} + +impl PartialEq for OwnedFormatItem { + fn eq(&self, rhs: &Component) -> bool { + matches!(self, Self::Component(component) if component == rhs) + } +} + +impl PartialEq for Component { + fn eq(&self, rhs: &OwnedFormatItem) -> bool { + rhs == self + } +} + +impl PartialEq<&[Self]> for OwnedFormatItem { + fn eq(&self, rhs: &&[Self]) -> bool { + matches!(self, Self::Compound(compound) if &&**compound == rhs) + } +} + +impl PartialEq for &[OwnedFormatItem] { + fn eq(&self, rhs: &OwnedFormatItem) -> bool { + rhs == self + } +} diff --git a/vendor/time/src/format_description/parse/ast.rs b/vendor/time/src/format_description/parse/ast.rs new file mode 100644 index 00000000..cf13de89 --- /dev/null +++ b/vendor/time/src/format_description/parse/ast.rs @@ -0,0 +1,384 @@ +//! AST for parsing format descriptions. + +use alloc::boxed::Box; +use alloc::string::String; +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> { + /// A literal string, formatted and parsed as-is. + /// + /// This should never be present inside a nested format description. + Literal(Spanned<&'a [u8]>), + /// A sequence of brackets. The first acts as the escape character. + /// + /// This should never be present if the lexer has `BACKSLASH_ESCAPE` set to `true`. + EscapedBracket { + /// The first bracket. + _first: Unused, + /// The second bracket. + _second: Unused, + }, + /// Part of a type, along with its modifiers. + Component { + /// Where the opening bracket was in the format string. + _opening_bracket: Unused, + /// Whitespace between the opening bracket and name. + _leading_whitespace: Unused>>, + /// The name of the component. + name: Spanned<&'a [u8]>, + /// The modifiers for the component. + modifiers: Box<[Modifier<'a>]>, + /// Whitespace between the modifiers and closing bracket. + _trailing_whitespace: Unused>>, + /// Where the closing bracket was in the format string. + _closing_bracket: Unused, + }, + /// An optional sequence of items. + Optional { + /// Where the opening bracket was in the format string. + opening_bracket: Location, + /// Whitespace between the opening bracket and "optional". + _leading_whitespace: Unused>>, + /// The "optional" keyword. + _optional_kw: Unused>, + /// Whitespace between the "optional" keyword and the opening bracket. + _whitespace: Unused>, + /// The items within the optional sequence. + nested_format_description: NestedFormatDescription<'a>, + /// Where the closing bracket was in the format string. + closing_bracket: Location, + }, + /// The first matching parse of a sequence of items. + First { + /// Where the opening bracket was in the format string. + opening_bracket: Location, + /// Whitespace between the opening bracket and "first". + _leading_whitespace: Unused>>, + /// The "first" keyword. + _first_kw: Unused>, + /// Whitespace between the "first" keyword and the opening bracket. + _whitespace: Unused>, + /// The sequences of items to try. + nested_format_descriptions: Box<[NestedFormatDescription<'a>]>, + /// Where the closing bracket was in the format string. + closing_bracket: Location, + }, +} + +/// A format description that is nested within another format description. +pub(super) struct NestedFormatDescription<'a> { + /// Where the opening bracket was in the format string. + pub(super) _opening_bracket: Unused, + /// The items within the nested format description. + pub(super) items: Box<[Item<'a>]>, + /// Where the closing bracket was in the format string. + pub(super) _closing_bracket: Unused, + /// Whitespace between the closing bracket and the next item. + pub(super) _trailing_whitespace: Unused>>, +} + +/// A modifier for a component. +pub(super) struct Modifier<'a> { + /// Whitespace preceding the modifier. + pub(super) _leading_whitespace: Unused>, + /// The key of the modifier. + pub(super) key: Spanned<&'a [u8]>, + /// Where the colon of the modifier was in the format string. + pub(super) _colon: Unused, + /// The value of the modifier. + pub(super) value: Spanned<&'a [u8]>, +} + +/// Parse the provided tokens into an AST. +pub(super) fn parse< + 'item: 'iter, + 'iter, + I: Iterator, Error>>, + const VERSION: usize, +>( + tokens: &'iter mut lexer::Lexed, +) -> impl Iterator, Error>> + 'iter { + validate_version!(VERSION); + parse_inner::<_, false, VERSION>(tokens) +} + +/// Parse the provided tokens into an AST. The const generic indicates whether the resulting +/// [`Item`] will be used directly or as part of a [`NestedFormatDescription`]. +fn parse_inner< + 'item, + I: Iterator, Error>>, + const NESTED: bool, + const VERSION: usize, +>( + tokens: &mut lexer::Lexed, +) -> impl Iterator, Error>> + '_ { + validate_version!(VERSION); + iter::from_fn(move || { + if NESTED && tokens.peek_closing_bracket().is_some() { + return None; + } + + let next = match tokens.next()? { + Ok(token) => token, + Err(err) => return Some(Err(err)), + }; + + Some(match next { + lexer::Token::Literal(Spanned { value: _, span: _ }) if NESTED => { + bug!("literal should not be present in nested description") + } + lexer::Token::Literal(value) => Ok(Item::Literal(value)), + lexer::Token::Bracket { + kind: lexer::BracketKind::Opening, + location, + } => { + if version!(..=1) { + if let Some(second_location) = tokens.next_if_opening_bracket() { + Ok(Item::EscapedBracket { + _first: unused(location), + _second: unused(second_location), + }) + } else { + parse_component::<_, VERSION>(location, tokens) + } + } else { + parse_component::<_, VERSION>(location, tokens) + } + } + lexer::Token::Bracket { + kind: lexer::BracketKind::Closing, + location: _, + } if NESTED => { + bug!("closing bracket should be caught by the `if` statement") + } + lexer::Token::Bracket { + kind: lexer::BracketKind::Closing, + location: _, + } => { + bug!("closing bracket should have been consumed by `parse_component`") + } + lexer::Token::ComponentPart { + kind: _, // whitespace is significant in nested components + value, + } if NESTED => Ok(Item::Literal(value)), + lexer::Token::ComponentPart { kind: _, value: _ } => { + bug!("component part should have been consumed by `parse_component`") + } + }) + }) +} + +/// Parse a component. This assumes that the opening bracket has already been consumed. +fn parse_component< + 'a, + I: Iterator, Error>>, + const VERSION: usize, +>( + opening_bracket: Location, + tokens: &mut lexer::Lexed, +) -> Result, Error> { + validate_version!(VERSION); + let leading_whitespace = tokens.next_if_whitespace(); + + let Some(name) = tokens.next_if_not_whitespace() else { + let span = match leading_whitespace { + Some(Spanned { value: _, span }) => span, + None => opening_bracket.to_self(), + }; + return Err(Error { + _inner: unused(span.error("expected component name")), + public: crate::error::InvalidFormatDescription::MissingComponentName { + index: span.start.byte as usize, + }, + }); + }; + + if *name == b"optional" { + let Some(whitespace) = tokens.next_if_whitespace() else { + return Err(Error { + _inner: unused(name.span.error("expected whitespace after `optional`")), + public: crate::error::InvalidFormatDescription::Expected { + what: "whitespace after `optional`", + index: name.span.end.byte as usize, + }, + }); + }; + + let nested = parse_nested::<_, VERSION>(whitespace.span.end, tokens)?; + + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + + return Ok(Item::Optional { + opening_bracket, + _leading_whitespace: unused(leading_whitespace), + _optional_kw: unused(name), + _whitespace: unused(whitespace), + nested_format_description: nested, + closing_bracket, + }); + } + + if *name == b"first" { + let Some(whitespace) = tokens.next_if_whitespace() else { + return Err(Error { + _inner: unused(name.span.error("expected whitespace after `first`")), + public: crate::error::InvalidFormatDescription::Expected { + what: "whitespace after `first`", + index: name.span.end.byte as usize, + }, + }); + }; + + let mut nested_format_descriptions = Vec::new(); + while let Ok(description) = parse_nested::<_, VERSION>(whitespace.span.end, tokens) { + nested_format_descriptions.push(description); + } + + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + + return Ok(Item::First { + opening_bracket, + _leading_whitespace: unused(leading_whitespace), + _first_kw: unused(name), + _whitespace: unused(whitespace), + nested_format_descriptions: nested_format_descriptions.into_boxed_slice(), + closing_bracket, + }); + } + + let mut modifiers = Vec::new(); + let trailing_whitespace = loop { + 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. + if let Some(location) = tokens.next_if_opening_bracket() { + return Err(Error { + _inner: unused( + location + .to_self() + .error("modifier must be of the form `key:value`"), + ), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from("["), + index: location.byte as usize, + }, + }); + } + + let Some(Spanned { value, span }) = tokens.next_if_not_whitespace() else { + break Some(whitespace); + }; + + let Some(colon_index) = value.iter().position(|&b| b == b':') else { + return Err(Error { + _inner: unused(span.error("modifier must be of the form `key:value`")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(value).into_owned(), + index: span.start.byte as usize, + }, + }); + }; + let key = &value[..colon_index]; + let value = &value[colon_index + 1..]; + + if key.is_empty() { + return Err(Error { + _inner: unused(span.shrink_to_start().error("expected modifier key")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::new(), + index: span.start.byte as usize, + }, + }); + } + if value.is_empty() { + return Err(Error { + _inner: unused(span.shrink_to_end().error("expected modifier value")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::new(), + index: span.shrink_to_end().start.byte as usize, + }, + }); + } + + modifiers.push(Modifier { + _leading_whitespace: unused(whitespace), + key: key.spanned(span.shrink_to_before(colon_index as u32)), + _colon: unused(span.start.offset(colon_index as u32)), + value: value.spanned(span.shrink_to_after(colon_index as u32)), + }); + }; + + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + + Ok(Item::Component { + _opening_bracket: unused(opening_bracket), + _leading_whitespace: unused(leading_whitespace), + name, + modifiers: modifiers.into_boxed_slice(), + _trailing_whitespace: unused(trailing_whitespace), + _closing_bracket: unused(closing_bracket), + }) +} + +/// Parse a nested format description. The location provided is the the most recent one consumed. +fn parse_nested<'a, I: Iterator, Error>>, const VERSION: usize>( + last_location: Location, + tokens: &mut lexer::Lexed, +) -> Result, Error> { + validate_version!(VERSION); + let Some(opening_bracket) = tokens.next_if_opening_bracket() else { + return Err(Error { + _inner: unused(last_location.error("expected opening bracket")), + public: crate::error::InvalidFormatDescription::Expected { + what: "opening bracket", + index: last_location.byte as usize, + }, + }); + }; + let items = parse_inner::<_, true, VERSION>(tokens).collect::>()?; + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + let trailing_whitespace = tokens.next_if_whitespace(); + + Ok(NestedFormatDescription { + _opening_bracket: unused(opening_bracket), + items, + _closing_bracket: unused(closing_bracket), + _trailing_whitespace: unused(trailing_whitespace), + }) +} diff --git a/vendor/time/src/format_description/parse/format_item.rs b/vendor/time/src/format_description/parse/format_item.rs new file mode 100644 index 00000000..d401daa5 --- /dev/null +++ b/vendor/time/src/format_description/parse/format_item.rs @@ -0,0 +1,549 @@ +//! Typed, validated representation of a parsed format description. + +use alloc::boxed::Box; +use alloc::string::String; +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>( + ast_items: impl Iterator, Error>>, +) -> impl Iterator, Error>> { + ast_items.map(|ast_item| ast_item.and_then(Item::from_ast)) +} + +/// A description of how to format and parse one part of a type. +pub(super) enum Item<'a> { + /// A literal string. + Literal(&'a [u8]), + /// Part of a type, along with its modifiers. + Component(Component), + /// A sequence of optional items. + Optional { + /// The items themselves. + value: Box<[Self]>, + /// The span of the full sequence. + span: Span, + }, + /// The first matching parse of a sequence of format descriptions. + First { + /// The sequence of format descriptions. + value: Box<[Box<[Self]>]>, + /// The span of the full sequence. + span: Span, + }, +} + +impl Item<'_> { + /// Parse an AST item into a format item. + pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result, Error> { + Ok(match ast_item { + ast::Item::Component { + _opening_bracket: _, + _leading_whitespace: _, + name, + modifiers, + _trailing_whitespace: _, + _closing_bracket: _, + } => Item::Component(component_from_ast(&name, &modifiers)?), + ast::Item::Literal(Spanned { value, span: _ }) => Item::Literal(value), + ast::Item::EscapedBracket { + _first: _, + _second: _, + } => Item::Literal(b"["), + ast::Item::Optional { + opening_bracket, + _leading_whitespace: _, + _optional_kw: _, + _whitespace: _, + nested_format_description, + closing_bracket, + } => { + let items = nested_format_description + .items + .into_vec() + .into_iter() + .map(Item::from_ast) + .collect::>()?; + Item::Optional { + value: items, + span: opening_bracket.to(closing_bracket), + } + } + ast::Item::First { + opening_bracket, + _leading_whitespace: _, + _first_kw: _, + _whitespace: _, + nested_format_descriptions, + closing_bracket, + } => { + let items = nested_format_descriptions + .into_vec() + .into_iter() + .map(|nested_format_description| { + nested_format_description + .items + .into_vec() + .into_iter() + .map(Item::from_ast) + .collect() + }) + .collect::>()?; + Item::First { + value: items, + span: opening_bracket.to(closing_bracket), + } + } + }) + } +} + +impl<'a> TryFrom> for crate::format_description::BorrowedFormatItem<'a> { + type Error = Error; + + fn try_from(item: Item<'a>) -> Result { + match item { + Item::Literal(literal) => Ok(Self::Literal(literal)), + Item::Component(component) => Ok(Self::Component(component.into())), + Item::Optional { value: _, span } => Err(Error { + _inner: unused(span.error( + "optional items are not supported in runtime-parsed format descriptions", + )), + public: crate::error::InvalidFormatDescription::NotSupported { + what: "optional item", + context: "runtime-parsed format descriptions", + index: span.start.byte as usize, + }, + }), + Item::First { value: _, span } => Err(Error { + _inner: unused(span.error( + "'first' items are not supported in runtime-parsed format descriptions", + )), + public: crate::error::InvalidFormatDescription::NotSupported { + what: "'first' item", + context: "runtime-parsed format descriptions", + index: span.start.byte as usize, + }, + }), + } + } +} + +impl From> for crate::format_description::OwnedFormatItem { + fn from(item: Item<'_>) -> Self { + match item { + Item::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()), + Item::Component(component) => Self::Component(component.into()), + Item::Optional { value, span: _ } => Self::Optional(Box::new(value.into())), + Item::First { value, span: _ } => { + Self::First(value.into_vec().into_iter().map(Into::into).collect()) + } + } + } +} + +impl<'a> From]>> for crate::format_description::OwnedFormatItem { + fn from(items: Box<[Item<'a>]>) -> Self { + let items = items.into_vec(); + match <[_; 1]>::try_from(items) { + Ok([item]) => item.into(), + Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()), + } + } +} + +/// Declare the `Component` struct. +macro_rules! component_definition { + (@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* }; + (@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? }; + (@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* }; + (@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? }; + + ($vis:vis enum $name:ident { + $($variant:ident = $parse_variant:literal {$( + $(#[$required:tt])? + $field:ident = $parse_field:literal: + Option<$(#[$from_str:tt])? $field_type:ty> + => $target_field:ident + ),* $(,)?}),* $(,)? + }) => { + $vis enum $name { + $($variant($variant),)* + } + + $($vis struct $variant { + $($field: Option<$field_type>),* + })* + + $(impl $variant { + /// Parse the component from the AST, given its modifiers. + fn with_modifiers( + modifiers: &[ast::Modifier<'_>], + _component_span: Span, + ) -> Result + { + // rustc will complain if the modifier is empty. + #[allow(unused_mut)] + let mut this = Self { + $($field: None),* + }; + + for modifier in modifiers { + $(#[allow(clippy::string_lit_as_bytes)] + if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) { + this.$field = component_definition!(@if_from_str $($from_str)? + then { + parse_from_modifier_value::<$field_type>(&modifier.value)? + } else { + <$field_type>::from_modifier_value(&modifier.value)? + }); + continue; + })* + return Err(Error { + _inner: unused(modifier.key.span.error("invalid modifier key")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(*modifier.key).into_owned(), + index: modifier.key.span.start.byte as usize, + } + }); + } + + $(component_definition! { @if_required $($required)? then { + if this.$field.is_none() { + return Err(Error { + _inner: unused(_component_span.error("missing required modifier")), + public: + crate::error::InvalidFormatDescription::MissingRequiredModifier { + name: $parse_field, + index: _component_span.start.byte as usize, + } + }); + } + }})* + + Ok(this) + } + })* + + impl From<$name> for crate::format_description::Component { + fn from(component: $name) -> Self { + match component {$( + $name::$variant($variant { $($field),* }) => { + $crate::format_description::component::Component::$variant( + $crate::format_description::modifier::$variant {$( + $target_field: component_definition! { @if_required $($required)? + then { + match $field { + Some(value) => value.into(), + None => bug!("required modifier was not set"), + } + } else { + $field.unwrap_or_default().into() + } + } + ),*} + ) + } + )*} + } + } + + /// Parse a component from the AST, given its name and modifiers. + fn component_from_ast( + name: &Spanned<&[u8]>, + modifiers: &[ast::Modifier<'_>], + ) -> Result { + $(#[allow(clippy::string_lit_as_bytes)] + if name.eq_ignore_ascii_case($parse_variant.as_bytes()) { + return Ok(Component::$variant($variant::with_modifiers(&modifiers, name.span)?)); + })* + Err(Error { + _inner: unused(name.span.error("invalid component")), + public: crate::error::InvalidFormatDescription::InvalidComponentName { + name: String::from_utf8_lossy(name).into_owned(), + index: name.span.start.byte as usize, + }, + }) + } + } +} + +// Keep in alphabetical order. +component_definition! { + pub(super) enum Component { + Day = "day" { + padding = "padding": Option => padding, + }, + End = "end" {}, + Hour = "hour" { + padding = "padding": Option => padding, + base = "repr": Option => is_12_hour_clock, + }, + Ignore = "ignore" { + #[required] + count = "count": Option<#[from_str] NonZeroU16> => count, + }, + Minute = "minute" { + padding = "padding": Option => padding, + }, + Month = "month" { + padding = "padding": Option => padding, + repr = "repr": Option => repr, + case_sensitive = "case_sensitive": Option => case_sensitive, + }, + OffsetHour = "offset_hour" { + sign_behavior = "sign": Option => sign_is_mandatory, + padding = "padding": Option => padding, + }, + OffsetMinute = "offset_minute" { + padding = "padding": Option => padding, + }, + OffsetSecond = "offset_second" { + padding = "padding": Option => padding, + }, + Ordinal = "ordinal" { + padding = "padding": Option => padding, + }, + Period = "period" { + case = "case": Option => is_uppercase, + case_sensitive = "case_sensitive": Option => case_sensitive, + }, + Second = "second" { + padding = "padding": Option => padding, + }, + Subsecond = "subsecond" { + digits = "digits": Option => digits, + }, + UnixTimestamp = "unix_timestamp" { + precision = "precision": Option => precision, + sign_behavior = "sign": Option => sign_is_mandatory, + }, + Weekday = "weekday" { + repr = "repr": Option => repr, + one_indexed = "one_indexed": Option => one_indexed, + case_sensitive = "case_sensitive": Option => case_sensitive, + }, + WeekNumber = "week_number" { + padding = "padding": Option => padding, + repr = "repr": Option => repr, + }, + Year = "year" { + padding = "padding": Option => padding, + repr = "repr": Option => repr, + range = "range": Option => range, + base = "base": Option => iso_week_based, + sign_behavior = "sign": Option => sign_is_mandatory, + }, + } +} + +/// Get the target type for a given enum. +macro_rules! target_ty { + ($name:ident $type:ty) => { + $type + }; + ($name:ident) => { + $crate::format_description::modifier::$name + }; +} + +/// Get the target value for a given enum. +macro_rules! target_value { + ($name:ident $variant:ident $value:expr) => { + $value + }; + ($name:ident $variant:ident) => { + $crate::format_description::modifier::$name::$variant + }; +} + +/// Declare the various modifiers. +/// +/// For the general case, ordinary syntax can be used. Note that you _must_ declare a default +/// variant. The only significant change is that the string representation of the variant must be +/// provided after the variant name. For example, `Numerical = b"numerical"` declares a variant +/// named `Numerical` with the string representation `b"numerical"`. This is the value that will be +/// used when parsing the modifier. The value is not case sensitive. +/// +/// If the type in the public API does not have the same name as the type in the internal +/// representation, then the former must be specified in parenthesis after the internal name. For +/// example, `HourBase(bool)` has an internal name "HourBase", but is represented as a boolean in +/// the public API. +/// +/// By default, the internal variant name is assumed to be the same as the public variant name. If +/// this is not the case, the qualified path to the variant must be specified in parenthesis after +/// the internal variant name. For example, `Twelve(true)` has an internal variant name "Twelve", +/// but is represented as `true` in the public API. +macro_rules! modifier { + ($( + enum $name:ident $(($target_ty:ty))? { + $( + $(#[$attr:meta])? + $variant:ident $(($target_value:expr))? = $parse_variant:literal + ),* $(,)? + } + )+) => {$( + #[derive(Default)] + enum $name { + $($(#[$attr])? $variant),* + } + + impl $name { + /// Parse the modifier from its string representation. + fn from_modifier_value(value: &Spanned<&[u8]>) -> Result, Error> { + $(if value.eq_ignore_ascii_case($parse_variant) { + return Ok(Some(Self::$variant)); + })* + Err(Error { + _inner: unused(value.span.error("invalid modifier value")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(value).into_owned(), + index: value.span.start.byte as usize, + }, + }) + } + } + + impl From<$name> for target_ty!($name $($target_ty)?) { + fn from(modifier: $name) -> Self { + match modifier { + $($name::$variant => target_value!($name $variant $($target_value)?)),* + } + } + } + )+}; +} + +// Keep in alphabetical order. +modifier! { + enum HourBase(bool) { + Twelve(true) = b"12", + #[default] + TwentyFour(false) = b"24", + } + + enum MonthCaseSensitive(bool) { + False(false) = b"false", + #[default] + True(true) = b"true", + } + + enum MonthRepr { + #[default] + Numerical = b"numerical", + Long = b"long", + Short = b"short", + } + + enum Padding { + Space = b"space", + #[default] + Zero = b"zero", + None = b"none", + } + + enum PeriodCase(bool) { + Lower(false) = b"lower", + #[default] + Upper(true) = b"upper", + } + + enum PeriodCaseSensitive(bool) { + False(false) = b"false", + #[default] + True(true) = b"true", + } + + enum SignBehavior(bool) { + #[default] + Automatic(false) = b"automatic", + Mandatory(true) = b"mandatory", + } + + enum SubsecondDigits { + One = b"1", + Two = b"2", + Three = b"3", + Four = b"4", + Five = b"5", + Six = b"6", + Seven = b"7", + Eight = b"8", + Nine = b"9", + #[default] + OneOrMore = b"1+", + } + + enum UnixTimestampPrecision { + #[default] + Second = b"second", + Millisecond = b"millisecond", + Microsecond = b"microsecond", + Nanosecond = b"nanosecond", + } + + enum WeekNumberRepr { + #[default] + Iso = b"iso", + Sunday = b"sunday", + Monday = b"monday", + } + + enum WeekdayCaseSensitive(bool) { + False(false) = b"false", + #[default] + True(true) = b"true", + } + + enum WeekdayOneIndexed(bool) { + False(false) = b"false", + #[default] + True(true) = b"true", + } + + enum WeekdayRepr { + Short = b"short", + #[default] + Long = b"long", + Sunday = b"sunday", + Monday = b"monday", + } + + enum YearBase(bool) { + #[default] + Calendar(false) = b"calendar", + IsoWeek(true) = b"iso_week", + } + + enum YearRepr { + #[default] + Full = b"full", + Century = b"century", + LastTwo = b"last_two", + } + + enum YearRange { + Standard = b"standard", + #[default] + Extended = b"extended", + } +} + +/// Parse a modifier value using `FromStr`. Requires the modifier value to be valid UTF-8. +fn parse_from_modifier_value(value: &Spanned<&[u8]>) -> Result, Error> { + str::from_utf8(value) + .ok() + .and_then(|val| val.parse::().ok()) + .map(|val| Some(val)) + .ok_or_else(|| Error { + _inner: unused(value.span.error("invalid modifier value")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(value).into_owned(), + index: value.span.start.byte as usize, + }, + }) +} diff --git a/vendor/time/src/format_description/parse/lexer.rs b/vendor/time/src/format_description/parse/lexer.rs new file mode 100644 index 00000000..a63722e1 --- /dev/null +++ b/vendor/time/src/format_description/parse/lexer.rs @@ -0,0 +1,284 @@ +//! Lexer for parsing format descriptions. + +use core::iter; + +use super::{attach_location, unused, Error, Location, Spanned, SpannedValue}; + +/// An iterator over the lexed tokens. +pub(super) struct Lexed { + /// The internal iterator. + iter: iter::Peekable, +} + +impl Iterator for Lexed { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +impl<'iter, 'token: 'iter, I: Iterator, Error>> + 'iter> Lexed { + /// Peek at the next item in the iterator. + pub(super) fn peek(&mut self) -> Option<&I::Item> { + self.iter.peek() + } + + /// Consume the next token if it is whitespace. + pub(super) fn next_if_whitespace(&mut self) -> Option> { + if let Some(&Ok(Token::ComponentPart { + kind: ComponentKind::Whitespace, + value, + })) = self.peek() + { + self.next(); // consume + Some(value) + } else { + None + } + } + + /// Consume the next token if it is a component item that is not whitespace. + pub(super) fn next_if_not_whitespace(&mut self) -> Option> { + if let Some(&Ok(Token::ComponentPart { + kind: ComponentKind::NotWhitespace, + value, + })) = self.peek() + { + self.next(); // consume + Some(value) + } else { + None + } + } + + /// Consume the next token if it is an opening bracket. + pub(super) fn next_if_opening_bracket(&mut self) -> Option { + if let Some(&Ok(Token::Bracket { + kind: BracketKind::Opening, + location, + })) = self.peek() + { + self.next(); // consume + Some(location) + } else { + None + } + } + + /// Peek at the next token if it is a closing bracket. + pub(super) fn peek_closing_bracket(&'iter mut self) -> Option<&'iter Location> { + if let Some(Ok(Token::Bracket { + kind: BracketKind::Closing, + location, + })) = self.peek() + { + Some(location) + } else { + None + } + } + + /// Consume the next token if it is a closing bracket. + pub(super) fn next_if_closing_bracket(&mut self) -> Option { + if let Some(&Ok(Token::Bracket { + kind: BracketKind::Closing, + location, + })) = self.peek() + { + self.next(); // consume + Some(location) + } else { + None + } + } +} + +/// A token emitted by the lexer. There is no semantic meaning at this stage. +pub(super) enum Token<'a> { + /// A literal string, formatted and parsed as-is. + Literal(Spanned<&'a [u8]>), + /// An opening or closing bracket. May or may not be the start or end of a component. + Bracket { + /// Whether the bracket is opening or closing. + kind: BracketKind, + /// Where the bracket was in the format string. + location: Location, + }, + /// One part of a component. This could be its name, a modifier, or whitespace. + ComponentPart { + /// Whether the part is whitespace or not. + kind: ComponentKind, + /// The part itself. + value: Spanned<&'a [u8]>, + }, +} + +/// What type of bracket is present. +pub(super) enum BracketKind { + /// An opening bracket: `[` + Opening, + /// A closing bracket: `]` + Closing, +} + +/// Indicates whether the component is whitespace or not. +pub(super) enum ComponentKind { + Whitespace, + NotWhitespace, +} + +/// Parse the string into a series of [`Token`]s. +/// +/// `VERSION` controls the version of the format description that is being parsed. Currently, this +/// must be 1 or 2. +/// +/// - When `VERSION` is 1, `[[` is the only escape sequence, resulting in a literal `[`. +/// - When `VERSION` is 2, all escape sequences begin with `\`. The only characters that may +/// currently follow are `\`, `[`, and `]`, all of which result in the literal character. All +/// other characters result in a lex error. +pub(super) fn lex( + mut input: &[u8], +) -> Lexed, Error>>> { + validate_version!(VERSION); + + let mut depth: u8 = 0; + let mut iter = attach_location(input.iter()).peekable(); + let mut second_bracket_location = None; + + let iter = iter::from_fn(move || { + // The flag is only set when version is zero. + if version!(..=1) { + // There is a flag set to emit the second half of an escaped bracket pair. + if let Some(location) = second_bracket_location.take() { + return Some(Ok(Token::Bracket { + kind: BracketKind::Opening, + location, + })); + } + } + + Some(Ok(match iter.next()? { + // possible escape sequence + (b'\\', backslash_loc) if version!(2..) => { + match iter.next() { + Some((b'\\' | b'[' | b']', char_loc)) => { + // The escaped character is emitted as-is. + let char = &input[1..2]; + input = &input[2..]; + if depth == 0 { + Token::Literal(char.spanned(backslash_loc.to(char_loc))) + } else { + Token::ComponentPart { + kind: ComponentKind::NotWhitespace, + value: char.spanned(backslash_loc.to(char_loc)), + } + } + } + Some((_, loc)) => { + return Some(Err(Error { + _inner: unused(loc.error("invalid escape sequence")), + public: crate::error::InvalidFormatDescription::Expected { + what: "valid escape sequence", + index: loc.byte as usize, + }, + })); + } + None => { + return Some(Err(Error { + _inner: unused(backslash_loc.error("unexpected end of input")), + public: crate::error::InvalidFormatDescription::Expected { + what: "valid escape sequence", + index: backslash_loc.byte as usize, + }, + })); + } + } + } + // potentially escaped opening bracket + (b'[', location) if version!(..=1) => { + if let Some((_, second_location)) = iter.next_if(|&(&byte, _)| byte == b'[') { + // Escaped bracket. Store the location of the second so we can emit it later. + second_bracket_location = Some(second_location); + input = &input[2..]; + } else { + // opening bracket + depth += 1; + input = &input[1..]; + } + + Token::Bracket { + kind: BracketKind::Opening, + location, + } + } + // opening bracket + (b'[', location) => { + depth += 1; + input = &input[1..]; + + Token::Bracket { + kind: BracketKind::Opening, + location, + } + } + // closing bracket + (b']', location) if depth > 0 => { + depth -= 1; + input = &input[1..]; + + Token::Bracket { + kind: BracketKind::Closing, + location, + } + } + // literal + (_, start_location) if depth == 0 => { + let mut bytes = 1; + let mut end_location = start_location; + + while let Some((_, location)) = + iter.next_if(|&(&byte, _)| !((version!(2..) && byte == b'\\') || byte == b'[')) + { + end_location = location; + bytes += 1; + } + + let value = &input[..bytes]; + input = &input[bytes..]; + + Token::Literal(value.spanned(start_location.to(end_location))) + } + // component part + (byte, start_location) => { + let mut bytes = 1; + let mut end_location = start_location; + let is_whitespace = byte.is_ascii_whitespace(); + + while let Some((_, location)) = iter.next_if(|&(byte, _)| { + !matches!(byte, b'\\' | b'[' | b']') + && is_whitespace == byte.is_ascii_whitespace() + }) { + end_location = location; + bytes += 1; + } + + let value = &input[..bytes]; + input = &input[bytes..]; + + Token::ComponentPart { + kind: if is_whitespace { + ComponentKind::Whitespace + } else { + ComponentKind::NotWhitespace + }, + value: value.spanned(start_location.to(end_location)), + } + } + })) + }); + + Lexed { + iter: iter.peekable(), + } +} diff --git a/vendor/time/src/format_description/parse/mod.rs b/vendor/time/src/format_description/parse/mod.rs new file mode 100644 index 00000000..d058594d --- /dev/null +++ b/vendor/time/src/format_description/parse/mod.rs @@ -0,0 +1,262 @@ +//! Parser for format descriptions. + +use alloc::boxed::Box; +use alloc::vec::Vec; + +pub use self::strftime::{parse_strftime_borrowed, parse_strftime_owned}; +use crate::{error, format_description}; + +/// A helper macro to make version restrictions simpler to read and write. +macro_rules! version { + ($range:expr) => { + $range.contains(&VERSION) + }; +} + +/// A helper macro to statically validate the version (when used as a const parameter). +macro_rules! validate_version { + ($version:ident) => { + let _ = $crate::format_description::parse::Version::<$version>::IS_VALID; + }; +} + +mod ast; +mod format_item; +mod lexer; +mod strftime; + +/// A struct that is used to ensure that the version is valid. +struct Version; +impl Version { + /// A constant that panics if the version is not valid. This results in a post-monomorphization + /// error. + const IS_VALID: () = assert!(N >= 1 && N <= 2); +} + +/// Parse a sequence of items from the format description. +/// +/// The syntax for the format description can be found in [the +/// book](https://time-rs.github.io/book/api/format-description.html). +/// +/// This function exists for backward compatibility reasons. It is equivalent to calling +/// `parse_borrowed::<1>(s)`. In the future, this function will be deprecated in favor of +/// `parse_borrowed`. +pub fn parse( + s: &str, +) -> Result>, error::InvalidFormatDescription> { + parse_borrowed::<1>(s) +} + +/// Parse a sequence of items from the format description. +/// +/// The syntax for the format description can be found in [the +/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format +/// description is provided as the const parameter. **It is recommended to use version 2.** +pub fn parse_borrowed( + s: &str, +) -> Result>, error::InvalidFormatDescription> { + validate_version!(VERSION); + let mut lexed = lexer::lex::(s.as_bytes()); + let ast = ast::parse::<_, VERSION>(&mut lexed); + let format_items = format_item::parse(ast); + Ok(format_items + .map(|res| res.and_then(TryInto::try_into)) + .collect::>()?) +} + +/// Parse a sequence of items from the format description. +/// +/// The syntax for the format description can be found in [the +/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format +/// description is provided as the const parameter. +/// +/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means +/// that there is no lifetime that needs to be handled. **It is recommended to use version 2.** +/// +/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem +pub fn parse_owned( + s: &str, +) -> Result { + validate_version!(VERSION); + let mut lexed = lexer::lex::(s.as_bytes()); + let ast = ast::parse::<_, VERSION>(&mut lexed); + let format_items = format_item::parse(ast); + let items = format_items.collect::, _>>()?; + Ok(items.into()) +} + +/// Attach [`Location`] information to each byte in the iterator. +fn attach_location<'item>( + iter: impl Iterator, +) -> impl Iterator { + let mut byte_pos = 0; + + iter.map(move |byte| { + let location = Location { byte: byte_pos }; + byte_pos += 1; + (byte, location) + }) +} + +/// A location within a string. +#[derive(Clone, Copy)] +struct Location { + /// The zero-indexed byte of the string. + byte: u32, +} + +impl Location { + /// Create a new [`Span`] from `self` to `other`. + const fn to(self, end: Self) -> Span { + Span { start: self, end } + } + + /// Create a new [`Span`] consisting entirely of `self`. + const fn to_self(self) -> Span { + Span { + start: self, + end: self, + } + } + + /// Offset the location by the provided amount. + /// + /// Note that this assumes the resulting location is on the same line as the original location. + #[must_use = "this does not modify the original value"] + const fn offset(&self, offset: u32) -> Self { + Self { + byte: self.byte + offset, + } + } + + /// Create an error with the provided message at this location. + const fn error(self, message: &'static str) -> ErrorInner { + ErrorInner { + _message: message, + _span: Span { + start: self, + end: self, + }, + } + } +} + +/// A start and end point within a string. +#[derive(Clone, Copy)] +struct Span { + start: Location, + end: Location, +} + +impl Span { + /// Obtain a `Span` pointing at the start of the pre-existing span. + #[must_use = "this does not modify the original value"] + const fn shrink_to_start(&self) -> Self { + Self { + start: self.start, + end: self.start, + } + } + + /// Obtain a `Span` pointing at the end of the pre-existing span. + #[must_use = "this does not modify the original value"] + const fn shrink_to_end(&self) -> Self { + Self { + start: self.end, + end: self.end, + } + } + + /// Obtain a `Span` that ends before the provided position of the pre-existing span. + #[must_use = "this does not modify the original value"] + const fn shrink_to_before(&self, pos: u32) -> Self { + Self { + start: self.start, + end: Location { + byte: self.start.byte + pos - 1, + }, + } + } + + /// Obtain a `Span` that starts after provided position to the end of the pre-existing span. + #[must_use = "this does not modify the original value"] + const fn shrink_to_after(&self, pos: u32) -> Self { + Self { + start: Location { + byte: self.start.byte + pos + 1, + }, + end: self.end, + } + } + + /// Create an error with the provided message at this span. + const fn error(self, message: &'static str) -> ErrorInner { + ErrorInner { + _message: message, + _span: self, + } + } +} + +/// A value with an associated [`Span`]. +#[derive(Clone, Copy)] +struct Spanned { + /// The value. + value: T, + /// Where the value was in the format string. + span: Span, +} + +impl core::ops::Deref for Spanned { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +/// Helper trait to attach a [`Span`] to a value. +trait SpannedValue: Sized { + /// Attach a [`Span`] to a value. + fn spanned(self, span: Span) -> Spanned; +} + +impl SpannedValue for T { + fn spanned(self, span: Span) -> Spanned { + Spanned { value: self, span } + } +} + +/// The internal error type. +struct ErrorInner { + /// The message displayed to the user. + _message: &'static str, + /// Where the error originated. + _span: Span, +} + +/// A complete error description. +struct Error { + /// The internal error. + _inner: Unused, + /// The error needed for interoperability with the rest of `time`. + public: error::InvalidFormatDescription, +} + +impl From for error::InvalidFormatDescription { + fn from(error: Error) -> Self { + error.public + } +} + +/// A value that may be used in the future, but currently is not. +/// +/// This struct exists so that data can semantically be passed around without _actually_ passing it +/// around. This way the data still exists if it is needed in the future. +// `PhantomData` is not used directly because we don't want to introduce any trait implementations. +struct Unused(core::marker::PhantomData); + +/// Indicate that a value is currently unused. +fn unused(_: T) -> Unused { + Unused(core::marker::PhantomData) +} diff --git a/vendor/time/src/format_description/parse/strftime.rs b/vendor/time/src/format_description/parse/strftime.rs new file mode 100644 index 00000000..5fcaf184 --- /dev/null +++ b/vendor/time/src/format_description/parse/strftime.rs @@ -0,0 +1,487 @@ +use alloc::string::String; +use alloc::vec::Vec; +use core::iter; + +use crate::error::InvalidFormatDescription; +use crate::format_description::parse::{ + attach_location, unused, Error, ErrorInner, Location, Spanned, SpannedValue, Unused, +}; +use crate::format_description::{self, modifier, BorrowedFormatItem, Component}; + +/// Parse a sequence of items from the [`strftime` format description][strftime docs]. +/// +/// The only heap allocation required is for the `Vec` itself. All components are bound to the +/// lifetime of the input. +/// +/// [strftime docs]: https://man7.org/linux/man-pages/man3/strftime.3.html +#[doc(alias = "parse_strptime_borrowed")] +pub fn parse_strftime_borrowed( + s: &str, +) -> Result>, InvalidFormatDescription> { + let tokens = lex(s.as_bytes()); + let items = into_items(tokens).collect::>()?; + Ok(items) +} + +/// Parse a sequence of items from the [`strftime` format description][strftime docs]. +/// +/// This requires heap allocation for some owned items. +/// +/// [strftime docs]: https://man7.org/linux/man-pages/man3/strftime.3.html +#[doc(alias = "parse_strptime_owned")] +pub fn parse_strftime_owned( + s: &str, +) -> Result { + parse_strftime_borrowed(s).map(Into::into) +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum Padding { + /// The default padding for a numeric component. Indicated by no character. + Default, + /// Pad a numeric component with spaces. Indicated by an underscore. + Spaces, + /// Do not pad a numeric component. Indicated by a hyphen. + None, + /// Pad a numeric component with zeroes. Indicated by a zero. + Zeroes, +} + +enum Token<'a> { + Literal(Spanned<&'a [u8]>), + Component { + _percent: Unused, + padding: Spanned, + component: Spanned, + }, +} + +fn lex(mut input: &[u8]) -> iter::Peekable, Error>>> { + let mut iter = attach_location(input.iter()).peekable(); + + iter::from_fn(move || { + Some(Ok(match iter.next()? { + (b'%', percent_loc) => match iter.next() { + Some((padding @ (b'_' | b'-' | b'0'), padding_loc)) => { + let padding = match padding { + b'_' => Padding::Spaces, + b'-' => Padding::None, + b'0' => Padding::Zeroes, + _ => unreachable!(), + }; + let (&component, component_loc) = iter.next()?; + input = &input[3..]; + Token::Component { + _percent: unused(percent_loc), + padding: padding.spanned(padding_loc.to_self()), + component: component.spanned(component_loc.to_self()), + } + } + Some((&component, component_loc)) => { + input = &input[2..]; + let span = component_loc.to_self(); + Token::Component { + _percent: unused(percent_loc), + padding: Padding::Default.spanned(span), + component: component.spanned(span), + } + } + None => { + return Some(Err(Error { + _inner: unused(percent_loc.error("unexpected end of input")), + public: InvalidFormatDescription::Expected { + what: "valid escape sequence", + index: percent_loc.byte as usize, + }, + })); + } + }, + (_, start_location) => { + let mut bytes = 1; + let mut end_location = start_location; + + while let Some((_, location)) = iter.next_if(|&(&byte, _)| byte != b'%') { + end_location = location; + bytes += 1; + } + + let value = &input[..bytes]; + input = &input[bytes..]; + + Token::Literal(value.spanned(start_location.to(end_location))) + } + })) + }) + .peekable() +} + +fn into_items<'iter, 'token: 'iter>( + mut tokens: iter::Peekable, Error>> + 'iter>, +) -> impl Iterator, Error>> + 'iter { + iter::from_fn(move || { + let next = match tokens.next()? { + Ok(token) => token, + Err(err) => return Some(Err(err)), + }; + + Some(match next { + Token::Literal(spanned) => Ok(BorrowedFormatItem::Literal(*spanned)), + Token::Component { + _percent, + padding, + component, + } => parse_component(padding, component), + }) + }) +} + +fn parse_component( + padding: Spanned, + component: Spanned, +) -> Result, Error> { + let padding_or_default = |padding: Padding, default| match padding { + Padding::Default => default, + Padding::Spaces => modifier::Padding::Space, + Padding::None => modifier::Padding::None, + Padding::Zeroes => modifier::Padding::Zero, + }; + + /// Helper macro to create a component. + macro_rules! component { + ($name:ident { $($inner:tt)* }) => { + BorrowedFormatItem::Component(Component::$name(modifier::$name { + $($inner)* + })) + } + } + + Ok(match *component { + b'%' => BorrowedFormatItem::Literal(b"%"), + b'a' => component!(Weekday { + repr: modifier::WeekdayRepr::Short, + one_indexed: true, + case_sensitive: true, + }), + b'A' => component!(Weekday { + repr: modifier::WeekdayRepr::Long, + one_indexed: true, + case_sensitive: true, + }), + b'b' | b'h' => component!(Month { + repr: modifier::MonthRepr::Short, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + b'B' => component!(Month { + repr: modifier::MonthRepr::Long, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + b'c' => BorrowedFormatItem::Compound(&[ + component!(Weekday { + repr: modifier::WeekdayRepr::Short, + one_indexed: true, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b" "), + component!(Month { + repr: modifier::MonthRepr::Short, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b" "), + component!(Day { + padding: modifier::Padding::Space + }), + BorrowedFormatItem::Literal(b" "), + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b" "), + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + ]), + b'C' => component!(Year { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::YearRepr::Century, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + b'd' => component!(Day { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b'D' => BorrowedFormatItem::Compound(&[ + component!(Month { + repr: modifier::MonthRepr::Numerical, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b"/"), + component!(Day { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b"/"), + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + ]), + b'e' => component!(Day { + padding: padding_or_default(*padding, modifier::Padding::Space), + }), + b'F' => BorrowedFormatItem::Compound(&[ + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + BorrowedFormatItem::Literal(b"-"), + component!(Month { + padding: modifier::Padding::Zero, + repr: modifier::MonthRepr::Numerical, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b"-"), + component!(Day { + padding: modifier::Padding::Zero, + }), + ]), + b'g' => component!(Year { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: true, + sign_is_mandatory: false, + }), + b'G' => component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: true, + sign_is_mandatory: false, + }), + b'H' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Zero), + is_12_hour_clock: false, + }), + b'I' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Zero), + is_12_hour_clock: true, + }), + b'j' => component!(Ordinal { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b'k' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Space), + is_12_hour_clock: false, + }), + b'l' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Space), + is_12_hour_clock: true, + }), + b'm' => component!(Month { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::MonthRepr::Numerical, + case_sensitive: true, + }), + b'M' => component!(Minute { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b'n' => BorrowedFormatItem::Literal(b"\n"), + b'O' => { + return Err(Error { + _inner: unused(ErrorInner { + _message: "unsupported modifier", + _span: component.span, + }), + public: InvalidFormatDescription::NotSupported { + what: "modifier", + context: "", + index: component.span.start.byte as usize, + }, + }) + } + b'p' => component!(Period { + is_uppercase: true, + case_sensitive: true + }), + b'P' => component!(Period { + is_uppercase: false, + case_sensitive: true + }), + b'r' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: true, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b" "), + component!(Period { + is_uppercase: true, + case_sensitive: true, + }), + ]), + b'R' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + ]), + b's' => component!(UnixTimestamp { + precision: modifier::UnixTimestampPrecision::Second, + sign_is_mandatory: false, + }), + b'S' => component!(Second { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b't' => BorrowedFormatItem::Literal(b"\t"), + b'T' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + ]), + b'u' => component!(Weekday { + repr: modifier::WeekdayRepr::Monday, + one_indexed: true, + case_sensitive: true, + }), + b'U' => component!(WeekNumber { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::WeekNumberRepr::Sunday, + }), + b'V' => component!(WeekNumber { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::WeekNumberRepr::Iso, + }), + b'w' => component!(Weekday { + repr: modifier::WeekdayRepr::Sunday, + one_indexed: true, + case_sensitive: true, + }), + b'W' => component!(WeekNumber { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::WeekNumberRepr::Monday, + }), + b'x' => BorrowedFormatItem::Compound(&[ + component!(Month { + repr: modifier::MonthRepr::Numerical, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b"/"), + component!(Day { + padding: modifier::Padding::Zero + }), + BorrowedFormatItem::Literal(b"/"), + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + ]), + b'X' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + ]), + b'y' => component!(Year { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + b'Y' => component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + b'z' => BorrowedFormatItem::Compound(&[ + component!(OffsetHour { + sign_is_mandatory: true, + padding: modifier::Padding::Zero, + }), + component!(OffsetMinute { + padding: modifier::Padding::Zero, + }), + ]), + b'Z' => { + return Err(Error { + _inner: unused(ErrorInner { + _message: "unsupported component", + _span: component.span, + }), + public: InvalidFormatDescription::NotSupported { + what: "component", + context: "", + index: component.span.start.byte as usize, + }, + }) + } + _ => { + return Err(Error { + _inner: unused(ErrorInner { + _message: "invalid component", + _span: component.span, + }), + public: InvalidFormatDescription::InvalidComponentName { + name: String::from_utf8_lossy(&[*component]).into_owned(), + index: component.span.start.byte as usize, + }, + }) + } + }) +} diff --git a/vendor/time/src/format_description/well_known/iso8601.rs b/vendor/time/src/format_description/well_known/iso8601.rs new file mode 100644 index 00000000..6b8ff2ab --- /dev/null +++ b/vendor/time/src/format_description/well_known/iso8601.rs @@ -0,0 +1,257 @@ +//! The format described in ISO 8601. + +mod adt_hack; + +use core::num::NonZeroU8; + +#[doc(hidden, no_inline)] +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). +/// +/// This implementation is of ISO 8601-1:2019. It may not be compatible with other versions. +/// +/// The const parameter `CONFIG` **must** be a value that was returned by [`Config::encode`]. +/// Passing any other value is **unspecified behavior**. +/// +/// Example: 1997-11-21T09:55:06.000000000-06:00 +/// +/// # Examples +#[cfg_attr(feature = "formatting", doc = "```rust")] +#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")] +/// # use time::format_description::well_known::Iso8601; +/// # use time_macros::datetime; +/// assert_eq!( +/// datetime!(1997-11-12 9:55:06 -6:00).format(&Iso8601::DEFAULT)?, +/// "1997-11-12T09:55:06.000000000-06:00" +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Iso8601; + +impl core::fmt::Debug for Iso8601 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Iso8601") + .field("config", &Config::decode(CONFIG)) + .finish() + } +} + +/// 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: + /// + /// - The configuration can be used for both formatting and parsing. + /// - The date, time, and UTC offset are all formatted. + /// - Separators (such as `-` and `:`) are included. + /// - The year contains four digits, such that the year must be between 0 and 9999. + /// - The date uses the calendar format. + /// - 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 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 = 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. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FormattedComponents { + /// The configuration can only be used for parsing. Using this to format a value is + /// unspecified behavior. + None, + /// Format only the date. + Date, + /// Format only the time. + Time, + /// Format only the UTC offset. + Offset, + /// Format the date and time. + DateTime, + /// Format the date, time, and UTC offset. + DateTimeOffset, + /// Format the time and UTC offset. + TimeOffset, +} + +/// Which format to use for the date. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DateKind { + /// Use the year-month-day format. + Calendar, + /// Use the year-week-weekday format. + Week, + /// Use the week-ordinal format. + Ordinal, +} + +/// The precision and number of decimal digits present for the time. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +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(missing_docs)] + decimal_digits: Option, + }, + /// Format the hour and minute. Seconds and nanoseconds will be represented with the specified + /// number of decimal digits, if any. + Minute { + #[allow(missing_docs)] + decimal_digits: Option, + }, + /// Format the hour, minute, and second. Nanoseconds will be represented with the specified + /// number of decimal digits, if any. + Second { + #[allow(missing_docs)] + decimal_digits: Option, + }, +} + +/// The precision for the UTC offset. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OffsetPrecision { + /// Format only the offset hour. Requires the offset minute to be zero. + Hour, + /// Format both the offset hour and minute. + Minute, +} + +/// Configuration for [`Iso8601`]. +// This is only used as a const generic, so there's no need to have a number of implementations on +// it. +#[allow(missing_copy_implementations)] +#[doc(alias = "EncodedConfig")] // People will likely search for `EncodedConfig`, so show them this. +#[derive(Debug)] +pub struct Config { + /// Which components, if any, will be formatted. + pub(crate) formatted_components: FormattedComponents, + /// Whether the format contains separators (such as `-` or `:`). + pub(crate) use_separators: bool, + /// Whether the year is six digits. + pub(crate) year_is_six_digits: bool, + /// The format used for the date. + pub(crate) date_kind: DateKind, + /// The precision and number of decimal digits present for the time. + pub(crate) time_precision: TimePrecision, + /// The precision for the UTC offset. + pub(crate) offset_precision: OffsetPrecision, +} + +impl Config { + /// A configuration for the [`Iso8601`] format. + /// + /// The following is the default behavior: + /// + /// - The configuration can be used for both formatting and parsing. + /// - The date, time, and UTC offset are all formatted. + /// - Separators (such as `-` and `:`) are included. + /// - The year contains four digits, such that the year must be between 0 and 9999. + /// - The date uses the calendar format. + /// - 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 the setter methods on this struct. + pub const DEFAULT: Self = Self { + formatted_components: FormattedComponents::DateTimeOffset, + use_separators: true, + year_is_six_digits: false, + date_kind: DateKind::Calendar, + time_precision: TimePrecision::Second { + decimal_digits: NonZeroU8::new(9), + }, + 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 { + formatted_components, + ..self + } + } + + /// Set whether the format contains separators (such as `-` or `:`). + pub const fn set_use_separators(self, use_separators: bool) -> Self { + Self { + use_separators, + ..self + } + } + + /// Set whether the year is six digits. + pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self { + Self { + year_is_six_digits, + ..self + } + } + + /// Set the format used for the date. + pub const fn set_date_kind(self, date_kind: DateKind) -> Self { + Self { date_kind, ..self } + } + + /// Set the precision and number of decimal digits present for the time. + pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self { + Self { + time_precision, + ..self + } + } + + /// Set the precision for the UTC offset. + pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self { + Self { + offset_precision, + ..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 new file mode 100644 index 00000000..6d3daebb --- /dev/null +++ b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs @@ -0,0 +1,247 @@ +//! Hackery to work around not being able to use ADTs in const generics on stable. + +use core::num::NonZeroU8; + +#[cfg(feature = "formatting")] +use super::Iso8601; +use super::{Config, DateKind, FormattedComponents as FC, OffsetPrecision, TimePrecision}; + +// This provides a way to include `EncodedConfig` in documentation without displaying the type it is +// aliased to. +#[doc(hidden)] +pub type DoNotRelyOnWhatThisIs = u128; + +/// An encoded [`Config`] that can be used as a const parameter to [`Iso8601`](super::Iso8601). +/// +/// The type this is aliased to must not be relied upon. It can change in any release without +/// notice. +pub type EncodedConfig = DoNotRelyOnWhatThisIs; + +#[cfg(feature = "formatting")] +impl Iso8601 { + /// The user-provided configuration for the ISO 8601 format. + const CONFIG: Config = Config::decode(CONFIG); + /// Whether the date should be formatted. + pub(crate) const FORMAT_DATE: bool = matches!( + Self::CONFIG.formatted_components, + FC::Date | FC::DateTime | FC::DateTimeOffset + ); + /// Whether the time should be formatted. + pub(crate) const FORMAT_TIME: bool = matches!( + Self::CONFIG.formatted_components, + FC::Time | FC::DateTime | FC::DateTimeOffset | FC::TimeOffset + ); + /// Whether the UTC offset should be formatted. + pub(crate) const FORMAT_OFFSET: bool = matches!( + Self::CONFIG.formatted_components, + FC::Offset | FC::DateTimeOffset | FC::TimeOffset + ); + /// Whether the year is six digits. + pub(crate) const YEAR_IS_SIX_DIGITS: bool = Self::CONFIG.year_is_six_digits; + /// Whether the format contains separators (such as `-` or `:`). + pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators; + /// Which format to use for the date. + pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind; + /// The precision and number of decimal digits to use for the time. + pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision; + /// The precision for the UTC offset. + pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision; +} + +impl Config { + /// 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`]. Any + /// other usage is unspecified behavior. + pub const fn encode(&self) -> EncodedConfig { + let mut bytes = [0; EncodedConfig::BITS as usize / 8]; + + bytes[0] = match self.formatted_components { + FC::None => 0, + FC::Date => 1, + FC::Time => 2, + FC::Offset => 3, + FC::DateTime => 4, + FC::DateTimeOffset => 5, + FC::TimeOffset => 6, + }; + bytes[1] = self.use_separators as u8; + bytes[2] = self.year_is_six_digits as u8; + bytes[3] = match self.date_kind { + DateKind::Calendar => 0, + DateKind::Week => 1, + DateKind::Ordinal => 2, + }; + bytes[4] = match self.time_precision { + TimePrecision::Hour { .. } => 0, + TimePrecision::Minute { .. } => 1, + TimePrecision::Second { .. } => 2, + }; + bytes[5] = match self.time_precision { + TimePrecision::Hour { decimal_digits } + | TimePrecision::Minute { decimal_digits } + | TimePrecision::Second { decimal_digits } => match decimal_digits { + None => 0, + Some(decimal_digits) => decimal_digits.get(), + }, + }; + bytes[6] = match self.offset_precision { + OffsetPrecision::Hour => 0, + OffsetPrecision::Minute => 1, + }; + + EncodedConfig::from_be_bytes(bytes) + } + + /// Decode the configuration. The configuration must have been generated from + /// [`Config::encode`]. + pub(super) const fn decode(encoded: EncodedConfig) -> Self { + let bytes = encoded.to_be_bytes(); + + let formatted_components = match bytes[0] { + 0 => FC::None, + 1 => FC::Date, + 2 => FC::Time, + 3 => FC::Offset, + 4 => FC::DateTime, + 5 => FC::DateTimeOffset, + 6 => FC::TimeOffset, + _ => panic!("invalid configuration"), + }; + let use_separators = match bytes[1] { + 0 => false, + 1 => true, + _ => panic!("invalid configuration"), + }; + let year_is_six_digits = match bytes[2] { + 0 => false, + 1 => true, + _ => panic!("invalid configuration"), + }; + let date_kind = match bytes[3] { + 0 => DateKind::Calendar, + 1 => DateKind::Week, + 2 => DateKind::Ordinal, + _ => panic!("invalid configuration"), + }; + let time_precision = match bytes[4] { + 0 => TimePrecision::Hour { + decimal_digits: NonZeroU8::new(bytes[5]), + }, + 1 => TimePrecision::Minute { + decimal_digits: NonZeroU8::new(bytes[5]), + }, + 2 => TimePrecision::Second { + decimal_digits: NonZeroU8::new(bytes[5]), + }, + _ => panic!("invalid configuration"), + }; + let offset_precision = match bytes[6] { + 0 => OffsetPrecision::Hour, + 1 => OffsetPrecision::Minute, + _ => panic!("invalid configuration"), + }; + + // No `for` loops in `const fn`. + let mut idx = 7; // first unused byte + while idx < EncodedConfig::BITS as usize / 8 { + assert!(bytes[idx] == 0, "invalid configuration"); + idx += 1; + } + + Self { + formatted_components, + use_separators, + year_is_six_digits, + date_kind, + time_precision, + offset_precision, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! eq { + ($a:expr, $b:expr) => {{ + let a = $a; + let b = $b; + a.formatted_components == b.formatted_components + && a.use_separators == b.use_separators + && a.year_is_six_digits == b.year_is_six_digits + && a.date_kind == b.date_kind + && a.time_precision == b.time_precision + && a.offset_precision == b.offset_precision + }}; + } + + #[test] + fn encoding_roundtrip() { + macro_rules! assert_roundtrip { + ($config:expr) => { + let config = $config; + let encoded = config.encode(); + let decoded = Config::decode(encoded); + assert!(eq!(config, decoded)); + }; + } + + assert_roundtrip!(Config::DEFAULT); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::None)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Date)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Time)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Offset)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTime)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTimeOffset)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::TimeOffset)); + assert_roundtrip!(Config::DEFAULT.set_use_separators(false)); + assert_roundtrip!(Config::DEFAULT.set_use_separators(true)); + assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(false)); + assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(true)); + assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Calendar)); + assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Week)); + assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Ordinal)); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour { + decimal_digits: None, + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute { + decimal_digits: None, + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second { + decimal_digits: None, + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour { + decimal_digits: NonZeroU8::new(1), + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute { + decimal_digits: NonZeroU8::new(1), + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second { + decimal_digits: NonZeroU8::new(1), + })); + assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Hour)); + assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Minute)); + } + + macro_rules! assert_decode_fail { + ($encoding:expr) => { + assert!(std::panic::catch_unwind(|| { + Config::decode($encoding); + }) + .is_err()); + }; + } + + #[test] + fn decode_fail() { + assert_decode_fail!(0x07_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_02_00_00_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_02_00_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_03_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_00_03_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_00_00_00_02_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_00_00_00_00_01_00_00_00_00_00_00_00_00); + } +} diff --git a/vendor/time/src/format_description/well_known/rfc2822.rs b/vendor/time/src/format_description/well_known/rfc2822.rs new file mode 100644 index 00000000..3c890ab1 --- /dev/null +++ b/vendor/time/src/format_description/well_known/rfc2822.rs @@ -0,0 +1,30 @@ +//! The format described in RFC 2822. + +/// The format described in [RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.3). +/// +/// Example: Fri, 21 Nov 1997 09:55:06 -0600 +/// +/// # Examples +#[cfg_attr(feature = "parsing", doc = "```rust")] +#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")] +/// # use time::{format_description::well_known::Rfc2822, OffsetDateTime}; +/// use time_macros::datetime; +/// assert_eq!( +/// OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?, +/// datetime!(1993-06-12 13:25:19 +00:00) +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +/// +#[cfg_attr(feature = "formatting", doc = "```rust")] +#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")] +/// # use time::format_description::well_known::Rfc2822; +/// # use time_macros::datetime; +/// assert_eq!( +/// datetime!(1997-11-21 09:55:06 -06:00).format(&Rfc2822)?, +/// "Fri, 21 Nov 1997 09:55:06 -0600" +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rfc2822; diff --git a/vendor/time/src/format_description/well_known/rfc3339.rs b/vendor/time/src/format_description/well_known/rfc3339.rs new file mode 100644 index 00000000..f0873cba --- /dev/null +++ b/vendor/time/src/format_description/well_known/rfc3339.rs @@ -0,0 +1,30 @@ +//! The format described in RFC 3339. + +/// The format described in [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6). +/// +/// Format example: 1985-04-12T23:20:50.52Z +/// +/// # Examples +#[cfg_attr(feature = "parsing", doc = "```rust")] +#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")] +/// # use time::{format_description::well_known::Rfc3339, OffsetDateTime}; +/// # use time_macros::datetime; +/// assert_eq!( +/// OffsetDateTime::parse("1985-04-12T23:20:50.52Z", &Rfc3339)?, +/// datetime!(1985-04-12 23:20:50.52 +00:00) +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +/// +#[cfg_attr(feature = "formatting", doc = "```rust")] +#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")] +/// # use time::format_description::well_known::Rfc3339; +/// # use time_macros::datetime; +/// assert_eq!( +/// datetime!(1985-04-12 23:20:50.52 +00:00).format(&Rfc3339)?, +/// "1985-04-12T23:20:50.52Z" +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rfc3339; diff --git a/vendor/time/src/formatting/formattable.rs b/vendor/time/src/formatting/formattable.rs new file mode 100644 index 00000000..7d1ce031 --- /dev/null +++ b/vendor/time/src/formatting/formattable.rs @@ -0,0 +1,312 @@ +//! A trait that can be used to format an item from its components. + +use alloc::string::String; +use alloc::vec::Vec; +use core::ops::Deref; +use std::io; + +use num_conv::prelude::*; + +use crate::format_description::well_known::iso8601::EncodedConfig; +use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339}; +use crate::format_description::{BorrowedFormatItem, OwnedFormatItem}; +use crate::formatting::{ + format_component, format_number_pad_zero, iso8601, write, MONTH_NAMES, WEEKDAY_NAMES, +}; +use crate::{error, Date, Time, UtcOffset}; + +/// A type that describes a format. +/// +/// Implementors of [`Formattable`] are [format descriptions](crate::format_description). +/// +/// [`Date::format`] and [`Time::format`] each use a format description to generate +/// a String from their data. See the respective methods for usage examples. +#[cfg_attr(docsrs, doc(notable_trait))] +pub trait Formattable: sealed::Sealed {} +impl Formattable for BorrowedFormatItem<'_> {} +impl Formattable for [BorrowedFormatItem<'_>] {} +impl Formattable for OwnedFormatItem {} +impl Formattable for [OwnedFormatItem] {} +impl Formattable for Rfc3339 {} +impl Formattable for Rfc2822 {} +impl Formattable for Iso8601 {} +impl Formattable for T where T::Target: Formattable {} + +/// Seal the trait to prevent downstream users from implementing it. +mod sealed { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Format the item using a format description, the intended output, and the various components. + pub trait Sealed { + /// Format the item into the provided output, returning the number of bytes written. + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option, + time: Option