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