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