1 | //! The [`Time`] struct and its associated `impl`s. |
2 | |
3 | use core::fmt; |
4 | use core::ops::{Add, Sub}; |
5 | use core::time::Duration as StdDuration; |
6 | #[cfg (feature = "formatting" )] |
7 | use std::io; |
8 | |
9 | use crate::convert::*; |
10 | #[cfg (feature = "formatting" )] |
11 | use crate::formatting::Formattable; |
12 | #[cfg (feature = "parsing" )] |
13 | use crate::parsing::Parsable; |
14 | use crate::util::DateAdjustment; |
15 | use crate::{error, Duration}; |
16 | |
17 | /// By explicitly inserting this enum where padding is expected, the compiler is able to better |
18 | /// perform niche value optimization. |
19 | #[repr (u8)] |
20 | #[derive (Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
21 | pub(crate) enum Padding { |
22 | #[allow (clippy::missing_docs_in_private_items)] |
23 | Optimize, |
24 | } |
25 | |
26 | /// The clock time within a given date. Nanosecond precision. |
27 | /// |
28 | /// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds |
29 | /// (either positive or negative). |
30 | /// |
31 | /// When comparing two `Time`s, they are assumed to be in the same calendar date. |
32 | #[derive (Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] |
33 | pub struct Time { |
34 | #[allow (clippy::missing_docs_in_private_items)] |
35 | hour: u8, |
36 | #[allow (clippy::missing_docs_in_private_items)] |
37 | minute: u8, |
38 | #[allow (clippy::missing_docs_in_private_items)] |
39 | second: u8, |
40 | #[allow (clippy::missing_docs_in_private_items)] |
41 | nanosecond: u32, |
42 | #[allow (clippy::missing_docs_in_private_items)] |
43 | padding: Padding, |
44 | } |
45 | |
46 | impl Time { |
47 | /// Create a `Time` that is exactly midnight. |
48 | /// |
49 | /// ```rust |
50 | /// # use time::Time; |
51 | /// # use time_macros::time; |
52 | /// assert_eq!(Time::MIDNIGHT, time!(0:00)); |
53 | /// ``` |
54 | pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0); |
55 | |
56 | /// The smallest value that can be represented by `Time`. |
57 | /// |
58 | /// `00:00:00.0` |
59 | pub(crate) const MIN: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0); |
60 | |
61 | /// The largest value that can be represented by `Time`. |
62 | /// |
63 | /// `23:59:59.999_999_999` |
64 | pub(crate) const MAX: Self = Self::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999); |
65 | |
66 | // region: constructors |
67 | /// Create a `Time` from its components. |
68 | #[doc (hidden)] |
69 | pub const fn __from_hms_nanos_unchecked( |
70 | hour: u8, |
71 | minute: u8, |
72 | second: u8, |
73 | nanosecond: u32, |
74 | ) -> Self { |
75 | debug_assert!(hour < Hour.per(Day)); |
76 | debug_assert!(minute < Minute.per(Hour)); |
77 | debug_assert!(second < Second.per(Minute)); |
78 | debug_assert!(nanosecond < Nanosecond.per(Second)); |
79 | |
80 | Self { |
81 | hour, |
82 | minute, |
83 | second, |
84 | nanosecond, |
85 | padding: Padding::Optimize, |
86 | } |
87 | } |
88 | |
89 | /// Attempt to create a `Time` from the hour, minute, and second. |
90 | /// |
91 | /// ```rust |
92 | /// # use time::Time; |
93 | /// assert!(Time::from_hms(1, 2, 3).is_ok()); |
94 | /// ``` |
95 | /// |
96 | /// ```rust |
97 | /// # use time::Time; |
98 | /// assert!(Time::from_hms(24, 0, 0).is_err()); // 24 isn't a valid hour. |
99 | /// assert!(Time::from_hms(0, 60, 0).is_err()); // 60 isn't a valid minute. |
100 | /// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second. |
101 | /// ``` |
102 | pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> { |
103 | ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); |
104 | ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); |
105 | ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); |
106 | Ok(Self::__from_hms_nanos_unchecked(hour, minute, second, 0)) |
107 | } |
108 | |
109 | /// Attempt to create a `Time` from the hour, minute, second, and millisecond. |
110 | /// |
111 | /// ```rust |
112 | /// # use time::Time; |
113 | /// assert!(Time::from_hms_milli(1, 2, 3, 4).is_ok()); |
114 | /// ``` |
115 | /// |
116 | /// ```rust |
117 | /// # use time::Time; |
118 | /// assert!(Time::from_hms_milli(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. |
119 | /// assert!(Time::from_hms_milli(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. |
120 | /// assert!(Time::from_hms_milli(0, 0, 60, 0).is_err()); // 60 isn't a valid second. |
121 | /// assert!(Time::from_hms_milli(0, 0, 0, 1_000).is_err()); // 1_000 isn't a valid millisecond. |
122 | /// ``` |
123 | pub const fn from_hms_milli( |
124 | hour: u8, |
125 | minute: u8, |
126 | second: u8, |
127 | millisecond: u16, |
128 | ) -> Result<Self, error::ComponentRange> { |
129 | ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); |
130 | ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); |
131 | ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); |
132 | ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1); |
133 | Ok(Self::__from_hms_nanos_unchecked( |
134 | hour, |
135 | minute, |
136 | second, |
137 | millisecond as u32 * Nanosecond.per(Millisecond), |
138 | )) |
139 | } |
140 | |
141 | /// Attempt to create a `Time` from the hour, minute, second, and microsecond. |
142 | /// |
143 | /// ```rust |
144 | /// # use time::Time; |
145 | /// assert!(Time::from_hms_micro(1, 2, 3, 4).is_ok()); |
146 | /// ``` |
147 | /// |
148 | /// ```rust |
149 | /// # use time::Time; |
150 | /// assert!(Time::from_hms_micro(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. |
151 | /// assert!(Time::from_hms_micro(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. |
152 | /// assert!(Time::from_hms_micro(0, 0, 60, 0).is_err()); // 60 isn't a valid second. |
153 | /// assert!(Time::from_hms_micro(0, 0, 0, 1_000_000).is_err()); // 1_000_000 isn't a valid microsecond. |
154 | /// ``` |
155 | pub const fn from_hms_micro( |
156 | hour: u8, |
157 | minute: u8, |
158 | second: u8, |
159 | microsecond: u32, |
160 | ) -> Result<Self, error::ComponentRange> { |
161 | ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); |
162 | ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); |
163 | ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); |
164 | ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1); |
165 | Ok(Self::__from_hms_nanos_unchecked( |
166 | hour, |
167 | minute, |
168 | second, |
169 | microsecond * Nanosecond.per(Microsecond) as u32, |
170 | )) |
171 | } |
172 | |
173 | /// Attempt to create a `Time` from the hour, minute, second, and nanosecond. |
174 | /// |
175 | /// ```rust |
176 | /// # use time::Time; |
177 | /// assert!(Time::from_hms_nano(1, 2, 3, 4).is_ok()); |
178 | /// ``` |
179 | /// |
180 | /// ```rust |
181 | /// # use time::Time; |
182 | /// assert!(Time::from_hms_nano(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. |
183 | /// assert!(Time::from_hms_nano(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. |
184 | /// assert!(Time::from_hms_nano(0, 0, 60, 0).is_err()); // 60 isn't a valid second. |
185 | /// assert!(Time::from_hms_nano(0, 0, 0, 1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond. |
186 | /// ``` |
187 | pub const fn from_hms_nano( |
188 | hour: u8, |
189 | minute: u8, |
190 | second: u8, |
191 | nanosecond: u32, |
192 | ) -> Result<Self, error::ComponentRange> { |
193 | ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); |
194 | ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); |
195 | ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); |
196 | ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1); |
197 | Ok(Self::__from_hms_nanos_unchecked( |
198 | hour, minute, second, nanosecond, |
199 | )) |
200 | } |
201 | // endregion constructors |
202 | |
203 | // region: getters |
204 | /// Get the clock hour, minute, and second. |
205 | /// |
206 | /// ```rust |
207 | /// # use time_macros::time; |
208 | /// assert_eq!(time!(0:00:00).as_hms(), (0, 0, 0)); |
209 | /// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59)); |
210 | /// ``` |
211 | pub const fn as_hms(self) -> (u8, u8, u8) { |
212 | (self.hour, self.minute, self.second) |
213 | } |
214 | |
215 | /// Get the clock hour, minute, second, and millisecond. |
216 | /// |
217 | /// ```rust |
218 | /// # use time_macros::time; |
219 | /// assert_eq!(time!(0:00:00).as_hms_milli(), (0, 0, 0, 0)); |
220 | /// assert_eq!(time!(23:59:59.999).as_hms_milli(), (23, 59, 59, 999)); |
221 | /// ``` |
222 | pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { |
223 | ( |
224 | self.hour, |
225 | self.minute, |
226 | self.second, |
227 | (self.nanosecond / Nanosecond.per(Millisecond)) as u16, |
228 | ) |
229 | } |
230 | |
231 | /// Get the clock hour, minute, second, and microsecond. |
232 | /// |
233 | /// ```rust |
234 | /// # use time_macros::time; |
235 | /// assert_eq!(time!(0:00:00).as_hms_micro(), (0, 0, 0, 0)); |
236 | /// assert_eq!( |
237 | /// time!(23:59:59.999_999).as_hms_micro(), |
238 | /// (23, 59, 59, 999_999) |
239 | /// ); |
240 | /// ``` |
241 | pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { |
242 | ( |
243 | self.hour, |
244 | self.minute, |
245 | self.second, |
246 | self.nanosecond / Nanosecond.per(Microsecond) as u32, |
247 | ) |
248 | } |
249 | |
250 | /// Get the clock hour, minute, second, and nanosecond. |
251 | /// |
252 | /// ```rust |
253 | /// # use time_macros::time; |
254 | /// assert_eq!(time!(0:00:00).as_hms_nano(), (0, 0, 0, 0)); |
255 | /// assert_eq!( |
256 | /// time!(23:59:59.999_999_999).as_hms_nano(), |
257 | /// (23, 59, 59, 999_999_999) |
258 | /// ); |
259 | /// ``` |
260 | pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { |
261 | (self.hour, self.minute, self.second, self.nanosecond) |
262 | } |
263 | |
264 | /// Get the clock hour. |
265 | /// |
266 | /// The returned value will always be in the range `0..24`. |
267 | /// |
268 | /// ```rust |
269 | /// # use time_macros::time; |
270 | /// assert_eq!(time!(0:00:00).hour(), 0); |
271 | /// assert_eq!(time!(23:59:59).hour(), 23); |
272 | /// ``` |
273 | pub const fn hour(self) -> u8 { |
274 | self.hour |
275 | } |
276 | |
277 | /// Get the minute within the hour. |
278 | /// |
279 | /// The returned value will always be in the range `0..60`. |
280 | /// |
281 | /// ```rust |
282 | /// # use time_macros::time; |
283 | /// assert_eq!(time!(0:00:00).minute(), 0); |
284 | /// assert_eq!(time!(23:59:59).minute(), 59); |
285 | /// ``` |
286 | pub const fn minute(self) -> u8 { |
287 | self.minute |
288 | } |
289 | |
290 | /// Get the second within the minute. |
291 | /// |
292 | /// The returned value will always be in the range `0..60`. |
293 | /// |
294 | /// ```rust |
295 | /// # use time_macros::time; |
296 | /// assert_eq!(time!(0:00:00).second(), 0); |
297 | /// assert_eq!(time!(23:59:59).second(), 59); |
298 | /// ``` |
299 | pub const fn second(self) -> u8 { |
300 | self.second |
301 | } |
302 | |
303 | /// Get the milliseconds within the second. |
304 | /// |
305 | /// The returned value will always be in the range `0..1_000`. |
306 | /// |
307 | /// ```rust |
308 | /// # use time_macros::time; |
309 | /// assert_eq!(time!(0:00).millisecond(), 0); |
310 | /// assert_eq!(time!(23:59:59.999).millisecond(), 999); |
311 | /// ``` |
312 | pub const fn millisecond(self) -> u16 { |
313 | (self.nanosecond / Nanosecond.per(Millisecond)) as _ |
314 | } |
315 | |
316 | /// Get the microseconds within the second. |
317 | /// |
318 | /// The returned value will always be in the range `0..1_000_000`. |
319 | /// |
320 | /// ```rust |
321 | /// # use time_macros::time; |
322 | /// assert_eq!(time!(0:00).microsecond(), 0); |
323 | /// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999); |
324 | /// ``` |
325 | pub const fn microsecond(self) -> u32 { |
326 | self.nanosecond / Nanosecond.per(Microsecond) as u32 |
327 | } |
328 | |
329 | /// Get the nanoseconds within the second. |
330 | /// |
331 | /// The returned value will always be in the range `0..1_000_000_000`. |
332 | /// |
333 | /// ```rust |
334 | /// # use time_macros::time; |
335 | /// assert_eq!(time!(0:00).nanosecond(), 0); |
336 | /// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999); |
337 | /// ``` |
338 | pub const fn nanosecond(self) -> u32 { |
339 | self.nanosecond |
340 | } |
341 | // endregion getters |
342 | |
343 | // region: arithmetic helpers |
344 | /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether |
345 | /// the date is different. |
346 | pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) { |
347 | let mut nanoseconds = self.nanosecond as i32 + duration.subsec_nanoseconds(); |
348 | let mut seconds = |
349 | self.second as i8 + (duration.whole_seconds() % Second.per(Minute) as i64) as i8; |
350 | let mut minutes = |
351 | self.minute as i8 + (duration.whole_minutes() % Minute.per(Hour) as i64) as i8; |
352 | let mut hours = self.hour as i8 + (duration.whole_hours() % Hour.per(Day) as i64) as i8; |
353 | let mut date_adjustment = DateAdjustment::None; |
354 | |
355 | cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds); |
356 | cascade!(seconds in 0..Second.per(Minute) as _ => minutes); |
357 | cascade!(minutes in 0..Minute.per(Hour) as _ => hours); |
358 | if hours >= Hour.per(Day) as _ { |
359 | hours -= Hour.per(Day) as i8; |
360 | date_adjustment = DateAdjustment::Next; |
361 | } else if hours < 0 { |
362 | hours += Hour.per(Day) as i8; |
363 | date_adjustment = DateAdjustment::Previous; |
364 | } |
365 | |
366 | ( |
367 | date_adjustment, |
368 | Self::__from_hms_nanos_unchecked( |
369 | hours as _, |
370 | minutes as _, |
371 | seconds as _, |
372 | nanoseconds as _, |
373 | ), |
374 | ) |
375 | } |
376 | |
377 | /// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning |
378 | /// whether the date is different. |
379 | pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) { |
380 | let mut nanoseconds = self.nanosecond as i32 - duration.subsec_nanoseconds(); |
381 | let mut seconds = |
382 | self.second as i8 - (duration.whole_seconds() % Second.per(Minute) as i64) as i8; |
383 | let mut minutes = |
384 | self.minute as i8 - (duration.whole_minutes() % Minute.per(Hour) as i64) as i8; |
385 | let mut hours = self.hour as i8 - (duration.whole_hours() % Hour.per(Day) as i64) as i8; |
386 | let mut date_adjustment = DateAdjustment::None; |
387 | |
388 | cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds); |
389 | cascade!(seconds in 0..Second.per(Minute) as _ => minutes); |
390 | cascade!(minutes in 0..Minute.per(Hour) as _ => hours); |
391 | if hours >= Hour.per(Day) as _ { |
392 | hours -= Hour.per(Day) as i8; |
393 | date_adjustment = DateAdjustment::Next; |
394 | } else if hours < 0 { |
395 | hours += Hour.per(Day) as i8; |
396 | date_adjustment = DateAdjustment::Previous; |
397 | } |
398 | |
399 | ( |
400 | date_adjustment, |
401 | Self::__from_hms_nanos_unchecked( |
402 | hours as _, |
403 | minutes as _, |
404 | seconds as _, |
405 | nanoseconds as _, |
406 | ), |
407 | ) |
408 | } |
409 | |
410 | /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, |
411 | /// returning whether the date is the previous date as the first element of the tuple. |
412 | pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) { |
413 | let mut nanosecond = self.nanosecond + duration.subsec_nanos(); |
414 | let mut second = self.second + (duration.as_secs() % Second.per(Minute) as u64) as u8; |
415 | let mut minute = self.minute |
416 | + ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as u8; |
417 | let mut hour = self.hour |
418 | + ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as u8; |
419 | let mut is_next_day = false; |
420 | |
421 | cascade!(nanosecond in 0..Nanosecond.per(Second) => second); |
422 | cascade!(second in 0..Second.per(Minute) => minute); |
423 | cascade!(minute in 0..Minute.per(Hour) => hour); |
424 | if hour >= Hour.per(Day) { |
425 | hour -= Hour.per(Day); |
426 | is_next_day = true; |
427 | } |
428 | |
429 | ( |
430 | is_next_day, |
431 | Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond), |
432 | ) |
433 | } |
434 | |
435 | /// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, |
436 | /// returning whether the date is the previous date as the first element of the tuple. |
437 | pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) { |
438 | let mut nanosecond = self.nanosecond as i32 - duration.subsec_nanos() as i32; |
439 | let mut second = self.second as i8 - (duration.as_secs() % Second.per(Minute) as u64) as i8; |
440 | let mut minute = self.minute as i8 |
441 | - ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as i8; |
442 | let mut hour = self.hour as i8 |
443 | - ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as i8; |
444 | let mut is_previous_day = false; |
445 | |
446 | cascade!(nanosecond in 0..Nanosecond.per(Second) as _ => second); |
447 | cascade!(second in 0..Second.per(Minute) as _ => minute); |
448 | cascade!(minute in 0..Minute.per(Hour) as _ => hour); |
449 | if hour < 0 { |
450 | hour += Hour.per(Day) as i8; |
451 | is_previous_day = true; |
452 | } |
453 | |
454 | ( |
455 | is_previous_day, |
456 | Self::__from_hms_nanos_unchecked(hour as _, minute as _, second as _, nanosecond as _), |
457 | ) |
458 | } |
459 | // endregion arithmetic helpers |
460 | |
461 | // region: replacement |
462 | /// Replace the clock hour. |
463 | /// |
464 | /// ```rust |
465 | /// # use time_macros::time; |
466 | /// assert_eq!( |
467 | /// time!(01:02:03.004_005_006).replace_hour(7), |
468 | /// Ok(time!(07:02:03.004_005_006)) |
469 | /// ); |
470 | /// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour |
471 | /// ``` |
472 | #[must_use = "This method does not mutate the original `Time`." ] |
473 | pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { |
474 | ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1); |
475 | Ok(Self::__from_hms_nanos_unchecked( |
476 | hour, |
477 | self.minute, |
478 | self.second, |
479 | self.nanosecond, |
480 | )) |
481 | } |
482 | |
483 | /// Replace the minutes within the hour. |
484 | /// |
485 | /// ```rust |
486 | /// # use time_macros::time; |
487 | /// assert_eq!( |
488 | /// time!(01:02:03.004_005_006).replace_minute(7), |
489 | /// Ok(time!(01:07:03.004_005_006)) |
490 | /// ); |
491 | /// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute |
492 | /// ``` |
493 | #[must_use = "This method does not mutate the original `Time`." ] |
494 | pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> { |
495 | ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1); |
496 | Ok(Self::__from_hms_nanos_unchecked( |
497 | self.hour, |
498 | minute, |
499 | self.second, |
500 | self.nanosecond, |
501 | )) |
502 | } |
503 | |
504 | /// Replace the seconds within the minute. |
505 | /// |
506 | /// ```rust |
507 | /// # use time_macros::time; |
508 | /// assert_eq!( |
509 | /// time!(01:02:03.004_005_006).replace_second(7), |
510 | /// Ok(time!(01:02:07.004_005_006)) |
511 | /// ); |
512 | /// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second |
513 | /// ``` |
514 | #[must_use = "This method does not mutate the original `Time`." ] |
515 | pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> { |
516 | ensure_value_in_range!(second in 0 => Second.per(Minute) - 1); |
517 | Ok(Self::__from_hms_nanos_unchecked( |
518 | self.hour, |
519 | self.minute, |
520 | second, |
521 | self.nanosecond, |
522 | )) |
523 | } |
524 | |
525 | /// Replace the milliseconds within the second. |
526 | /// |
527 | /// ```rust |
528 | /// # use time_macros::time; |
529 | /// assert_eq!( |
530 | /// time!(01:02:03.004_005_006).replace_millisecond(7), |
531 | /// Ok(time!(01:02:03.007)) |
532 | /// ); |
533 | /// assert!(time!(01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond |
534 | /// ``` |
535 | #[must_use = "This method does not mutate the original `Time`." ] |
536 | pub const fn replace_millisecond( |
537 | self, |
538 | millisecond: u16, |
539 | ) -> Result<Self, error::ComponentRange> { |
540 | ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1); |
541 | Ok(Self::__from_hms_nanos_unchecked( |
542 | self.hour, |
543 | self.minute, |
544 | self.second, |
545 | millisecond as u32 * Nanosecond.per(Millisecond), |
546 | )) |
547 | } |
548 | |
549 | /// Replace the microseconds within the second. |
550 | /// |
551 | /// ```rust |
552 | /// # use time_macros::time; |
553 | /// assert_eq!( |
554 | /// time!(01:02:03.004_005_006).replace_microsecond(7_008), |
555 | /// Ok(time!(01:02:03.007_008)) |
556 | /// ); |
557 | /// assert!(time!(01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond |
558 | /// ``` |
559 | #[must_use = "This method does not mutate the original `Time`." ] |
560 | pub const fn replace_microsecond( |
561 | self, |
562 | microsecond: u32, |
563 | ) -> Result<Self, error::ComponentRange> { |
564 | ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1); |
565 | Ok(Self::__from_hms_nanos_unchecked( |
566 | self.hour, |
567 | self.minute, |
568 | self.second, |
569 | microsecond * Nanosecond.per(Microsecond) as u32, |
570 | )) |
571 | } |
572 | |
573 | /// Replace the nanoseconds within the second. |
574 | /// |
575 | /// ```rust |
576 | /// # use time_macros::time; |
577 | /// assert_eq!( |
578 | /// time!(01:02:03.004_005_006).replace_nanosecond(7_008_009), |
579 | /// Ok(time!(01:02:03.007_008_009)) |
580 | /// ); |
581 | /// 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 |
582 | /// ``` |
583 | #[must_use = "This method does not mutate the original `Time`." ] |
584 | pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { |
585 | ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1); |
586 | Ok(Self::__from_hms_nanos_unchecked( |
587 | self.hour, |
588 | self.minute, |
589 | self.second, |
590 | nanosecond, |
591 | )) |
592 | } |
593 | // endregion replacement |
594 | } |
595 | |
596 | // region: formatting & parsing |
597 | #[cfg (feature = "formatting" )] |
598 | impl Time { |
599 | /// Format the `Time` using the provided [format description](crate::format_description). |
600 | pub fn format_into( |
601 | self, |
602 | output: &mut impl io::Write, |
603 | format: &(impl Formattable + ?Sized), |
604 | ) -> Result<usize, crate::error::Format> { |
605 | format.format_into(output, None, Some(self), None) |
606 | } |
607 | |
608 | /// Format the `Time` using the provided [format description](crate::format_description). |
609 | /// |
610 | /// ```rust |
611 | /// # use time::format_description; |
612 | /// # use time_macros::time; |
613 | /// let format = format_description::parse("[hour]:[minute]:[second]" )?; |
614 | /// assert_eq!(time!(12:00).format(&format)?, "12:00:00" ); |
615 | /// # Ok::<_, time::Error>(()) |
616 | /// ``` |
617 | pub fn format( |
618 | self, |
619 | format: &(impl Formattable + ?Sized), |
620 | ) -> Result<String, crate::error::Format> { |
621 | format.format(None, Some(self), None) |
622 | } |
623 | } |
624 | |
625 | #[cfg (feature = "parsing" )] |
626 | impl Time { |
627 | /// Parse a `Time` from the input using the provided [format |
628 | /// description](crate::format_description). |
629 | /// |
630 | /// ```rust |
631 | /// # use time::Time; |
632 | /// # use time_macros::{time, format_description}; |
633 | /// let format = format_description!("[hour]:[minute]:[second]"); |
634 | /// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00)); |
635 | /// # Ok::<_, time::Error>(()) |
636 | /// ``` |
637 | pub fn parse( |
638 | input: &str, |
639 | description: &(impl Parsable + ?Sized), |
640 | ) -> Result<Self, error::Parse> { |
641 | description.parse_time(input.as_bytes()) |
642 | } |
643 | } |
644 | |
645 | impl fmt::Display for Time { |
646 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
647 | let (value: u32, width: usize) = match self.nanosecond() { |
648 | nanos: u32 if nanos % 10 != 0 => (nanos, 9), |
649 | nanos: u32 if (nanos / 10) % 10 != 0 => (nanos / 10, 8), |
650 | nanos: u32 if (nanos / 100) % 10 != 0 => (nanos / 100, 7), |
651 | nanos: u32 if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6), |
652 | nanos: u32 if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5), |
653 | nanos: u32 if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4), |
654 | nanos: u32 if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3), |
655 | nanos: u32 if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2), |
656 | nanos: u32 => (nanos / 100_000_000, 1), |
657 | }; |
658 | write!( |
659 | f, |
660 | " {}: {:02}: {:02}. {value:0width$}" , |
661 | self.hour, self.minute, self.second, |
662 | ) |
663 | } |
664 | } |
665 | |
666 | impl fmt::Debug for Time { |
667 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
668 | fmt::Display::fmt(self, f) |
669 | } |
670 | } |
671 | // endregion formatting & parsing |
672 | |
673 | // region: trait impls |
674 | impl Add<Duration> for Time { |
675 | type Output = Self; |
676 | |
677 | /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow. |
678 | /// |
679 | /// ```rust |
680 | /// # use time::ext::NumericalDuration; |
681 | /// # use time_macros::time; |
682 | /// assert_eq!(time!(12:00) + 2.hours(), time!(14:00)); |
683 | /// assert_eq!(time!(0:00:01) + (-2).seconds(), time!(23:59:59)); |
684 | /// ``` |
685 | fn add(self, duration: Duration) -> Self::Output { |
686 | self.adjusting_add(duration).1 |
687 | } |
688 | } |
689 | |
690 | impl Add<StdDuration> for Time { |
691 | type Output = Self; |
692 | |
693 | /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow. |
694 | /// |
695 | /// ```rust |
696 | /// # use time::ext::NumericalStdDuration; |
697 | /// # use time_macros::time; |
698 | /// assert_eq!(time!(12:00) + 2.std_hours(), time!(14:00)); |
699 | /// assert_eq!(time!(23:59:59) + 2.std_seconds(), time!(0:00:01)); |
700 | /// ``` |
701 | fn add(self, duration: StdDuration) -> Self::Output { |
702 | self.adjusting_add_std(duration).1 |
703 | } |
704 | } |
705 | |
706 | impl_add_assign!(Time: Duration, StdDuration); |
707 | |
708 | impl Sub<Duration> for Time { |
709 | type Output = Self; |
710 | |
711 | /// Subtract the sub-day time of the [`Duration`] from the `Time`. Wraps on overflow. |
712 | /// |
713 | /// ```rust |
714 | /// # use time::ext::NumericalDuration; |
715 | /// # use time_macros::time; |
716 | /// assert_eq!(time!(14:00) - 2.hours(), time!(12:00)); |
717 | /// assert_eq!(time!(23:59:59) - (-2).seconds(), time!(0:00:01)); |
718 | /// ``` |
719 | fn sub(self, duration: Duration) -> Self::Output { |
720 | self.adjusting_sub(duration).1 |
721 | } |
722 | } |
723 | |
724 | impl Sub<StdDuration> for Time { |
725 | type Output = Self; |
726 | |
727 | /// Subtract the sub-day time of the [`std::time::Duration`] from the `Time`. Wraps on overflow. |
728 | /// |
729 | /// ```rust |
730 | /// # use time::ext::NumericalStdDuration; |
731 | /// # use time_macros::time; |
732 | /// assert_eq!(time!(14:00) - 2.std_hours(), time!(12:00)); |
733 | /// assert_eq!(time!(0:00:01) - 2.std_seconds(), time!(23:59:59)); |
734 | /// ``` |
735 | fn sub(self, duration: StdDuration) -> Self::Output { |
736 | self.adjusting_sub_std(duration).1 |
737 | } |
738 | } |
739 | |
740 | impl_sub_assign!(Time: Duration, StdDuration); |
741 | |
742 | impl Sub for Time { |
743 | type Output = Duration; |
744 | |
745 | /// Subtract two `Time`s, returning the [`Duration`] between. This assumes both `Time`s are in |
746 | /// the same calendar day. |
747 | /// |
748 | /// ```rust |
749 | /// # use time::ext::NumericalDuration; |
750 | /// # use time_macros::time; |
751 | /// assert_eq!(time!(0:00) - time!(0:00), 0.seconds()); |
752 | /// assert_eq!(time!(1:00) - time!(0:00), 1.hours()); |
753 | /// assert_eq!(time!(0:00) - time!(1:00), (-1).hours()); |
754 | /// assert_eq!(time!(0:00) - time!(23:00), (-23).hours()); |
755 | /// ``` |
756 | fn sub(self, rhs: Self) -> Self::Output { |
757 | let hour_diff = (self.hour as i8) - (rhs.hour as i8); |
758 | let minute_diff = (self.minute as i8) - (rhs.minute as i8); |
759 | let second_diff = (self.second as i8) - (rhs.second as i8); |
760 | let nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32); |
761 | |
762 | let seconds = hour_diff as i64 * Second.per(Hour) as i64 |
763 | + minute_diff as i64 * Second.per(Minute) as i64 |
764 | + second_diff as i64; |
765 | |
766 | let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 { |
767 | (seconds - 1, nanosecond_diff + Nanosecond.per(Second) as i32) |
768 | } else if seconds < 0 && nanosecond_diff > 0 { |
769 | (seconds + 1, nanosecond_diff - Nanosecond.per(Second) as i32) |
770 | } else { |
771 | (seconds, nanosecond_diff) |
772 | }; |
773 | |
774 | Duration::new_unchecked(seconds, nanoseconds) |
775 | } |
776 | } |
777 | // endregion trait impls |
778 | |