diff options
Diffstat (limited to 'third_party/rust/time/src/time.rs')
-rw-r--r-- | third_party/rust/time/src/time.rs | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/third_party/rust/time/src/time.rs b/third_party/rust/time/src/time.rs new file mode 100644 index 0000000000..32fa97790f --- /dev/null +++ b/third_party/rust/time/src/time.rs @@ -0,0 +1,761 @@ +//! The [`Time`] struct and its associated `impl`s. + +use core::fmt; +use core::ops::{Add, Sub}; +use core::time::Duration as StdDuration; +#[cfg(feature = "formatting")] +use std::io; + +#[cfg(feature = "formatting")] +use crate::formatting::Formattable; +#[cfg(feature = "parsing")] +use crate::parsing::Parsable; +use crate::util::DateAdjustment; +use crate::{error, Duration}; + +/// By explicitly inserting this enum where padding is expected, the compiler is able to better +/// perform niche value optimization. +#[repr(u8)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) enum Padding { + #[allow(clippy::missing_docs_in_private_items)] + Optimize, +} + +/// The clock time within a given date. Nanosecond precision. +/// +/// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds +/// (either positive or negative). +/// +/// When comparing two `Time`s, they are assumed to be in the same calendar date. +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Time { + #[allow(clippy::missing_docs_in_private_items)] + hour: u8, + #[allow(clippy::missing_docs_in_private_items)] + minute: u8, + #[allow(clippy::missing_docs_in_private_items)] + second: u8, + #[allow(clippy::missing_docs_in_private_items)] + nanosecond: u32, + #[allow(clippy::missing_docs_in_private_items)] + padding: Padding, +} + +impl Time { + /// Create a `Time` that is exactly midnight. + /// + /// ```rust + /// # use time::Time; + /// # use time_macros::time; + /// assert_eq!(Time::MIDNIGHT, time!(0:00)); + /// ``` + pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0); + + /// The smallest value that can be represented by `Time`. + /// + /// `00:00:00.0` + pub(crate) const MIN: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0); + + /// The largest value that can be represented by `Time`. + /// + /// `23:59:59.999_999_999` + pub(crate) const MAX: Self = Self::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999); + + // region: constructors + /// Create a `Time` from its components. + #[doc(hidden)] + pub const fn __from_hms_nanos_unchecked( + hour: u8, + minute: u8, + second: u8, + nanosecond: u32, + ) -> Self { + debug_assert!(hour < 24); + debug_assert!(minute < 60); + debug_assert!(second < 60); + debug_assert!(nanosecond < 1_000_000_000); + + Self { + hour, + minute, + second, + nanosecond, + padding: Padding::Optimize, + } + } + + /// Attempt to create a `Time` from the hour, minute, and second. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms(1, 2, 3).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms(24, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms(0, 60, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second. + /// ``` + pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(hour in 0 => 23); + ensure_value_in_range!(minute in 0 => 59); + ensure_value_in_range!(second in 0 => 59); + Ok(Self::__from_hms_nanos_unchecked(hour, minute, second, 0)) + } + + /// Attempt to create a `Time` from the hour, minute, second, and millisecond. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_milli(1, 2, 3, 4).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_milli(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms_milli(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms_milli(0, 0, 60, 0).is_err()); // 60 isn't a valid second. + /// assert!(Time::from_hms_milli(0, 0, 0, 1_000).is_err()); // 1_000 isn't a valid millisecond. + /// ``` + pub const fn from_hms_milli( + hour: u8, + minute: u8, + second: u8, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(hour in 0 => 23); + ensure_value_in_range!(minute in 0 => 59); + ensure_value_in_range!(second in 0 => 59); + ensure_value_in_range!(millisecond in 0 => 999); + Ok(Self::__from_hms_nanos_unchecked( + hour, + minute, + second, + millisecond as u32 * 1_000_000, + )) + } + + /// Attempt to create a `Time` from the hour, minute, second, and microsecond. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_micro(1, 2, 3, 4).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_micro(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms_micro(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms_micro(0, 0, 60, 0).is_err()); // 60 isn't a valid second. + /// assert!(Time::from_hms_micro(0, 0, 0, 1_000_000).is_err()); // 1_000_000 isn't a valid microsecond. + /// ``` + pub const fn from_hms_micro( + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(hour in 0 => 23); + ensure_value_in_range!(minute in 0 => 59); + ensure_value_in_range!(second in 0 => 59); + ensure_value_in_range!(microsecond in 0 => 999_999); + Ok(Self::__from_hms_nanos_unchecked( + hour, + minute, + second, + microsecond * 1_000, + )) + } + + /// Attempt to create a `Time` from the hour, minute, second, and nanosecond. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_nano(1, 2, 3, 4).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_nano(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms_nano(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms_nano(0, 0, 60, 0).is_err()); // 60 isn't a valid second. + /// assert!(Time::from_hms_nano(0, 0, 0, 1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond. + /// ``` + pub const fn from_hms_nano( + hour: u8, + minute: u8, + second: u8, + nanosecond: u32, + ) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(hour in 0 => 23); + ensure_value_in_range!(minute in 0 => 59); + ensure_value_in_range!(second in 0 => 59); + ensure_value_in_range!(nanosecond in 0 => 999_999_999); + Ok(Self::__from_hms_nanos_unchecked( + hour, minute, second, nanosecond, + )) + } + // endregion constructors + + // region: getters + /// Get the clock hour, minute, and second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms(), (0, 0, 0)); + /// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59)); + /// ``` + pub const fn as_hms(self) -> (u8, u8, u8) { + (self.hour, self.minute, self.second) + } + + /// Get the clock hour, minute, second, and millisecond. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms_milli(), (0, 0, 0, 0)); + /// assert_eq!(time!(23:59:59.999).as_hms_milli(), (23, 59, 59, 999)); + /// ``` + pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { + ( + self.hour, + self.minute, + self.second, + (self.nanosecond / 1_000_000) as u16, + ) + } + + /// Get the clock hour, minute, second, and microsecond. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms_micro(), (0, 0, 0, 0)); + /// assert_eq!( + /// time!(23:59:59.999_999).as_hms_micro(), + /// (23, 59, 59, 999_999) + /// ); + /// ``` + pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { + (self.hour, self.minute, self.second, self.nanosecond / 1_000) + } + + /// Get the clock hour, minute, second, and nanosecond. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms_nano(), (0, 0, 0, 0)); + /// assert_eq!( + /// time!(23:59:59.999_999_999).as_hms_nano(), + /// (23, 59, 59, 999_999_999) + /// ); + /// ``` + pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { + (self.hour, self.minute, self.second, self.nanosecond) + } + + /// Get the clock hour. + /// + /// The returned value will always be in the range `0..24`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).hour(), 0); + /// assert_eq!(time!(23:59:59).hour(), 23); + /// ``` + pub const fn hour(self) -> u8 { + self.hour + } + + /// Get the minute within the hour. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).minute(), 0); + /// assert_eq!(time!(23:59:59).minute(), 59); + /// ``` + pub const fn minute(self) -> u8 { + self.minute + } + + /// Get the second within the minute. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).second(), 0); + /// assert_eq!(time!(23:59:59).second(), 59); + /// ``` + pub const fn second(self) -> u8 { + self.second + } + + /// Get the milliseconds within the second. + /// + /// The returned value will always be in the range `0..1_000`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00).millisecond(), 0); + /// assert_eq!(time!(23:59:59.999).millisecond(), 999); + /// ``` + pub const fn millisecond(self) -> u16 { + (self.nanosecond / 1_000_000) as _ + } + + /// Get the microseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00).microsecond(), 0); + /// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999); + /// ``` + pub const fn microsecond(self) -> u32 { + self.nanosecond / 1_000 + } + + /// Get the nanoseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000_000`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00).nanosecond(), 0); + /// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999); + /// ``` + pub const fn nanosecond(self) -> u32 { + self.nanosecond + } + // endregion getters + + // region: arithmetic helpers + /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether + /// the date is different. + pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) { + let mut nanoseconds = self.nanosecond as i32 + duration.subsec_nanoseconds(); + let mut seconds = self.second as i8 + (duration.whole_seconds() % 60) as i8; + let mut minutes = self.minute as i8 + (duration.whole_minutes() % 60) as i8; + let mut hours = self.hour as i8 + (duration.whole_hours() % 24) as i8; + let mut date_adjustment = DateAdjustment::None; + + cascade!(nanoseconds in 0..1_000_000_000 => seconds); + cascade!(seconds in 0..60 => minutes); + cascade!(minutes in 0..60 => hours); + if hours >= 24 { + hours -= 24; + date_adjustment = DateAdjustment::Next; + } else if hours < 0 { + hours += 24; + date_adjustment = DateAdjustment::Previous; + } + + ( + date_adjustment, + Self::__from_hms_nanos_unchecked( + hours as _, + minutes as _, + seconds as _, + nanoseconds as _, + ), + ) + } + + /// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning + /// whether the date is different. + pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) { + let mut nanoseconds = self.nanosecond as i32 - duration.subsec_nanoseconds(); + let mut seconds = self.second as i8 - (duration.whole_seconds() % 60) as i8; + let mut minutes = self.minute as i8 - (duration.whole_minutes() % 60) as i8; + let mut hours = self.hour as i8 - (duration.whole_hours() % 24) as i8; + let mut date_adjustment = DateAdjustment::None; + + cascade!(nanoseconds in 0..1_000_000_000 => seconds); + cascade!(seconds in 0..60 => minutes); + cascade!(minutes in 0..60 => hours); + if hours >= 24 { + hours -= 24; + date_adjustment = DateAdjustment::Next; + } else if hours < 0 { + hours += 24; + date_adjustment = DateAdjustment::Previous; + } + + ( + date_adjustment, + Self::__from_hms_nanos_unchecked( + hours as _, + minutes as _, + seconds as _, + nanoseconds as _, + ), + ) + } + + /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, + /// returning whether the date is the previous date as the first element of the tuple. + pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) { + let mut nanosecond = self.nanosecond + duration.subsec_nanos(); + let mut second = self.second + (duration.as_secs() % 60) as u8; + let mut minute = self.minute + ((duration.as_secs() / 60) % 60) as u8; + let mut hour = self.hour + ((duration.as_secs() / 3_600) % 24) as u8; + let mut is_next_day = false; + + cascade!(nanosecond in 0..1_000_000_000 => second); + cascade!(second in 0..60 => minute); + cascade!(minute in 0..60 => hour); + if hour >= 24 { + hour -= 24; + is_next_day = true; + } + + ( + is_next_day, + Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond), + ) + } + + /// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, + /// returning whether the date is the previous date as the first element of the tuple. + pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) { + let mut nanosecond = self.nanosecond as i32 - duration.subsec_nanos() as i32; + let mut second = self.second as i8 - (duration.as_secs() % 60) as i8; + let mut minute = self.minute as i8 - ((duration.as_secs() / 60) % 60) as i8; + let mut hour = self.hour as i8 - ((duration.as_secs() / 3_600) % 24) as i8; + let mut is_previous_day = false; + + cascade!(nanosecond in 0..1_000_000_000 => second); + cascade!(second in 0..60 => minute); + cascade!(minute in 0..60 => hour); + if hour < 0 { + hour += 24; + is_previous_day = true; + } + + ( + is_previous_day, + Self::__from_hms_nanos_unchecked(hour as _, minute as _, second as _, nanosecond as _), + ) + } + // endregion arithmetic helpers + + // region: replacement + /// Replace the clock hour. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_hour(7), + /// Ok(time!(07:02:03.004_005_006)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(hour in 0 => 23); + Ok(Self::__from_hms_nanos_unchecked( + hour, + self.minute, + self.second, + self.nanosecond, + )) + } + + /// Replace the minutes within the hour. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_minute(7), + /// Ok(time!(01:07:03.004_005_006)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(minute in 0 => 59); + Ok(Self::__from_hms_nanos_unchecked( + self.hour, + minute, + self.second, + self.nanosecond, + )) + } + + /// Replace the seconds within the minute. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_second(7), + /// Ok(time!(01:02:07.004_005_006)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(second in 0 => 59); + Ok(Self::__from_hms_nanos_unchecked( + self.hour, + self.minute, + second, + self.nanosecond, + )) + } + + /// Replace the milliseconds within the second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_millisecond(7), + /// Ok(time!(01:02:03.007)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_millisecond( + self, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(millisecond in 0 => 999); + Ok(Self::__from_hms_nanos_unchecked( + self.hour, + self.minute, + self.second, + millisecond as u32 * 1_000_000, + )) + } + + /// Replace the microseconds within the second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_microsecond(7_008), + /// Ok(time!(01:02:03.007_008)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_microsecond( + self, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(microsecond in 0 => 999_999); + Ok(Self::__from_hms_nanos_unchecked( + self.hour, + self.minute, + self.second, + microsecond * 1000, + )) + } + + /// Replace the nanoseconds within the second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_nanosecond(7_008_009), + /// Ok(time!(01:02:03.007_008_009)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { + ensure_value_in_range!(nanosecond in 0 => 999_999_999); + Ok(Self::__from_hms_nanos_unchecked( + self.hour, + self.minute, + self.second, + nanosecond, + )) + } + // endregion replacement +} + +// region: formatting & parsing +#[cfg(feature = "formatting")] +impl Time { + /// Format the `Time` using the provided [format description](crate::format_description). + pub fn format_into( + self, + output: &mut impl io::Write, + format: &(impl Formattable + ?Sized), + ) -> Result<usize, crate::error::Format> { + format.format_into(output, None, Some(self), None) + } + + /// Format the `Time` using the provided [format description](crate::format_description). + /// + /// ```rust + /// # use time::format_description; + /// # use time_macros::time; + /// let format = format_description::parse("[hour]:[minute]:[second]")?; + /// assert_eq!(time!(12:00).format(&format)?, "12:00:00"); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn format( + self, + format: &(impl Formattable + ?Sized), + ) -> Result<String, crate::error::Format> { + format.format(None, Some(self), None) + } +} + +#[cfg(feature = "parsing")] +impl Time { + /// Parse a `Time` from the input using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::Time; + /// # use time_macros::{time, format_description}; + /// let format = format_description!("[hour]:[minute]:[second]"); + /// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00)); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + description.parse_time(input.as_bytes()) + } +} + +impl fmt::Display for Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (value, width) = match self.nanosecond() { + nanos if nanos % 10 != 0 => (nanos, 9), + nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8), + nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7), + nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6), + nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5), + nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4), + nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3), + nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2), + nanos => (nanos / 100_000_000, 1), + }; + write!( + f, + "{}:{:02}:{:02}.{value:0width$}", + self.hour, self.minute, self.second, + ) + } +} + +impl fmt::Debug for Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} +// endregion formatting & parsing + +// region: trait impls +impl Add<Duration> for Time { + type Output = Self; + + /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::time; + /// assert_eq!(time!(12:00) + 2.hours(), time!(14:00)); + /// assert_eq!(time!(0:00:01) + (-2).seconds(), time!(23:59:59)); + /// ``` + fn add(self, duration: Duration) -> Self::Output { + self.adjusting_add(duration).1 + } +} + +impl Add<StdDuration> for Time { + type Output = Self; + + /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalStdDuration; + /// # use time_macros::time; + /// assert_eq!(time!(12:00) + 2.std_hours(), time!(14:00)); + /// assert_eq!(time!(23:59:59) + 2.std_seconds(), time!(0:00:01)); + /// ``` + fn add(self, duration: StdDuration) -> Self::Output { + self.adjusting_add_std(duration).1 + } +} + +impl_add_assign!(Time: Duration, StdDuration); + +impl Sub<Duration> for Time { + type Output = Self; + + /// Subtract the sub-day time of the [`Duration`] from the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::time; + /// assert_eq!(time!(14:00) - 2.hours(), time!(12:00)); + /// assert_eq!(time!(23:59:59) - (-2).seconds(), time!(0:00:01)); + /// ``` + fn sub(self, duration: Duration) -> Self::Output { + self.adjusting_sub(duration).1 + } +} + +impl Sub<StdDuration> for Time { + type Output = Self; + + /// Subtract the sub-day time of the [`std::time::Duration`] from the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalStdDuration; + /// # use time_macros::time; + /// assert_eq!(time!(14:00) - 2.std_hours(), time!(12:00)); + /// assert_eq!(time!(0:00:01) - 2.std_seconds(), time!(23:59:59)); + /// ``` + fn sub(self, duration: StdDuration) -> Self::Output { + self.adjusting_sub_std(duration).1 + } +} + +impl_sub_assign!(Time: Duration, StdDuration); + +impl Sub for Time { + type Output = Duration; + + /// Subtract two `Time`s, returning the [`Duration`] between. This assumes both `Time`s are in + /// the same calendar day. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::time; + /// assert_eq!(time!(0:00) - time!(0:00), 0.seconds()); + /// assert_eq!(time!(1:00) - time!(0:00), 1.hours()); + /// assert_eq!(time!(0:00) - time!(1:00), (-1).hours()); + /// assert_eq!(time!(0:00) - time!(23:00), (-23).hours()); + /// ``` + fn sub(self, rhs: Self) -> Self::Output { + let hour_diff = (self.hour as i8) - (rhs.hour as i8); + let minute_diff = (self.minute as i8) - (rhs.minute as i8); + let second_diff = (self.second as i8) - (rhs.second as i8); + let nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32); + + let seconds = hour_diff as i64 * 3_600 + minute_diff as i64 * 60 + second_diff as i64; + + let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 { + (seconds - 1, nanosecond_diff + 1_000_000_000) + } else if seconds < 0 && nanosecond_diff > 0 { + (seconds + 1, nanosecond_diff - 1_000_000_000) + } else { + (seconds, nanosecond_diff) + }; + + Duration::new_unchecked(seconds, nanoseconds) + } +} +// endregion trait impls |