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