rs_std_ext/time.rs
1//! [`std::time`] related extensions.
2
3use std::time::Duration;
4
5/// Extension methods for constructing [`std::time::Duration`] with numbers.
6///
7/// ## Note
8///
9/// This trait is not implemented for the `Duration`,
10/// for extension methods operating on a `Duration`,
11/// see [`crate::time::DurationExt`] for more information.
12pub trait DurationNumExt: DurationNumExtFallible {
13 /// Create a `Duration`, using the postfix syntax.
14 ///
15 /// ## Example
16 ///
17 /// ```rust
18 /// use rs_std_ext::time::DurationNumExt;
19 /// use std::time::Duration;
20 ///
21 /// assert_eq!(10u8.seconds(), Duration::from_secs(10u64));
22 /// ```
23 ///
24 /// ## Panics
25 ///
26 /// This function panics if the number is too large or negative.
27 fn seconds(&self) -> Duration {
28 DurationNumExtFallible::seconds(self).unwrap()
29 }
30
31 /// Create a `Duration`, using the postfix syntax.
32 ///
33 /// ## Example
34 ///
35 /// ```rust
36 /// use rs_std_ext::time::DurationNumExt;
37 /// use std::time::Duration;
38 ///
39 /// assert_eq!(10u8.milliseconds(), Duration::from_millis(10u64));
40 /// ```
41 ///
42 /// ## Panics
43 ///
44 /// This function panics if the number is too large or negative.
45 fn milliseconds(&self) -> Duration {
46 DurationNumExtFallible::milliseconds(self).unwrap()
47 }
48
49 /// Create a `Duration`, using the postfix syntax.
50 ///
51 /// ## Example
52 ///
53 /// ```rust
54 /// use rs_std_ext::time::DurationNumExt;
55 /// use std::time::Duration;
56 ///
57 /// assert_eq!(10u8.microseconds(), Duration::from_micros(10u64));
58 /// ```
59 ///
60 /// ## Panics
61 ///
62 /// This function panics if the number is too large or negative.
63 fn microseconds(&self) -> Duration {
64 DurationNumExtFallible::microseconds(self).unwrap()
65 }
66
67 /// Create a `Duration`, using the postfix syntax.
68 ///
69 /// ## Example
70 ///
71 /// ```rust
72 /// use rs_std_ext::time::DurationNumExt;
73 /// use std::time::Duration;
74 ///
75 /// assert_eq!(10u8.nanoseconds(), Duration::from_nanos(10u64));
76 /// ```
77 ///
78 /// ## Panics
79 ///
80 /// This function panics if the number is too large or negative.
81 fn nanoseconds(&self) -> Duration {
82 DurationNumExtFallible::nanoseconds(self).unwrap()
83 }
84
85 /// Create a `Duration`, using the postfix syntax.
86 ///
87 /// ## Example
88 ///
89 /// ```rust
90 /// use rs_std_ext::time::DurationNumExt;
91 /// use std::time::Duration;
92 ///
93 /// assert_eq!(10u128.minutes(), Duration::from_secs(10u64 * 60));
94 /// ```
95 fn minutes(&self) -> Duration {
96 DurationNumExtFallible::minutes(self).unwrap()
97 }
98
99 /// Create a `Duration`, using the postfix syntax.
100 ///
101 /// ## Example
102 ///
103 /// ```rust
104 /// use rs_std_ext::time::DurationNumExt;
105 /// use std::time::Duration;
106 ///
107 /// assert_eq!(10u128.hours(), Duration::from_secs(10u64 * 60 * 60));
108 /// ```
109 fn hours(&self) -> Duration {
110 DurationNumExtFallible::hours(self).unwrap()
111 }
112
113 /// Create a `Duration`, using the postfix syntax.
114 ///
115 /// ## Example
116 ///
117 /// ```rust
118 /// use rs_std_ext::time::DurationNumExt;
119 /// use std::time::Duration;
120 ///
121 /// assert_eq!(10u128.days(), Duration::from_secs(10u64 * 60 * 60 * 24));
122 /// ```
123 fn days(&self) -> Duration {
124 DurationNumExtFallible::days(self).unwrap()
125 }
126}
127
128/// Extension methods for constructing [`std::time::Duration`] with numbers.
129///
130/// ## Note
131///
132/// This trait is not implemented for the `Duration`,
133/// for extension methods operating on a `Duration`,
134/// see [`crate::time::DurationExt`] for more information.
135///
136/// ## Warning
137///
138/// Unlike the `DurationNumExt`, this trait's method returns an
139/// `Option<Duration>`.
140/// Numbers that may be too large to be represented like [`num::BigInt`]
141/// or may be negative like signed integers will implement this trait.
142pub trait DurationNumExtFallible {
143 /// Create a `Duration`, using the postfix syntax.
144 ///
145 /// ## Example
146 ///
147 /// ```rust
148 /// use rs_std_ext::time::DurationNumExtFallible;
149 /// use std::time::Duration;
150 ///
151 /// assert_eq!(10u128.seconds(), Some(Duration::from_secs(10u64)));
152 /// assert!(u128::MAX.seconds().is_none());
153 /// ```
154 fn seconds(&self) -> Option<Duration>;
155
156 /// Create a `Duration`, using the postfix syntax.
157 ///
158 /// ## Example
159 ///
160 /// ```rust
161 /// use rs_std_ext::time::DurationNumExtFallible;
162 /// use std::time::Duration;
163 ///
164 ///
165 /// assert_eq!(10u128.milliseconds(), Some(Duration::from_millis(10u64)));
166 /// assert!(u128::MAX.milliseconds().is_none());
167 /// ```
168 fn milliseconds(&self) -> Option<Duration>;
169
170 /// Create a `Duration`, using the postfix syntax.
171 ///
172 /// ## Example
173 ///
174 /// ```rust
175 /// use rs_std_ext::time::DurationNumExtFallible;
176 /// use std::time::Duration;
177 ///
178 /// assert_eq!(10u128.microseconds(), Some(Duration::from_micros(10u64)));
179 /// assert!(u128::MAX.microseconds().is_none());
180 /// ```
181 fn microseconds(&self) -> Option<Duration>;
182
183 /// Create a `Duration`, using the postfix syntax.
184 ///
185 /// ## Example
186 ///
187 /// ```rust
188 /// use rs_std_ext::time::DurationNumExtFallible;
189 /// use std::time::Duration;
190 ///
191 /// assert_eq!(10u128.nanoseconds(), Some(Duration::from_nanos(10u64)));
192 /// assert!(u128::MAX.nanoseconds().is_none());
193 /// ```
194 fn nanoseconds(&self) -> Option<Duration>;
195
196 /// Create a `Duration`, using the postfix syntax.
197 ///
198 /// ## Example
199 ///
200 /// ```rust
201 /// use rs_std_ext::time::DurationNumExtFallible;
202 /// use std::time::Duration;
203 ///
204 /// assert_eq!(10u128.minutes(), Some(Duration::from_secs(10u64 * 60)));
205 /// ```
206 fn minutes(&self) -> Option<Duration>;
207
208 /// Create a `Duration`, using the postfix syntax.
209 ///
210 /// ## Example
211 ///
212 /// ```rust
213 /// use rs_std_ext::time::DurationNumExtFallible;
214 /// use std::time::Duration;
215 ///
216 /// assert_eq!(10u128.hours(), Some(Duration::from_secs(10u64 * 60 * 60)));
217 /// ```
218 fn hours(&self) -> Option<Duration>;
219
220 /// Create a `Duration`, using the postfix syntax.
221 ///
222 /// ## Example
223 ///
224 /// ```rust
225 /// use rs_std_ext::time::DurationNumExtFallible;
226 /// use std::time::Duration;
227 ///
228 /// assert_eq!(10u128.days(), Some(Duration::from_secs(10u64 * 60 * 60 * 24)));
229 /// ```
230 fn days(&self) -> Option<Duration>;
231}
232
233pub const SECS_PER_MINUTE: u64 = 60;
234pub const SECS_PER_HOUR: u64 = 60 * 60;
235pub const SECS_PER_DAY: u64 = 60 * 60 * 24;
236
237#[cfg(feature = "crate-num")]
238mod __num_impl {
239 use std::time::Duration;
240
241 use num::{BigInt, BigUint, ToPrimitive};
242
243 use super::{extfn, DurationNumExtFallible, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MINUTE};
244
245 impl DurationNumExtFallible for BigUint {
246 #[inline]
247 fn seconds(&self) -> Option<Duration> {
248 self.to_u64().and_then(extfn::checked_from_secs)
249 }
250
251 #[inline]
252 fn microseconds(&self) -> Option<Duration> {
253 self.to_u64().and_then(extfn::checked_from_micros)
254 }
255
256 #[inline]
257 fn milliseconds(&self) -> Option<Duration> {
258 self.to_u64().and_then(extfn::checked_from_millis)
259 }
260
261 #[inline]
262 fn nanoseconds(&self) -> Option<Duration> {
263 self.to_u64().and_then(extfn::checked_from_nanos)
264 }
265
266 #[inline]
267 fn minutes(&self) -> Option<Duration> {
268 self.to_u64()
269 .and_then(|v| v.checked_mul(SECS_PER_MINUTE))
270 .and_then(extfn::checked_from_secs)
271 }
272
273 #[inline]
274 fn hours(&self) -> Option<Duration> {
275 self.to_u64()
276 .and_then(|v| v.checked_mul(SECS_PER_HOUR))
277 .and_then(extfn::checked_from_secs)
278 }
279
280 #[inline]
281 fn days(&self) -> Option<Duration> {
282 self.to_u64()
283 .and_then(|v| v.checked_mul(SECS_PER_DAY))
284 .and_then(extfn::checked_from_secs)
285 }
286 }
287
288 impl DurationNumExtFallible for BigInt {
289 #[inline]
290 fn seconds(&self) -> Option<Duration> {
291 self.to_u64().and_then(extfn::checked_from_secs)
292 }
293
294 #[inline]
295 fn microseconds(&self) -> Option<Duration> {
296 self.to_u64().and_then(extfn::checked_from_micros)
297 }
298
299 #[inline]
300 fn milliseconds(&self) -> Option<Duration> {
301 self.to_u64().and_then(extfn::checked_from_millis)
302 }
303
304 #[inline]
305 fn nanoseconds(&self) -> Option<Duration> {
306 self.to_u64().and_then(extfn::checked_from_nanos)
307 }
308
309 #[inline]
310 fn minutes(&self) -> Option<Duration> {
311 self.to_u64()
312 .and_then(|v| v.checked_mul(SECS_PER_MINUTE))
313 .and_then(extfn::checked_from_secs)
314 }
315
316 #[inline]
317 fn hours(&self) -> Option<Duration> {
318 self.to_u64()
319 .and_then(|v| v.checked_mul(SECS_PER_HOUR))
320 .and_then(extfn::checked_from_secs)
321 }
322
323 #[inline]
324 fn days(&self) -> Option<Duration> {
325 self.to_u64()
326 .and_then(|v| v.checked_mul(SECS_PER_DAY))
327 .and_then(extfn::checked_from_secs)
328 }
329 }
330}
331
332mod __std_impl {
333 use std::time::Duration;
334
335 use super::{
336 extfn, DurationNumExt, DurationNumExtFallible, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MINUTE,
337 };
338
339 macro_rules! __impl_non_fallible {
340 ($($ty:ty),*) => {
341 $(__impl_non_fallible!(@$ty);)*
342 };
343
344 (@$ty:ty) => {
345 impl DurationNumExt for $ty {
346 #[inline]
347 fn seconds(&self) -> Duration {
348 Duration::from_secs(*self as u64)
349 }
350
351 #[inline]
352 fn microseconds(&self) -> Duration {
353 Duration::from_micros(*self as u64)
354 }
355
356 #[inline]
357 fn milliseconds(&self) -> Duration {
358 Duration::from_millis(*self as u64)
359 }
360
361 #[inline]
362 fn nanoseconds(&self) -> Duration {
363 Duration::from_nanos(*self as u64)
364 }
365 }
366 };
367 }
368
369 __impl_non_fallible!(u8, u16, u32, u64);
370
371 macro_rules! __impl_fallible_int {
372 (with-unwrap $($ty:ty),*) => {
373 $(__impl_fallible_int!(@$ty);)*
374 $(impl DurationNumExt for $ty {})*
375 };
376
377 ($($ty:ty),*) => {
378 $(__impl_fallible_int!(@$ty);)*
379 };
380
381 (@$ty:ty) => {
382 impl DurationNumExtFallible for $ty {
383 #[inline]
384 fn seconds(&self) -> Option<Duration> {
385 (*self).try_into().ok().and_then(extfn::checked_from_secs)
386 }
387
388 #[inline]
389 fn microseconds(&self) -> Option<Duration> {
390 (*self).try_into().ok().and_then(extfn::checked_from_micros)
391 }
392
393 #[inline]
394 fn milliseconds(&self) -> Option<Duration> {
395 (*self).try_into().ok().and_then(extfn::checked_from_millis)
396 }
397
398 #[inline]
399 fn nanoseconds(&self) -> Option<Duration> {
400 (*self).try_into().ok().and_then(extfn::checked_from_nanos)
401 }
402
403 #[inline]
404 fn minutes(&self) -> Option<Duration> {
405 (*self)
406 .try_into()
407 .ok()
408 .and_then(|e: u64| e.checked_mul(SECS_PER_MINUTE))
409 .and_then(extfn::checked_from_secs)
410 }
411
412 #[inline]
413 fn hours(&self) -> Option<Duration> {
414 (*self)
415 .try_into()
416 .ok()
417 .and_then(|e: u64| e.checked_mul(SECS_PER_HOUR))
418 .and_then(extfn::checked_from_secs)
419 }
420
421 #[inline]
422 fn days(&self) -> Option<Duration> {
423 (*self)
424 .try_into()
425 .ok()
426 .and_then(|e: u64| e.checked_mul(SECS_PER_DAY))
427 .and_then(extfn::checked_from_secs)
428 }
429 }
430 };
431 }
432
433 __impl_fallible_int!(with-unwrap i8, i16, i32, i64, i128, u128);
434 // theoretically infallible
435 __impl_fallible_int!(u8, u16, u32, u64);
436
437 macro_rules! __impl_fallible_float {
438 ($($ty:ty: $ident:ident),*) => {
439 $(__impl_fallible_float!(@$ty, $ident);)*
440 };
441
442 (@$ty:ty, $ident:ident) => {
443 impl DurationNumExtFallible for $ty {
444 #[inline]
445 fn seconds(&self) -> Option<Duration> {
446 Duration::$ident(*self).ok()
447 }
448
449 #[inline]
450 fn microseconds(&self) -> Option<Duration> {
451 Duration::$ident(*self / 1_000_000.0).ok()
452 }
453
454 #[inline]
455 fn milliseconds(&self) -> Option<Duration> {
456 Duration::$ident(*self / 1_000.0).ok()
457 }
458
459 #[inline]
460 fn nanoseconds(&self) -> Option<Duration> {
461 Duration::$ident(*self / 1_000_000_000.0).ok()
462 }
463
464 #[inline]
465 fn minutes(&self) -> Option<Duration> {
466 Duration::$ident(*self * 60.0).ok()
467 }
468
469 #[inline]
470 fn hours(&self) -> Option<Duration> {
471 Duration::$ident(*self * 3600.0).ok()
472 }
473
474 #[inline]
475 fn days(&self) -> Option<Duration> {
476 Duration::$ident(*self * 86400.0).ok()
477 }
478 }
479
480 impl DurationNumExt for $ty {}
481 };
482 }
483
484 __impl_fallible_float!(f32: try_from_secs_f32, f64: try_from_secs_f64);
485}
486
487pub const NANOS_PER_SEC: u32 = 1_000_000_000;
488pub const NANOS_PER_MILLI: u32 = 1_000_000;
489pub const NANOS_PER_MICRO: u32 = 1_000;
490pub const MILLIS_PER_SEC: u64 = 1_000;
491pub const MICROS_PER_SEC: u64 = 1_000_000;
492
493/// Static functions for [`std::time::Duration`].
494pub mod extfn {
495 use std::time::Duration;
496
497 use super::{MICROS_PER_SEC, MILLIS_PER_SEC, NANOS_PER_MICRO, NANOS_PER_MILLI, NANOS_PER_SEC};
498
499 /// A checked version of [`Duration::from_secs`][duration_from_secs].
500 ///
501 /// [duration_from_secs]: std::time::Duration#method.from_secs
502 pub fn checked_from_secs(secs: u64) -> Option<Duration> {
503 checked_new(secs, 0)
504 }
505
506 /// A checked version of [`Duration::from_millis`][duration_from_millis].
507 ///
508 /// [duration_from_millis]: std::time::Duration#method.from_millis
509 pub fn checked_from_millis(millis: u64) -> Option<Duration> {
510 millis
511 .checked_div(MILLIS_PER_SEC)
512 .zip(
513 millis
514 .checked_rem(MILLIS_PER_SEC)
515 .and_then(|r| <u64 as TryInto<u32>>::try_into(r).ok())
516 .and_then(|r| r.checked_mul(NANOS_PER_MILLI)),
517 )
518 .and_then(|(x, y)| checked_new(x, y))
519 }
520
521 /// A checked version of [`Duration::from_micros`][duration_from_micros].
522 ///
523 /// [duration_from_micros]: std::time::Duration#method.from_micros
524 pub fn checked_from_micros(micros: u64) -> Option<Duration> {
525 micros
526 .checked_div(MICROS_PER_SEC)
527 .zip(
528 micros
529 .checked_rem(MICROS_PER_SEC)
530 .and_then(|r| <u64 as TryInto<u32>>::try_into(r).ok())
531 .and_then(|r| r.checked_mul(NANOS_PER_MICRO)),
532 )
533 .and_then(|(x, y)| checked_new(x, y))
534 }
535
536 /// A checked version of [`Duration::from_nanos`][duration_from_nanos].
537 ///
538 /// [duration_from_nanos]: std::time::Duration#method.from_nanos
539 pub fn checked_from_nanos(nanos: u64) -> Option<Duration> {
540 nanos
541 .checked_div(NANOS_PER_SEC as u64)
542 .zip(
543 nanos
544 .checked_rem(NANOS_PER_SEC as u64)
545 .and_then(|r| <u64 as TryInto<u32>>::try_into(r).ok()),
546 )
547 .and_then(|(x, y)| checked_new(x, y))
548 }
549
550 /// A pre-checked version of [`Duration::new`][duration_new].
551 ///
552 /// Note that `Duration` does not offer direct construction,
553 /// and this method internally calls `Duration::new`, which
554 /// might still panics. However, this method uplifts the check,
555 /// and theoretically never panics.
556 ///
557 /// [duration_new]: std::time::Duration#method.new
558 pub fn checked_new(secs: u64, nanos: u32) -> Option<Duration> {
559 let secs = match secs.checked_add((nanos / NANOS_PER_SEC) as u64) {
560 Some(secs) => secs,
561 None => return None,
562 };
563 let nanos = nanos % NANOS_PER_SEC;
564 Some(Duration::new(secs, nanos))
565 }
566}
567
568/// Extension methods for [`std::time::Duration`].
569pub trait DurationExt {}