time/
time.rs

1//! The [`Time`] struct and its associated `impl`s.
2
3#[cfg(feature = "formatting")]
4use alloc::string::String;
5use core::fmt;
6use core::ops::{Add, Sub};
7use core::time::Duration as StdDuration;
8#[cfg(feature = "formatting")]
9use std::io;
10
11use deranged::{RangedU32, RangedU8};
12use num_conv::prelude::*;
13use powerfmt::ext::FormatterExt;
14use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
15
16use crate::convert::*;
17#[cfg(feature = "formatting")]
18use crate::formatting::Formattable;
19use crate::internal_macros::{cascade, ensure_ranged, impl_add_assign, impl_sub_assign};
20#[cfg(feature = "parsing")]
21use crate::parsing::Parsable;
22use crate::util::DateAdjustment;
23use crate::{error, Duration};
24
25/// By explicitly inserting this enum where padding is expected, the compiler is able to better
26/// perform niche value optimization.
27#[repr(u8)]
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub(crate) enum Padding {
30    #[allow(clippy::missing_docs_in_private_items)]
31    Optimize,
32}
33
34/// The type of the `hour` field of `Time`.
35type Hours = RangedU8<0, { Hour::per(Day) - 1 }>;
36/// The type of the `minute` field of `Time`.
37type Minutes = RangedU8<0, { Minute::per(Hour) - 1 }>;
38/// The type of the `second` field of `Time`.
39type Seconds = RangedU8<0, { Second::per(Minute) - 1 }>;
40/// The type of the `nanosecond` field of `Time`.
41type Nanoseconds = RangedU32<0, { Nanosecond::per(Second) - 1 }>;
42
43/// The clock time within a given date. Nanosecond precision.
44///
45/// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds
46/// (either positive or negative).
47///
48/// When comparing two `Time`s, they are assumed to be in the same calendar date.
49#[derive(Clone, Copy, Eq)]
50#[repr(C)]
51pub struct Time {
52    // The order of this struct's fields matter!
53    // Do not change them.
54
55    // Little endian version
56    #[cfg(target_endian = "little")]
57    #[allow(clippy::missing_docs_in_private_items)]
58    nanosecond: Nanoseconds,
59    #[cfg(target_endian = "little")]
60    #[allow(clippy::missing_docs_in_private_items)]
61    second: Seconds,
62    #[cfg(target_endian = "little")]
63    #[allow(clippy::missing_docs_in_private_items)]
64    minute: Minutes,
65    #[cfg(target_endian = "little")]
66    #[allow(clippy::missing_docs_in_private_items)]
67    hour: Hours,
68    #[cfg(target_endian = "little")]
69    #[allow(clippy::missing_docs_in_private_items)]
70    padding: Padding,
71
72    // Big endian version
73    #[cfg(target_endian = "big")]
74    #[allow(clippy::missing_docs_in_private_items)]
75    padding: Padding,
76    #[cfg(target_endian = "big")]
77    #[allow(clippy::missing_docs_in_private_items)]
78    hour: Hours,
79    #[cfg(target_endian = "big")]
80    #[allow(clippy::missing_docs_in_private_items)]
81    minute: Minutes,
82    #[cfg(target_endian = "big")]
83    #[allow(clippy::missing_docs_in_private_items)]
84    second: Seconds,
85    #[cfg(target_endian = "big")]
86    #[allow(clippy::missing_docs_in_private_items)]
87    nanosecond: Nanoseconds,
88}
89
90impl core::hash::Hash for Time {
91    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
92        self.as_u64().hash(state)
93    }
94}
95
96impl PartialEq for Time {
97    fn eq(&self, other: &Self) -> bool {
98        self.as_u64().eq(&other.as_u64())
99    }
100}
101
102impl PartialOrd for Time {
103    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
104        Some(self.cmp(other))
105    }
106}
107
108impl Ord for Time {
109    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
110        self.as_u64().cmp(&other.as_u64())
111    }
112}
113
114impl Time {
115    /// Provides an u64 based representation **of the correct endianness**
116    ///
117    /// This representation can be used to do comparisons equality testing or hashing.
118    const fn as_u64(self) -> u64 {
119        let nano_bytes = self.nanosecond.get().to_ne_bytes();
120
121        #[cfg(target_endian = "big")]
122        return u64::from_be_bytes([
123            self.padding as u8,
124            self.hour.get(),
125            self.minute.get(),
126            self.second.get(),
127            nano_bytes[0],
128            nano_bytes[1],
129            nano_bytes[2],
130            nano_bytes[3],
131        ]);
132
133        #[cfg(target_endian = "little")]
134        return u64::from_le_bytes([
135            nano_bytes[0],
136            nano_bytes[1],
137            nano_bytes[2],
138            nano_bytes[3],
139            self.second.get(),
140            self.minute.get(),
141            self.hour.get(),
142            self.padding as u8,
143        ]);
144    }
145
146    /// Create a `Time` that is exactly midnight.
147    ///
148    /// ```rust
149    /// # use time::Time;
150    /// # use time_macros::time;
151    /// assert_eq!(Time::MIDNIGHT, time!(0:00));
152    /// ```
153    pub const MIDNIGHT: Self = Self::MIN;
154
155    /// The smallest value that can be represented by `Time`.
156    ///
157    /// `00:00:00.0`
158    pub(crate) const MIN: Self =
159        Self::from_hms_nanos_ranged(Hours::MIN, Minutes::MIN, Seconds::MIN, Nanoseconds::MIN);
160
161    /// The largest value that can be represented by `Time`.
162    ///
163    /// `23:59:59.999_999_999`
164    pub(crate) const MAX: Self =
165        Self::from_hms_nanos_ranged(Hours::MAX, Minutes::MAX, Seconds::MAX, Nanoseconds::MAX);
166
167    // region: constructors
168    /// Create a `Time` from its components.
169    ///
170    /// # Safety
171    ///
172    /// - `hours` must be in the range `0..=23`.
173    /// - `minutes` must be in the range `0..=59`.
174    /// - `seconds` must be in the range `0..=59`.
175    /// - `nanoseconds` must be in the range `0..=999_999_999`.
176    #[doc(hidden)]
177    pub const unsafe fn __from_hms_nanos_unchecked(
178        hour: u8,
179        minute: u8,
180        second: u8,
181        nanosecond: u32,
182    ) -> Self {
183        // Safety: The caller must uphold the safety invariants.
184        unsafe {
185            Self::from_hms_nanos_ranged(
186                Hours::new_unchecked(hour),
187                Minutes::new_unchecked(minute),
188                Seconds::new_unchecked(second),
189                Nanoseconds::new_unchecked(nanosecond),
190            )
191        }
192    }
193
194    /// Attempt to create a `Time` from the hour, minute, and second.
195    ///
196    /// ```rust
197    /// # use time::Time;
198    /// assert!(Time::from_hms(1, 2, 3).is_ok());
199    /// ```
200    ///
201    /// ```rust
202    /// # use time::Time;
203    /// assert!(Time::from_hms(24, 0, 0).is_err()); // 24 isn't a valid hour.
204    /// assert!(Time::from_hms(0, 60, 0).is_err()); // 60 isn't a valid minute.
205    /// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second.
206    /// ```
207    pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> {
208        Ok(Self::from_hms_nanos_ranged(
209            ensure_ranged!(Hours: hour),
210            ensure_ranged!(Minutes: minute),
211            ensure_ranged!(Seconds: second),
212            Nanoseconds::MIN,
213        ))
214    }
215
216    /// Create a `Time` from the hour, minute, second, and nanosecond.
217    pub(crate) const fn from_hms_nanos_ranged(
218        hour: Hours,
219        minute: Minutes,
220        second: Seconds,
221        nanosecond: Nanoseconds,
222    ) -> Self {
223        Self {
224            hour,
225            minute,
226            second,
227            nanosecond,
228            padding: Padding::Optimize,
229        }
230    }
231
232    /// Attempt to create a `Time` from the hour, minute, second, and millisecond.
233    ///
234    /// ```rust
235    /// # use time::Time;
236    /// assert!(Time::from_hms_milli(1, 2, 3, 4).is_ok());
237    /// ```
238    ///
239    /// ```rust
240    /// # use time::Time;
241    /// assert!(Time::from_hms_milli(24, 0, 0, 0).is_err()); // 24 isn't a valid hour.
242    /// assert!(Time::from_hms_milli(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
243    /// assert!(Time::from_hms_milli(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
244    /// assert!(Time::from_hms_milli(0, 0, 0, 1_000).is_err()); // 1_000 isn't a valid millisecond.
245    /// ```
246    pub const fn from_hms_milli(
247        hour: u8,
248        minute: u8,
249        second: u8,
250        millisecond: u16,
251    ) -> Result<Self, error::ComponentRange> {
252        Ok(Self::from_hms_nanos_ranged(
253            ensure_ranged!(Hours: hour),
254            ensure_ranged!(Minutes: minute),
255            ensure_ranged!(Seconds: second),
256            ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)),
257        ))
258    }
259
260    /// Attempt to create a `Time` from the hour, minute, second, and microsecond.
261    ///
262    /// ```rust
263    /// # use time::Time;
264    /// assert!(Time::from_hms_micro(1, 2, 3, 4).is_ok());
265    /// ```
266    ///
267    /// ```rust
268    /// # use time::Time;
269    /// assert!(Time::from_hms_micro(24, 0, 0, 0).is_err()); // 24 isn't a valid hour.
270    /// assert!(Time::from_hms_micro(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
271    /// assert!(Time::from_hms_micro(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
272    /// assert!(Time::from_hms_micro(0, 0, 0, 1_000_000).is_err()); // 1_000_000 isn't a valid microsecond.
273    /// ```
274    pub const fn from_hms_micro(
275        hour: u8,
276        minute: u8,
277        second: u8,
278        microsecond: u32,
279    ) -> Result<Self, error::ComponentRange> {
280        Ok(Self::from_hms_nanos_ranged(
281            ensure_ranged!(Hours: hour),
282            ensure_ranged!(Minutes: minute),
283            ensure_ranged!(Seconds: second),
284            ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32),
285        ))
286    }
287
288    /// Attempt to create a `Time` from the hour, minute, second, and nanosecond.
289    ///
290    /// ```rust
291    /// # use time::Time;
292    /// assert!(Time::from_hms_nano(1, 2, 3, 4).is_ok());
293    /// ```
294    ///
295    /// ```rust
296    /// # use time::Time;
297    /// assert!(Time::from_hms_nano(24, 0, 0, 0).is_err()); // 24 isn't a valid hour.
298    /// assert!(Time::from_hms_nano(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
299    /// assert!(Time::from_hms_nano(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
300    /// assert!(Time::from_hms_nano(0, 0, 0, 1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond.
301    /// ```
302    pub const fn from_hms_nano(
303        hour: u8,
304        minute: u8,
305        second: u8,
306        nanosecond: u32,
307    ) -> Result<Self, error::ComponentRange> {
308        Ok(Self::from_hms_nanos_ranged(
309            ensure_ranged!(Hours: hour),
310            ensure_ranged!(Minutes: minute),
311            ensure_ranged!(Seconds: second),
312            ensure_ranged!(Nanoseconds: nanosecond),
313        ))
314    }
315    // endregion constructors
316
317    // region: getters
318    /// Get the clock hour, minute, and second.
319    ///
320    /// ```rust
321    /// # use time_macros::time;
322    /// assert_eq!(time!(0:00:00).as_hms(), (0, 0, 0));
323    /// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59));
324    /// ```
325    pub const fn as_hms(self) -> (u8, u8, u8) {
326        (self.hour.get(), self.minute.get(), self.second.get())
327    }
328
329    /// Get the clock hour, minute, second, and millisecond.
330    ///
331    /// ```rust
332    /// # use time_macros::time;
333    /// assert_eq!(time!(0:00:00).as_hms_milli(), (0, 0, 0, 0));
334    /// assert_eq!(time!(23:59:59.999).as_hms_milli(), (23, 59, 59, 999));
335    /// ```
336    pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
337        (
338            self.hour.get(),
339            self.minute.get(),
340            self.second.get(),
341            (self.nanosecond.get() / Nanosecond::per(Millisecond)) as u16,
342        )
343    }
344
345    /// Get the clock hour, minute, second, and microsecond.
346    ///
347    /// ```rust
348    /// # use time_macros::time;
349    /// assert_eq!(time!(0:00:00).as_hms_micro(), (0, 0, 0, 0));
350    /// assert_eq!(
351    ///     time!(23:59:59.999_999).as_hms_micro(),
352    ///     (23, 59, 59, 999_999)
353    /// );
354    /// ```
355    pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
356        (
357            self.hour.get(),
358            self.minute.get(),
359            self.second.get(),
360            self.nanosecond.get() / Nanosecond::per(Microsecond) as u32,
361        )
362    }
363
364    /// Get the clock hour, minute, second, and nanosecond.
365    ///
366    /// ```rust
367    /// # use time_macros::time;
368    /// assert_eq!(time!(0:00:00).as_hms_nano(), (0, 0, 0, 0));
369    /// assert_eq!(
370    ///     time!(23:59:59.999_999_999).as_hms_nano(),
371    ///     (23, 59, 59, 999_999_999)
372    /// );
373    /// ```
374    pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
375        (
376            self.hour.get(),
377            self.minute.get(),
378            self.second.get(),
379            self.nanosecond.get(),
380        )
381    }
382
383    /// Get the clock hour, minute, second, and nanosecond.
384    #[cfg(feature = "quickcheck")]
385    pub(crate) const fn as_hms_nano_ranged(self) -> (Hours, Minutes, Seconds, Nanoseconds) {
386        (self.hour, self.minute, self.second, self.nanosecond)
387    }
388
389    /// Get the clock hour.
390    ///
391    /// The returned value will always be in the range `0..24`.
392    ///
393    /// ```rust
394    /// # use time_macros::time;
395    /// assert_eq!(time!(0:00:00).hour(), 0);
396    /// assert_eq!(time!(23:59:59).hour(), 23);
397    /// ```
398    pub const fn hour(self) -> u8 {
399        self.hour.get()
400    }
401
402    /// Get the minute within the hour.
403    ///
404    /// The returned value will always be in the range `0..60`.
405    ///
406    /// ```rust
407    /// # use time_macros::time;
408    /// assert_eq!(time!(0:00:00).minute(), 0);
409    /// assert_eq!(time!(23:59:59).minute(), 59);
410    /// ```
411    pub const fn minute(self) -> u8 {
412        self.minute.get()
413    }
414
415    /// Get the second within the minute.
416    ///
417    /// The returned value will always be in the range `0..60`.
418    ///
419    /// ```rust
420    /// # use time_macros::time;
421    /// assert_eq!(time!(0:00:00).second(), 0);
422    /// assert_eq!(time!(23:59:59).second(), 59);
423    /// ```
424    pub const fn second(self) -> u8 {
425        self.second.get()
426    }
427
428    /// Get the milliseconds within the second.
429    ///
430    /// The returned value will always be in the range `0..1_000`.
431    ///
432    /// ```rust
433    /// # use time_macros::time;
434    /// assert_eq!(time!(0:00).millisecond(), 0);
435    /// assert_eq!(time!(23:59:59.999).millisecond(), 999);
436    /// ```
437    pub const fn millisecond(self) -> u16 {
438        (self.nanosecond.get() / Nanosecond::per(Millisecond)) as _
439    }
440
441    /// Get the microseconds within the second.
442    ///
443    /// The returned value will always be in the range `0..1_000_000`.
444    ///
445    /// ```rust
446    /// # use time_macros::time;
447    /// assert_eq!(time!(0:00).microsecond(), 0);
448    /// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999);
449    /// ```
450    pub const fn microsecond(self) -> u32 {
451        self.nanosecond.get() / Nanosecond::per(Microsecond) as u32
452    }
453
454    /// Get the nanoseconds within the second.
455    ///
456    /// The returned value will always be in the range `0..1_000_000_000`.
457    ///
458    /// ```rust
459    /// # use time_macros::time;
460    /// assert_eq!(time!(0:00).nanosecond(), 0);
461    /// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999);
462    /// ```
463    pub const fn nanosecond(self) -> u32 {
464        self.nanosecond.get()
465    }
466    // endregion getters
467
468    // region: arithmetic helpers
469    /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether
470    /// the date is different.
471    pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) {
472        let mut nanoseconds = self.nanosecond.get() as i32 + duration.subsec_nanoseconds();
473        let mut seconds =
474            self.second.get() as i8 + (duration.whole_seconds() % Second::per(Minute) as i64) as i8;
475        let mut minutes =
476            self.minute.get() as i8 + (duration.whole_minutes() % Minute::per(Hour) as i64) as i8;
477        let mut hours =
478            self.hour.get() as i8 + (duration.whole_hours() % Hour::per(Day) as i64) as i8;
479        let mut date_adjustment = DateAdjustment::None;
480
481        cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds);
482        cascade!(seconds in 0..Second::per(Minute) as _ => minutes);
483        cascade!(minutes in 0..Minute::per(Hour) as _ => hours);
484        if hours >= Hour::per(Day) as _ {
485            hours -= Hour::per(Day) as i8;
486            date_adjustment = DateAdjustment::Next;
487        } else if hours < 0 {
488            hours += Hour::per(Day) as i8;
489            date_adjustment = DateAdjustment::Previous;
490        }
491
492        (
493            date_adjustment,
494            // Safety: The cascades above ensure the values are in range.
495            unsafe {
496                Self::__from_hms_nanos_unchecked(
497                    hours as _,
498                    minutes as _,
499                    seconds as _,
500                    nanoseconds as _,
501                )
502            },
503        )
504    }
505
506    /// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning
507    /// whether the date is different.
508    pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) {
509        let mut nanoseconds = self.nanosecond.get() as i32 - duration.subsec_nanoseconds();
510        let mut seconds =
511            self.second.get() as i8 - (duration.whole_seconds() % Second::per(Minute) as i64) as i8;
512        let mut minutes =
513            self.minute.get() as i8 - (duration.whole_minutes() % Minute::per(Hour) as i64) as i8;
514        let mut hours =
515            self.hour.get() as i8 - (duration.whole_hours() % Hour::per(Day) as i64) as i8;
516        let mut date_adjustment = DateAdjustment::None;
517
518        cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds);
519        cascade!(seconds in 0..Second::per(Minute) as _ => minutes);
520        cascade!(minutes in 0..Minute::per(Hour) as _ => hours);
521        if hours >= Hour::per(Day) as _ {
522            hours -= Hour::per(Day) as i8;
523            date_adjustment = DateAdjustment::Next;
524        } else if hours < 0 {
525            hours += Hour::per(Day) as i8;
526            date_adjustment = DateAdjustment::Previous;
527        }
528
529        (
530            date_adjustment,
531            // Safety: The cascades above ensure the values are in range.
532            unsafe {
533                Self::__from_hms_nanos_unchecked(
534                    hours as _,
535                    minutes as _,
536                    seconds as _,
537                    nanoseconds as _,
538                )
539            },
540        )
541    }
542
543    /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
544    /// returning whether the date is the previous date as the first element of the tuple.
545    pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) {
546        let mut nanosecond = self.nanosecond.get() + duration.subsec_nanos();
547        let mut second =
548            self.second.get() + (duration.as_secs() % Second::per(Minute) as u64) as u8;
549        let mut minute = self.minute.get()
550            + ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as u8;
551        let mut hour = self.hour.get()
552            + ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as u8;
553        let mut is_next_day = false;
554
555        cascade!(nanosecond in 0..Nanosecond::per(Second) => second);
556        cascade!(second in 0..Second::per(Minute) => minute);
557        cascade!(minute in 0..Minute::per(Hour) => hour);
558        if hour >= Hour::per(Day) {
559            hour -= Hour::per(Day);
560            is_next_day = true;
561        }
562
563        (
564            is_next_day,
565            // Safety: The cascades above ensure the values are in range.
566            unsafe { Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) },
567        )
568    }
569
570    /// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
571    /// returning whether the date is the previous date as the first element of the tuple.
572    pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) {
573        let mut nanosecond = self.nanosecond.get() as i32 - duration.subsec_nanos() as i32;
574        let mut second =
575            self.second.get() as i8 - (duration.as_secs() % Second::per(Minute) as u64) as i8;
576        let mut minute = self.minute.get() as i8
577            - ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as i8;
578        let mut hour = self.hour.get() as i8
579            - ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as i8;
580        let mut is_previous_day = false;
581
582        cascade!(nanosecond in 0..Nanosecond::per(Second) as _ => second);
583        cascade!(second in 0..Second::per(Minute) as _ => minute);
584        cascade!(minute in 0..Minute::per(Hour) as _ => hour);
585        if hour < 0 {
586            hour += Hour::per(Day) as i8;
587            is_previous_day = true;
588        }
589
590        (
591            is_previous_day,
592            // Safety: The cascades above ensure the values are in range.
593            unsafe {
594                Self::__from_hms_nanos_unchecked(
595                    hour as _,
596                    minute as _,
597                    second as _,
598                    nanosecond as _,
599                )
600            },
601        )
602    }
603    // endregion arithmetic helpers
604
605    // region: replacement
606    /// Replace the clock hour.
607    ///
608    /// ```rust
609    /// # use time_macros::time;
610    /// assert_eq!(
611    ///     time!(01:02:03.004_005_006).replace_hour(7),
612    ///     Ok(time!(07:02:03.004_005_006))
613    /// );
614    /// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour
615    /// ```
616    #[must_use = "This method does not mutate the original `Time`."]
617    pub const fn replace_hour(mut self, hour: u8) -> Result<Self, error::ComponentRange> {
618        self.hour = ensure_ranged!(Hours: hour);
619        Ok(self)
620    }
621
622    /// Replace the minutes within the hour.
623    ///
624    /// ```rust
625    /// # use time_macros::time;
626    /// assert_eq!(
627    ///     time!(01:02:03.004_005_006).replace_minute(7),
628    ///     Ok(time!(01:07:03.004_005_006))
629    /// );
630    /// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute
631    /// ```
632    #[must_use = "This method does not mutate the original `Time`."]
633    pub const fn replace_minute(mut self, minute: u8) -> Result<Self, error::ComponentRange> {
634        self.minute = ensure_ranged!(Minutes: minute);
635        Ok(self)
636    }
637
638    /// Replace the seconds within the minute.
639    ///
640    /// ```rust
641    /// # use time_macros::time;
642    /// assert_eq!(
643    ///     time!(01:02:03.004_005_006).replace_second(7),
644    ///     Ok(time!(01:02:07.004_005_006))
645    /// );
646    /// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second
647    /// ```
648    #[must_use = "This method does not mutate the original `Time`."]
649    pub const fn replace_second(mut self, second: u8) -> Result<Self, error::ComponentRange> {
650        self.second = ensure_ranged!(Seconds: second);
651        Ok(self)
652    }
653
654    /// Replace the milliseconds within the second.
655    ///
656    /// ```rust
657    /// # use time_macros::time;
658    /// assert_eq!(
659    ///     time!(01:02:03.004_005_006).replace_millisecond(7),
660    ///     Ok(time!(01:02:03.007))
661    /// );
662    /// assert!(time!(01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
663    /// ```
664    #[must_use = "This method does not mutate the original `Time`."]
665    pub const fn replace_millisecond(
666        mut self,
667        millisecond: u16,
668    ) -> Result<Self, error::ComponentRange> {
669        self.nanosecond =
670            ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond));
671        Ok(self)
672    }
673
674    /// Replace the microseconds within the second.
675    ///
676    /// ```rust
677    /// # use time_macros::time;
678    /// assert_eq!(
679    ///     time!(01:02:03.004_005_006).replace_microsecond(7_008),
680    ///     Ok(time!(01:02:03.007_008))
681    /// );
682    /// assert!(time!(01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
683    /// ```
684    #[must_use = "This method does not mutate the original `Time`."]
685    pub const fn replace_microsecond(
686        mut self,
687        microsecond: u32,
688    ) -> Result<Self, error::ComponentRange> {
689        self.nanosecond =
690            ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32);
691        Ok(self)
692    }
693
694    /// Replace the nanoseconds within the second.
695    ///
696    /// ```rust
697    /// # use time_macros::time;
698    /// assert_eq!(
699    ///     time!(01:02:03.004_005_006).replace_nanosecond(7_008_009),
700    ///     Ok(time!(01:02:03.007_008_009))
701    /// );
702    /// 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
703    /// ```
704    #[must_use = "This method does not mutate the original `Time`."]
705    pub const fn replace_nanosecond(
706        mut self,
707        nanosecond: u32,
708    ) -> Result<Self, error::ComponentRange> {
709        self.nanosecond = ensure_ranged!(Nanoseconds: nanosecond);
710        Ok(self)
711    }
712    // endregion replacement
713}
714
715// region: formatting & parsing
716#[cfg(feature = "formatting")]
717impl Time {
718    /// Format the `Time` using the provided [format description](crate::format_description).
719    pub fn format_into(
720        self,
721        output: &mut impl io::Write,
722        format: &(impl Formattable + ?Sized),
723    ) -> Result<usize, error::Format> {
724        format.format_into(output, None, Some(self), None)
725    }
726
727    /// Format the `Time` using the provided [format description](crate::format_description).
728    ///
729    /// ```rust
730    /// # use time::format_description;
731    /// # use time_macros::time;
732    /// let format = format_description::parse("[hour]:[minute]:[second]")?;
733    /// assert_eq!(time!(12:00).format(&format)?, "12:00:00");
734    /// # Ok::<_, time::Error>(())
735    /// ```
736    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
737        format.format(None, Some(self), None)
738    }
739}
740
741#[cfg(feature = "parsing")]
742impl Time {
743    /// Parse a `Time` from the input using the provided [format
744    /// description](crate::format_description).
745    ///
746    /// ```rust
747    /// # use time::Time;
748    /// # use time_macros::{time, format_description};
749    /// let format = format_description!("[hour]:[minute]:[second]");
750    /// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00));
751    /// # Ok::<_, time::Error>(())
752    /// ```
753    pub fn parse(
754        input: &str,
755        description: &(impl Parsable + ?Sized),
756    ) -> Result<Self, error::Parse> {
757        description.parse_time(input.as_bytes())
758    }
759}
760
761mod private {
762    #[non_exhaustive]
763    #[derive(Debug, Clone, Copy)]
764    pub struct TimeMetadata {
765        /// How many characters wide the formatted subsecond is.
766        pub(super) subsecond_width: u8,
767        /// The value to use when formatting the subsecond. Leading zeroes will be added as
768        /// necessary.
769        pub(super) subsecond_value: u32,
770    }
771}
772use private::TimeMetadata;
773
774impl SmartDisplay for Time {
775    type Metadata = TimeMetadata;
776
777    fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
778        let (subsecond_value, subsecond_width) = match self.nanosecond() {
779            nanos if nanos % 10 != 0 => (nanos, 9),
780            nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
781            nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
782            nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
783            nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
784            nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
785            nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
786            nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
787            nanos => (nanos / 100_000_000, 1),
788        };
789
790        let formatted_width = smart_display::padded_width_of!(
791            self.hour.get(),
792            ":",
793            self.minute.get() => width(2) fill('0'),
794            ":",
795            self.second.get() => width(2) fill('0'),
796            ".",
797        ) + subsecond_width;
798
799        Metadata::new(
800            formatted_width,
801            self,
802            TimeMetadata {
803                subsecond_width: subsecond_width.truncate(),
804                subsecond_value,
805            },
806        )
807    }
808
809    fn fmt_with_metadata(
810        &self,
811        f: &mut fmt::Formatter<'_>,
812        metadata: Metadata<Self>,
813    ) -> fmt::Result {
814        let subsecond_width = metadata.subsecond_width.extend();
815        let subsecond_value = metadata.subsecond_value;
816
817        f.pad_with_width(
818            metadata.unpadded_width(),
819            format_args!(
820                "{}:{:02}:{:02}.{subsecond_value:0subsecond_width$}",
821                self.hour, self.minute, self.second
822            ),
823        )
824    }
825}
826
827impl fmt::Display for Time {
828    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829        SmartDisplay::fmt(self, f)
830    }
831}
832
833impl fmt::Debug for Time {
834    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835        fmt::Display::fmt(self, f)
836    }
837}
838// endregion formatting & parsing
839
840// region: trait impls
841impl Add<Duration> for Time {
842    type Output = Self;
843
844    /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow.
845    ///
846    /// ```rust
847    /// # use time::ext::NumericalDuration;
848    /// # use time_macros::time;
849    /// assert_eq!(time!(12:00) + 2.hours(), time!(14:00));
850    /// assert_eq!(time!(0:00:01) + (-2).seconds(), time!(23:59:59));
851    /// ```
852    fn add(self, duration: Duration) -> Self::Output {
853        self.adjusting_add(duration).1
854    }
855}
856
857impl Add<StdDuration> for Time {
858    type Output = Self;
859
860    /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow.
861    ///
862    /// ```rust
863    /// # use time::ext::NumericalStdDuration;
864    /// # use time_macros::time;
865    /// assert_eq!(time!(12:00) + 2.std_hours(), time!(14:00));
866    /// assert_eq!(time!(23:59:59) + 2.std_seconds(), time!(0:00:01));
867    /// ```
868    fn add(self, duration: StdDuration) -> Self::Output {
869        self.adjusting_add_std(duration).1
870    }
871}
872
873impl_add_assign!(Time: Duration, StdDuration);
874
875impl Sub<Duration> for Time {
876    type Output = Self;
877
878    /// Subtract the sub-day time of the [`Duration`] from the `Time`. Wraps on overflow.
879    ///
880    /// ```rust
881    /// # use time::ext::NumericalDuration;
882    /// # use time_macros::time;
883    /// assert_eq!(time!(14:00) - 2.hours(), time!(12:00));
884    /// assert_eq!(time!(23:59:59) - (-2).seconds(), time!(0:00:01));
885    /// ```
886    fn sub(self, duration: Duration) -> Self::Output {
887        self.adjusting_sub(duration).1
888    }
889}
890
891impl Sub<StdDuration> for Time {
892    type Output = Self;
893
894    /// Subtract the sub-day time of the [`std::time::Duration`] from the `Time`. Wraps on overflow.
895    ///
896    /// ```rust
897    /// # use time::ext::NumericalStdDuration;
898    /// # use time_macros::time;
899    /// assert_eq!(time!(14:00) - 2.std_hours(), time!(12:00));
900    /// assert_eq!(time!(0:00:01) - 2.std_seconds(), time!(23:59:59));
901    /// ```
902    fn sub(self, duration: StdDuration) -> Self::Output {
903        self.adjusting_sub_std(duration).1
904    }
905}
906
907impl_sub_assign!(Time: Duration, StdDuration);
908
909impl Sub for Time {
910    type Output = Duration;
911
912    /// Subtract two `Time`s, returning the [`Duration`] between. This assumes both `Time`s are in
913    /// the same calendar day.
914    ///
915    /// ```rust
916    /// # use time::ext::NumericalDuration;
917    /// # use time_macros::time;
918    /// assert_eq!(time!(0:00) - time!(0:00), 0.seconds());
919    /// assert_eq!(time!(1:00) - time!(0:00), 1.hours());
920    /// assert_eq!(time!(0:00) - time!(1:00), (-1).hours());
921    /// assert_eq!(time!(0:00) - time!(23:00), (-23).hours());
922    /// ```
923    fn sub(self, rhs: Self) -> Self::Output {
924        let hour_diff = self.hour.get().cast_signed() - rhs.hour.get().cast_signed();
925        let minute_diff = self.minute.get().cast_signed() - rhs.minute.get().cast_signed();
926        let second_diff = self.second.get().cast_signed() - rhs.second.get().cast_signed();
927        let nanosecond_diff =
928            self.nanosecond.get().cast_signed() - rhs.nanosecond.get().cast_signed();
929
930        let seconds = hour_diff.extend::<i64>() * Second::per(Hour).cast_signed().extend::<i64>()
931            + minute_diff.extend::<i64>() * Second::per(Minute).cast_signed().extend::<i64>()
932            + second_diff.extend::<i64>();
933
934        let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 {
935            (
936                seconds - 1,
937                nanosecond_diff + Nanosecond::per(Second).cast_signed(),
938            )
939        } else if seconds < 0 && nanosecond_diff > 0 {
940            (
941                seconds + 1,
942                nanosecond_diff - Nanosecond::per(Second).cast_signed(),
943            )
944        } else {
945            (seconds, nanosecond_diff)
946        };
947
948        // Safety: `nanoseconds` is in range due to the overflow handling.
949        unsafe { Duration::new_unchecked(seconds, nanoseconds) }
950    }
951}
952// endregion trait impls