//! The [`Time`] struct and its associated `impl`s. #[cfg(feature = "formatting")] use alloc::string::String; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::time::Duration as StdDuration; use core::{fmt, hint}; #[cfg(feature = "formatting")] use std::io; use deranged::{RangedU8, RangedU32}; use num_conv::prelude::*; use powerfmt::ext::FormatterExt; use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; use crate::convert::*; #[cfg(feature = "formatting")] use crate::formatting::Formattable; use crate::internal_macros::{cascade, ensure_ranged}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::DateAdjustment; use crate::{Duration, error}; /// 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 type of the `hour` field of `Time`. type Hours = RangedU8<0, { Hour::per_t::(Day) - 1 }>; /// The type of the `minute` field of `Time`. type Minutes = RangedU8<0, { Minute::per_t::(Hour) - 1 }>; /// The type of the `second` field of `Time`. type Seconds = RangedU8<0, { Second::per_t::(Minute) - 1 }>; /// The type of the `nanosecond` field of `Time`. type Nanoseconds = RangedU32<0, { Nanosecond::per_t::(Second) - 1 }>; /// The clock time within a given date. Nanosecond precision. /// /// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds /// (either positive or negative). /// /// When comparing two `Time`s, they are assumed to be in the same calendar date. #[derive(Clone, Copy, Eq)] #[cfg_attr(not(docsrs), repr(C))] pub struct Time { // The order of this struct's fields matter! Do not reorder them. // Little endian version #[cfg(target_endian = "little")] nanosecond: Nanoseconds, #[cfg(target_endian = "little")] second: Seconds, #[cfg(target_endian = "little")] minute: Minutes, #[cfg(target_endian = "little")] hour: Hours, #[cfg(target_endian = "little")] padding: Padding, // Big endian version #[cfg(target_endian = "big")] padding: Padding, #[cfg(target_endian = "big")] hour: Hours, #[cfg(target_endian = "big")] minute: Minutes, #[cfg(target_endian = "big")] second: Seconds, #[cfg(target_endian = "big")] nanosecond: Nanoseconds, } impl Hash for Time { #[inline] fn hash(&self, state: &mut H) where H: Hasher, { self.as_u64().hash(state) } } impl PartialEq for Time { #[inline] fn eq(&self, other: &Self) -> bool { self.as_u64().eq(&other.as_u64()) } } impl PartialOrd for Time { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Time { #[inline] fn cmp(&self, other: &Self) -> Ordering { self.as_u64().cmp(&other.as_u64()) } } impl Time { /// Provide a representation of `Time` as a `u64`. This value can be used for equality, hashing, /// and ordering. #[inline] pub(crate) const fn as_u64(self) -> u64 { // Safety: `self` is presumed valid because it exists, and any value of `u64` is valid. Size // and alignment are enforced by the compiler. There is no implicit padding in either `Time` // or `u64`. unsafe { core::mem::transmute(self) } } /// A `Time` that is exactly midnight. This is the smallest possible value for a `Time`. /// /// ```rust /// # use time::Time; /// # use time_macros::time; /// assert_eq!(Time::MIDNIGHT, time!(0:00)); /// ``` #[doc(alias = "MIN")] pub const MIDNIGHT: Self = Self::from_hms_nanos_ranged(Hours::MIN, Minutes::MIN, Seconds::MIN, Nanoseconds::MIN); /// A `Time` that is one nanosecond before midnight. This is the largest possible value for a /// `Time`. /// /// ```rust /// # use time::Time; /// # use time_macros::time; /// assert_eq!(Time::MAX, time!(23:59:59.999_999_999)); /// ``` pub const MAX: Self = Self::from_hms_nanos_ranged(Hours::MAX, Minutes::MAX, Seconds::MAX, Nanoseconds::MAX); /// Create a `Time` from its components. /// /// # Safety /// /// - `hours` must be in the range `0..=23`. /// - `minutes` must be in the range `0..=59`. /// - `seconds` must be in the range `0..=59`. /// - `nanoseconds` must be in the range `0..=999_999_999`. #[doc(hidden)] #[inline] #[track_caller] pub const unsafe fn __from_hms_nanos_unchecked( hour: u8, minute: u8, second: u8, nanosecond: u32, ) -> Self { // Safety: The caller must uphold the safety invariants. unsafe { Self::from_hms_nanos_ranged( Hours::new_unchecked(hour), Minutes::new_unchecked(minute), Seconds::new_unchecked(second), Nanoseconds::new_unchecked(nanosecond), ) } } /// 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. /// ``` #[inline] pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result { Ok(Self::from_hms_nanos_ranged( ensure_ranged!(Hours: hour), ensure_ranged!(Minutes: minute), ensure_ranged!(Seconds: second), Nanoseconds::MIN, )) } /// Create a `Time` from the hour, minute, second, and nanosecond. #[inline] pub(crate) const fn from_hms_nanos_ranged( hour: Hours, minute: Minutes, second: Seconds, nanosecond: Nanoseconds, ) -> Self { Self { hour, minute, second, nanosecond, padding: Padding::Optimize, } } /// Attempt to create a `Time` from the hour, minute, second, and millisecond. /// /// ```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. /// ``` #[inline] pub const fn from_hms_milli( hour: u8, minute: u8, second: u8, millisecond: u16, ) -> Result { Ok(Self::from_hms_nanos_ranged( ensure_ranged!(Hours: hour), ensure_ranged!(Minutes: minute), ensure_ranged!(Seconds: second), ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per_t::(Millisecond)), )) } /// 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. /// ``` #[inline] pub const fn from_hms_micro( hour: u8, minute: u8, second: u8, microsecond: u32, ) -> Result { Ok(Self::from_hms_nanos_ranged( ensure_ranged!(Hours: hour), ensure_ranged!(Minutes: minute), ensure_ranged!(Seconds: second), ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per_t::(Microsecond)), )) } /// 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. /// ``` #[inline] pub const fn from_hms_nano( hour: u8, minute: u8, second: u8, nanosecond: u32, ) -> Result { Ok(Self::from_hms_nanos_ranged( ensure_ranged!(Hours: hour), ensure_ranged!(Minutes: minute), ensure_ranged!(Seconds: second), ensure_ranged!(Nanoseconds: nanosecond), )) } /// 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)); /// ``` #[inline] pub const fn as_hms(self) -> (u8, u8, u8) { (self.hour.get(), self.minute.get(), self.second.get()) } /// 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)); /// ``` #[inline] pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { ( self.hour.get(), self.minute.get(), self.second.get(), (self.nanosecond.get() / Nanosecond::per_t::(Millisecond)) 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) /// ); /// ``` #[inline] pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { ( self.hour.get(), self.minute.get(), self.second.get(), self.nanosecond.get() / Nanosecond::per_t::(Microsecond), ) } /// 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) /// ); /// ``` #[inline] pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { ( self.hour.get(), self.minute.get(), self.second.get(), self.nanosecond.get(), ) } /// Get the clock hour, minute, second, and nanosecond. #[cfg(feature = "quickcheck")] #[inline] pub(crate) const fn as_hms_nano_ranged(self) -> (Hours, Minutes, Seconds, Nanoseconds) { (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); /// ``` #[inline] pub const fn hour(self) -> u8 { self.hour.get() } /// 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); /// ``` #[inline] pub const fn minute(self) -> u8 { self.minute.get() } /// 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); /// ``` #[inline] pub const fn second(self) -> u8 { self.second.get() } /// 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); /// ``` #[inline] pub const fn millisecond(self) -> u16 { (self.nanosecond.get() / Nanosecond::per_t::(Millisecond)) as u16 } /// 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); /// ``` #[inline] pub const fn microsecond(self) -> u32 { self.nanosecond.get() / Nanosecond::per_t::(Microsecond) } /// 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); /// ``` #[inline] pub const fn nanosecond(self) -> u32 { self.nanosecond.get() } /// Determine the [`Duration`] that, if added to `self`, would result in the parameter. /// /// ```rust /// # use time::Time; /// # use time::ext::NumericalDuration; /// # use time_macros::time; /// assert_eq!(time!(18:00).duration_until(Time::MIDNIGHT), 6.hours()); /// assert_eq!(time!(23:00).duration_until(time!(1:00)), 2.hours()); /// ``` #[inline] pub const fn duration_until(self, other: Self) -> Duration { let mut nanoseconds = other.nanosecond.get().cast_signed() - self.nanosecond.get().cast_signed(); let seconds = other.second.get().cast_signed() - self.second.get().cast_signed(); let minutes = other.minute.get().cast_signed() - self.minute.get().cast_signed(); let hours = other.hour.get().cast_signed() - self.hour.get().cast_signed(); // Safety: For all four variables, the bounds are obviously true given the previous bounds // and nature of subtraction. unsafe { hint::assert_unchecked( nanoseconds >= Nanoseconds::MIN.get().cast_signed() - Nanoseconds::MAX.get().cast_signed(), ); hint::assert_unchecked( nanoseconds <= Nanoseconds::MAX.get().cast_signed() - Nanoseconds::MIN.get().cast_signed(), ); hint::assert_unchecked( seconds >= Seconds::MIN.get().cast_signed() - Seconds::MAX.get().cast_signed(), ); hint::assert_unchecked( seconds <= Seconds::MAX.get().cast_signed() - Seconds::MIN.get().cast_signed(), ); hint::assert_unchecked( minutes >= Minutes::MIN.get().cast_signed() - Minutes::MAX.get().cast_signed(), ); hint::assert_unchecked( minutes <= Minutes::MAX.get().cast_signed() - Minutes::MIN.get().cast_signed(), ); hint::assert_unchecked( hours >= Hours::MIN.get().cast_signed() - Hours::MAX.get().cast_signed(), ); hint::assert_unchecked( hours <= Hours::MAX.get().cast_signed() - Hours::MIN.get().cast_signed(), ); } let mut total_seconds = hours as i32 * Second::per_t::(Hour) + minutes as i32 * Second::per_t::(Minute) + seconds as i32; cascade!(nanoseconds in 0..Nanosecond::per_t(Second) => total_seconds); if total_seconds < 0 { total_seconds += Second::per_t::(Day); } // Safety: The range of `nanoseconds` is guaranteed by the cascades above. unsafe { Duration::new_unchecked(total_seconds as i64, nanoseconds) } } /// Determine the [`Duration`] that, if added to the parameter, would result in `self`. /// /// ```rust /// # use time::Time; /// # use time::ext::NumericalDuration; /// # use time_macros::time; /// assert_eq!(Time::MIDNIGHT.duration_since(time!(18:00)), 6.hours()); /// assert_eq!(time!(1:00).duration_since(time!(23:00)), 2.hours()); /// ``` #[inline] pub const fn duration_since(self, other: Self) -> Duration { other.duration_until(self) } /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether /// the date is different. #[inline] pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) { let mut nanoseconds = self.nanosecond.get().cast_signed() + duration.subsec_nanoseconds(); let mut seconds = self.second.get().cast_signed() + (duration.whole_seconds() % Second::per_t::(Minute)) as i8; let mut minutes = self.minute.get().cast_signed() + (duration.whole_minutes() % Minute::per_t::(Hour)) as i8; let mut hours = self.hour.get().cast_signed() + (duration.whole_hours() % Hour::per_t::(Day)) as i8; let mut date_adjustment = DateAdjustment::None; cascade!(nanoseconds in 0..Nanosecond::per_t(Second) => seconds); cascade!(seconds in 0..Second::per_t(Minute) => minutes); cascade!(minutes in 0..Minute::per_t(Hour) => hours); if hours >= Hour::per_t(Day) { hours -= Hour::per_t::(Day); date_adjustment = DateAdjustment::Next; } else if hours < 0 { hours += Hour::per_t::(Day); date_adjustment = DateAdjustment::Previous; } ( date_adjustment, // Safety: The cascades above ensure the values are in range. unsafe { Self::__from_hms_nanos_unchecked( hours.cast_unsigned(), minutes.cast_unsigned(), seconds.cast_unsigned(), nanoseconds.cast_unsigned(), ) }, ) } /// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning /// whether the date is different. #[inline] pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) { let mut nanoseconds = self.nanosecond.get().cast_signed() - duration.subsec_nanoseconds(); let mut seconds = self.second.get().cast_signed() - (duration.whole_seconds() % Second::per_t::(Minute)) as i8; let mut minutes = self.minute.get().cast_signed() - (duration.whole_minutes() % Minute::per_t::(Hour)) as i8; let mut hours = self.hour.get().cast_signed() - (duration.whole_hours() % Hour::per_t::(Day)) as i8; let mut date_adjustment = DateAdjustment::None; cascade!(nanoseconds in 0..Nanosecond::per_t(Second) => seconds); cascade!(seconds in 0..Second::per_t(Minute) => minutes); cascade!(minutes in 0..Minute::per_t(Hour) => hours); if hours >= Hour::per_t(Day) { hours -= Hour::per_t::(Day); date_adjustment = DateAdjustment::Next; } else if hours < 0 { hours += Hour::per_t::(Day); date_adjustment = DateAdjustment::Previous; } ( date_adjustment, // Safety: The cascades above ensure the values are in range. unsafe { Self::__from_hms_nanos_unchecked( hours.cast_unsigned(), minutes.cast_unsigned(), seconds.cast_unsigned(), nanoseconds.cast_unsigned(), ) }, ) } /// 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. #[inline] pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) { let mut nanosecond = self.nanosecond.get() + duration.subsec_nanos(); let mut second = self.second.get() + (duration.as_secs() % Second::per_t::(Minute)) as u8; let mut minute = self.minute.get() + ((duration.as_secs() / Second::per_t::(Minute)) % Minute::per_t::(Hour)) as u8; let mut hour = self.hour.get() + ((duration.as_secs() / Second::per_t::(Hour)) % Hour::per_t::(Day)) as u8; let mut is_next_day = false; cascade!(nanosecond in 0..Nanosecond::per_t(Second) => second); cascade!(second in 0..Second::per_t(Minute) => minute); cascade!(minute in 0..Minute::per_t(Hour) => hour); if hour >= Hour::per_t::(Day) { hour -= Hour::per_t::(Day); is_next_day = true; } ( is_next_day, // Safety: The cascades above ensure the values are in range. unsafe { Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) }, ) } /// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, /// returning whether the date is the previous date as the first element of the tuple. #[inline] pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) { let mut nanosecond = self.nanosecond.get().cast_signed() - duration.subsec_nanos().cast_signed(); let mut second = self.second.get().cast_signed() - (duration.as_secs() % Second::per_t::(Minute)) as i8; let mut minute = self.minute.get().cast_signed() - ((duration.as_secs() / Second::per_t::(Minute)) % Minute::per_t::(Hour)) as i8; let mut hour = self.hour.get().cast_signed() - ((duration.as_secs() / Second::per_t::(Hour)) % Hour::per_t::(Day)) as i8; let mut is_previous_day = false; cascade!(nanosecond in 0..Nanosecond::per_t(Second) => second); cascade!(second in 0..Second::per_t(Minute) => minute); cascade!(minute in 0..Minute::per_t(Hour) => hour); if hour < 0 { hour += Hour::per_t::(Day); is_previous_day = true; } ( is_previous_day, // Safety: The cascades above ensure the values are in range. unsafe { Self::__from_hms_nanos_unchecked( hour.cast_unsigned(), minute.cast_unsigned(), second.cast_unsigned(), nanosecond.cast_unsigned(), ) }, ) } /// 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`."] #[inline] pub const fn replace_hour(mut self, hour: u8) -> Result { self.hour = ensure_ranged!(Hours: hour); Ok(self) } /// Truncate the time to the hour, setting the minute, second, and subsecond components to zero. /// /// ```rust /// # use time_macros::time; /// assert_eq!(time!(01:02:03.004_005_006).truncate_to_hour(), time!(01:00)); /// ``` #[must_use = "This method does not mutate the original `Time`."] #[inline] pub const fn truncate_to_hour(mut self) -> Self { self.minute = Minutes::MIN; self.second = Seconds::MIN; self.nanosecond = Nanoseconds::MIN; self } /// 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`."] #[inline] pub const fn replace_minute(mut self, minute: u8) -> Result { self.minute = ensure_ranged!(Minutes: minute); Ok(self) } /// Truncate the time to the minute, setting the second and subsecond components to zero. /// /// ```rust /// # use time_macros::time; /// assert_eq!( /// time!(01:02:03.004_005_006).truncate_to_minute(), /// time!(01:02) /// ); /// ``` #[must_use = "This method does not mutate the original `Time`."] #[inline] pub const fn truncate_to_minute(mut self) -> Self { self.second = Seconds::MIN; self.nanosecond = Nanoseconds::MIN; self } /// 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`."] #[inline] pub const fn replace_second(mut self, second: u8) -> Result { self.second = ensure_ranged!(Seconds: second); Ok(self) } /// Truncate the time to the second, setting the subsecond component to zero. /// /// ```rust /// # use time_macros::time; /// assert_eq!( /// time!(01:02:03.004_005_006).truncate_to_second(), /// time!(01:02:03) /// ); /// ``` #[must_use = "This method does not mutate the original `Time`."] #[inline] pub const fn truncate_to_second(mut self) -> Self { self.nanosecond = Nanoseconds::MIN; self } /// 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`."] #[inline] pub const fn replace_millisecond( mut self, millisecond: u16, ) -> Result { self.nanosecond = ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per_t::(Millisecond)); Ok(self) } /// Truncate the time to the millisecond, setting the microsecond and nanosecond components to /// zero. /// /// ```rust /// # use time_macros::time; /// assert_eq!( /// time!(01:02:03.004_005_006).truncate_to_millisecond(), /// time!(01:02:03.004) /// ); /// ``` #[must_use = "This method does not mutate the original `Time`."] #[inline] pub const fn truncate_to_millisecond(mut self) -> Self { // Safety: Truncating to the millisecond will always produce a valid nanosecond. self.nanosecond = unsafe { Nanoseconds::new_unchecked(self.nanosecond.get() - (self.nanosecond.get() % 1_000_000)) }; self } /// 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`."] #[inline] pub const fn replace_microsecond( mut self, microsecond: u32, ) -> Result { self.nanosecond = ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per_t::(Microsecond)); Ok(self) } /// Truncate the time to the microsecond, setting the nanosecond component to zero. /// /// ```rust /// # use time_macros::time; /// assert_eq!( /// time!(01:02:03.004_005_006).truncate_to_microsecond(), /// time!(01:02:03.004_005) /// ); /// ``` #[must_use = "This method does not mutate the original `Time`."] #[inline] pub const fn truncate_to_microsecond(mut self) -> Self { // Safety: Truncating to the microsecond will always produce a valid nanosecond. self.nanosecond = unsafe { Nanoseconds::new_unchecked(self.nanosecond.get() - (self.nanosecond.get() % 1_000)) }; self } /// 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`."] #[inline] pub const fn replace_nanosecond( mut self, nanosecond: u32, ) -> Result { self.nanosecond = ensure_ranged!(Nanoseconds: nanosecond); Ok(self) } } #[cfg(feature = "formatting")] impl Time { /// Format the `Time` using the provided [format description](crate::format_description). #[inline] pub fn format_into( self, output: &mut (impl io::Write + ?Sized), format: &(impl Formattable + ?Sized), ) -> Result { format.format_into(output, &self, &mut Default::default()) } /// 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>(()) /// ``` #[inline] pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result { format.format(&self, &mut Default::default()) } } #[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>(()) /// ``` #[inline] pub fn parse( input: &str, description: &(impl Parsable + ?Sized), ) -> Result { description.parse_time(input.as_bytes()) } } mod private { /// Metadata for `Time`. #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct TimeMetadata { /// How many characters wide the formatted subsecond is. pub(super) subsecond_width: u8, /// The value to use when formatting the subsecond. Leading zeroes will be added as /// necessary. pub(super) subsecond_value: u32, } } use private::TimeMetadata; impl SmartDisplay for Time { type Metadata = TimeMetadata; #[inline] fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { let (subsecond_value, subsecond_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), }; let formatted_width = smart_display::padded_width_of!( self.hour.get(), ":", self.minute.get() => width(2) fill('0'), ":", self.second.get() => width(2) fill('0'), ".", ) + subsecond_width; Metadata::new( formatted_width, self, TimeMetadata { subsecond_width: subsecond_width.truncate(), subsecond_value, }, ) } #[inline] fn fmt_with_metadata( &self, f: &mut fmt::Formatter<'_>, metadata: Metadata, ) -> fmt::Result { let subsecond_width = metadata.subsecond_width.extend(); let subsecond_value = metadata.subsecond_value; f.pad_with_width( metadata.unpadded_width(), format_args!( "{}:{:02}:{:02}.{subsecond_value:0subsecond_width$}", self.hour, self.minute, self.second ), ) } } impl fmt::Display for Time { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { SmartDisplay::fmt(self, f) } } impl fmt::Debug for Time { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl Add 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)); /// ``` #[inline] fn add(self, duration: Duration) -> Self::Output { self.adjusting_add(duration).1 } } impl AddAssign for Time { #[inline] fn add_assign(&mut self, rhs: Duration) { *self = *self + rhs; } } impl Add 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)); /// ``` #[inline] fn add(self, duration: StdDuration) -> Self::Output { self.adjusting_add_std(duration).1 } } impl AddAssign for Time { #[inline] fn add_assign(&mut self, rhs: StdDuration) { *self = *self + rhs; } } impl Sub 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)); /// ``` #[inline] fn sub(self, duration: Duration) -> Self::Output { self.adjusting_sub(duration).1 } } impl SubAssign for Time { #[inline] fn sub_assign(&mut self, rhs: Duration) { *self = *self - rhs; } } impl Sub 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)); /// ``` #[inline] fn sub(self, duration: StdDuration) -> Self::Output { self.adjusting_sub_std(duration).1 } } impl SubAssign for Time { #[inline] fn sub_assign(&mut self, rhs: StdDuration) { *self = *self - rhs; } } 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()); /// ``` #[inline] fn sub(self, rhs: Self) -> Self::Output { let hour_diff = self.hour.get().cast_signed() - rhs.hour.get().cast_signed(); let minute_diff = self.minute.get().cast_signed() - rhs.minute.get().cast_signed(); let second_diff = self.second.get().cast_signed() - rhs.second.get().cast_signed(); let nanosecond_diff = self.nanosecond.get().cast_signed() - rhs.nanosecond.get().cast_signed(); let seconds = hour_diff.extend::() * Second::per_t::(Hour) + minute_diff.extend::() * Second::per_t::(Minute) + second_diff.extend::(); let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 { ( seconds - 1, nanosecond_diff + Nanosecond::per_t::(Second), ) } else if seconds < 0 && nanosecond_diff > 0 { ( seconds + 1, nanosecond_diff - Nanosecond::per_t::(Second), ) } else { (seconds, nanosecond_diff) }; // Safety: `nanoseconds` is in range due to the overflow handling. unsafe { Duration::new_unchecked(seconds.extend(), nanoseconds) } } }