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