devela/phys/time/delta.rs
1// devela::phys::time::delta
2//
3//! Defines the [`TimeDelta`] struct.
4//
5// TOC
6// - basic methods
7// - Operations involving integers
8// - Operations involving floats
9// - additional methods
10// - conversions
11//
12// TODO
13// - signed duration from jiff + time (similar to duration but signed)
14// - impl conversions from/to std, jiff::SignedDuration, TimeSplit
15// - operate on SystemInstant TODO
16// - improve error types (use standalone crate errors, from num, etc.)
17// - I believe only individual errors are returned in any case.
18
19use crate::Duration;
20#[allow(unused)]
21#[cfg(_float··)]
22use crate::ExtFloat;
23#[cfg(feature = "std")]
24use crate::SystemInstant;
25
26/// A signed duration of time, stored as an `(i64, i32)` pair of seconds and nanoseconds.
27///
28/// Supports negative values, allowing representation of both past and future offsets.
29//
30// TODO: comparison with Duration.
31#[doc = crate::doc_!(vendor: "jiff")]
32#[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
33pub struct TimeDelta {
34 secs: i64,
35 nanos: i32,
36}
37
38const NANOS_PER_SEC: i32 = 1_000_000_000;
39const NANOS_PER_MILLI: i32 = 1_000_000;
40const NANOS_PER_MICRO: i32 = 1_000;
41const MILLIS_PER_SEC: i64 = 1_000;
42const MICROS_PER_SEC: i64 = 1_000_000;
43const SECS_PER_MINUTE: i64 = 60;
44const MINS_PER_HOUR: i64 = 60;
45
46/// # Basic methods
47impl TimeDelta {
48 /// A duration of zero time.
49 pub const ZERO: TimeDelta = TimeDelta { secs: 0, nanos: 0 };
50
51 /// The minimum possible duration. Or the "most negative" duration.
52 pub const MIN: TimeDelta = TimeDelta { secs: i64::MIN, nanos: -(NANOS_PER_SEC - 1) };
53
54 /// The maximum possible duration.
55 pub const MAX: TimeDelta = TimeDelta { secs: i64::MAX, nanos: NANOS_PER_SEC - 1 };
56
57 /// Creates a new `TimeDelta` from the given number of whole seconds and additional nanoseconds.
58 ///
59 /// If the absolute value of the nanoseconds is greater than or equal to
60 /// 1 second, then the excess balances into the number of whole seconds.
61 ///
62 /// # Panics
63 /// When the absolute value of the nanoseconds is greater than or equal
64 /// to 1 second and the excess that carries over to the number of whole
65 /// seconds overflows `i64`.
66 ///
67 /// This never panics when `nanos` is less than `1_000_000_000`.
68 pub const fn new(mut secs: i64, mut nanos: i32) -> TimeDelta {
69 // When |nanos| exceeds 1 second, we balance the excess up to seconds.
70 if !(-NANOS_PER_SEC < nanos && nanos < NANOS_PER_SEC) {
71 // Never wraps or panics because NANOS_PER_SEC!={0,-1}.
72 let addsecs = nanos / NANOS_PER_SEC;
73 secs = match secs.checked_add(addsecs as i64) {
74 Some(secs) => secs,
75 None => panic!("nanoseconds overflowed seconds in TimeDelta::new"),
76 };
77 // Never wraps or panics because NANOS_PER_SEC!={0,-1}.
78 nanos %= NANOS_PER_SEC;
79 }
80 // At this point, we're done if either unit is zero or if they have the same sign.
81 if nanos == 0 || secs == 0 || secs.signum() == (nanos.signum() as i64) {
82 return TimeDelta::new_unchecked(secs, nanos);
83 }
84 // Otherwise, the only work we have to do is to balance negative nanos
85 // into positive seconds, or positive nanos into negative seconds.
86 if secs < 0 {
87 debug_assert!(nanos > 0);
88 // Never wraps because adding +1 to a negative i64 never overflows.
89 //
90 // MSRV(1.79): Consider using `unchecked_add` here.
91 secs += 1;
92 // Never wraps because subtracting +1_000_000_000 from a positive i32 never overflows.
93 //
94 // MSRV(1.79): Consider using `unchecked_sub` here.
95 nanos -= NANOS_PER_SEC;
96 } else {
97 debug_assert!(secs > 0);
98 debug_assert!(nanos < 0);
99 // Never wraps because subtracting +1 from a positive i64 never
100 // overflows.
101 //
102 // MSRV(1.79): Consider using `unchecked_add` here.
103 secs -= 1;
104 // Never wraps because adding +1_000_000_000 to a negative i32 never overflows.
105 //
106 // MSRV(1.79): Consider using `unchecked_add` here.
107 nanos += NANOS_PER_SEC;
108 }
109 TimeDelta::new_unchecked(secs, nanos)
110 }
111
112 // CHECK where's used originally
113 // /// Creates a new signed duration without handling nanosecond overflow.
114 // ///
115 // /// This might produce tighter code in some cases.
116 // ///
117 // /// # Panics
118 // /// When `|nanos|` is greater than or equal to 1 second.
119 // pub(crate) const fn new_without_nano_overflow(
120 // secs: i64,
121 // nanos: i32,
122 // ) -> TimeDelta {
123 // assert!(nanos <= 999_999_999);
124 // assert!(nanos >= -999_999_999);
125 // TimeDelta::new_unchecked(secs, nanos)
126 // }
127
128 /// Creates a new signed duration without handling nanosecond overflow.
129 ///
130 /// This might produce tighter code in some cases.
131 ///
132 /// In debug mode only, when `|nanos|` is greater than or equal to 1 second.
133 ///
134 /// This is not exported so that code outside this module can rely on
135 /// `|nanos|` being less than a second for purposes of memory safety.
136 const fn new_unchecked(secs: i64, nanos: i32) -> TimeDelta {
137 debug_assert!(nanos <= 999_999_999);
138 debug_assert!(nanos >= -999_999_999);
139 TimeDelta { secs, nanos }
140 }
141
142 /// Creates a new `TimeDelta` from the given number of whole seconds.
143 pub const fn from_secs(secs: i64) -> TimeDelta {
144 TimeDelta::new_unchecked(secs, 0)
145 }
146
147 /// Creates a new `TimeDelta` from the given number of whole milliseconds.
148 ///
149 /// Note that since this accepts an `i64`, this method cannot be used
150 /// to construct the full range of possible signed duration values. In
151 /// particular, [`TimeDelta::as_millis`] returns an `i128`, and this
152 /// may be a value that would otherwise overflow an `i64`.
153 pub const fn from_millis(millis: i64) -> TimeDelta {
154 // OK because MILLIS_PER_SEC!={-1,0}.
155 let secs = millis / MILLIS_PER_SEC;
156 // OK because MILLIS_PER_SEC!={-1,0} and because millis % MILLIS_PER_SEC
157 // can be at most 999, and 999 * 1_000_000 never overflows i32.
158 let nanos = (millis % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI;
159 TimeDelta::new_unchecked(secs, nanos)
160 }
161
162 /// Creates a new `TimeDelta` from the given number of whole microseconds.
163 ///
164 /// Note that since this accepts an `i64`, this method cannot be used
165 /// to construct the full range of possible signed duration values. In
166 /// particular, [`TimeDelta::as_micros`] returns an `i128`, and this
167 /// may be a value that would otherwise overflow an `i64`.
168 pub const fn from_micros(micros: i64) -> TimeDelta {
169 // OK because MICROS_PER_SEC!={-1,0}.
170 let secs = micros / MICROS_PER_SEC;
171 // OK because MICROS_PER_SEC!={-1,0} and because millis % MICROS_PER_SEC
172 // can be at most 999, and 999 * 1_000_000 never overflows i32.
173 let nanos = (micros % MICROS_PER_SEC) as i32 * NANOS_PER_MICRO;
174 TimeDelta::new_unchecked(secs, nanos)
175 }
176
177 /// Creates a new `TimeDelta` from the given number of whole nanoseconds.
178 ///
179 /// Note that since this accepts an `i64`, this method cannot be used
180 /// to construct the full range of possible signed duration values. In
181 /// particular, [`TimeDelta::as_nanos`] returns an `i128`, which may
182 /// be a value that would otherwise overflow an `i64`.
183 pub const fn from_nanos(nanos: i64) -> TimeDelta {
184 // OK because NANOS_PER_SEC!={-1,0}.
185 let secs = nanos / (NANOS_PER_SEC as i64);
186 // OK because NANOS_PER_SEC!={-1,0}.
187 let nanos = (nanos % (NANOS_PER_SEC as i64)) as i32;
188 TimeDelta::new_unchecked(secs, nanos)
189 }
190
191 /// Creates a new `TimeDelta` from the given number of hours.
192 /// Every hour is exactly `3,600` seconds.
193 ///
194 /// # Panics
195 /// Panics if the number of hours, after being converted to nanoseconds,
196 /// overflows the minimum or maximum `TimeDelta` values.
197 pub const fn from_hours(hours: i64) -> TimeDelta {
198 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
199 const MIN_HOUR: i64 = i64::MIN / (SECS_PER_MINUTE * MINS_PER_HOUR);
200 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
201 const MAX_HOUR: i64 = i64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR);
202 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
203 if hours < MIN_HOUR {
204 panic!("hours overflowed minimum number of TimeDelta seconds")
205 }
206 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
207 if hours > MAX_HOUR {
208 panic!("hours overflowed maximum number of TimeDelta seconds")
209 }
210 TimeDelta::from_secs(hours * MINS_PER_HOUR * SECS_PER_MINUTE)
211 }
212
213 /// Creates a new `TimeDelta` from the given number of minutes.
214 /// Every minute is exactly `60` seconds.
215 ///
216 /// # Panics
217 /// Panics if the number of minutes, after being converted to nanoseconds,
218 /// overflows the minimum or maximum `TimeDelta` values.
219 pub const fn from_mins(minutes: i64) -> TimeDelta {
220 // OK because SECS_PER_MINUTE!={-1,0}.
221 const MIN_MINUTE: i64 = i64::MIN / SECS_PER_MINUTE;
222 // OK because SECS_PER_MINUTE!={-1,0}.
223 const MAX_MINUTE: i64 = i64::MAX / SECS_PER_MINUTE;
224 // OK because SECS_PER_MINUTE!={-1,0}.
225 if minutes < MIN_MINUTE {
226 panic!("minutes overflowed minimum number of TimeDelta seconds")
227 }
228 // OK because SECS_PER_MINUTE!={-1,0}.
229 if minutes > MAX_MINUTE {
230 panic!("minutes overflowed maximum number of TimeDelta seconds")
231 }
232 TimeDelta::from_secs(minutes * SECS_PER_MINUTE)
233 }
234
235 /// Returns true if this duration spans no time.
236 pub const fn is_zero(&self) -> bool {
237 self.secs == 0 && self.nanos == 0
238 }
239
240 /// Returns the number of whole seconds in this duration.
241 ///
242 /// The value returned is negative when the duration is negative.
243 ///
244 /// This does not include any fractional component corresponding to units
245 /// less than a second. To access those, use one of the `subsec` methods
246 /// such as [`TimeDelta::subsec_nanos`].
247 pub const fn as_secs(&self) -> i64 {
248 self.secs
249 }
250
251 /// Returns the fractional part of this duration in whole milliseconds.
252 ///
253 /// The value returned is negative when the duration is negative. It is
254 /// guaranteed that the range of the value returned is in the inclusive
255 /// range `-999..=999`.
256 ///
257 /// To get the length of the total duration represented in milliseconds,
258 /// use [`TimeDelta::as_millis`].
259 pub const fn subsec_millis(&self) -> i32 {
260 // OK because NANOS_PER_MILLI!={-1,0}.
261 self.nanos / NANOS_PER_MILLI
262 }
263
264 /// Returns the fractional part of this duration in whole microseconds.
265 ///
266 /// The value returned is negative when the duration is negative. It is
267 /// guaranteed that the range of the value returned is in the inclusive
268 /// range `-999_999..=999_999`.
269 ///
270 /// To get the length of the total duration represented in microseconds,
271 /// use [`TimeDelta::as_micros`].
272 pub const fn subsec_micros(&self) -> i32 {
273 // OK because NANOS_PER_MICRO!={-1,0}.
274 self.nanos / NANOS_PER_MICRO
275 }
276
277 /// Returns the fractional part of this duration in whole nanoseconds.
278 ///
279 /// The value returned is negative when the duration is negative. It is
280 /// guaranteed that the range of the value returned is in the inclusive
281 /// range `-999_999_999..=999_999_999`.
282 ///
283 /// To get the length of the total duration represented in nanoseconds,
284 /// use [`TimeDelta::as_nanos`].
285 pub const fn subsec_nanos(&self) -> i32 {
286 self.nanos
287 }
288
289 /// Returns the total duration in units of whole milliseconds.
290 ///
291 /// The value returned is negative when the duration is negative.
292 ///
293 /// To get only the fractional component of this duration in units of
294 /// whole milliseconds, use [`TimeDelta::subsec_millis`].
295 pub const fn as_millis(&self) -> i128 {
296 // OK because 1_000 times any i64 will never overflow i128.
297 let millis = (self.secs as i128) * (MILLIS_PER_SEC as i128);
298 // OK because NANOS_PER_MILLI!={-1,0}.
299 let subsec_millis = (self.nanos / NANOS_PER_MILLI) as i128;
300 // OK because subsec_millis maxes out at 999, and adding that to
301 // i64::MAX*1_000 will never overflow a i128.
302 millis + subsec_millis
303 }
304
305 /// Returns the total duration in units of whole microseconds.
306 ///
307 /// The value returned is negative when the duration is negative.
308 ///
309 /// To get only the fractional component of this duration in units of
310 /// whole microseconds, use [`TimeDelta::subsec_micros`].
311 pub const fn as_micros(&self) -> i128 {
312 // OK because 1_000_000 times any i64 will never overflow i128.
313 let micros = (self.secs as i128) * (MICROS_PER_SEC as i128);
314 // OK because NANOS_PER_MICRO!={-1,0}.
315 let subsec_micros = (self.nanos / NANOS_PER_MICRO) as i128;
316 // OK because subsec_micros maxes out at 999_999, and adding that to
317 // i64::MAX*1_000_000 will never overflow a i128.
318 micros + subsec_micros
319 }
320
321 /// Returns the total duration in units of whole nanoseconds.
322 ///
323 /// The value returned is negative when the duration is negative.
324 ///
325 /// To get only the fractional component of this duration in units of
326 /// whole nanoseconds, use [`TimeDelta::subsec_nanos`].
327 pub const fn as_nanos(&self) -> i128 {
328 // OK because 1_000_000_000 times any i64 will never overflow i128.
329 let nanos = (self.secs as i128) * (NANOS_PER_SEC as i128);
330 // OK because subsec_nanos maxes out at 999_999_999, and adding that to
331 // i64::MAX*1_000_000_000 will never overflow a i128.
332 nanos + (self.nanos as i128)
333 }
334
335 // NOTE: We don't provide `abs_diff` here because we can't represent the
336 // difference between all possible durations. For example,
337 // `abs_diff(TimeDelta::MAX, TimeDelta::MIN)`. It therefore seems
338 // like we should actually return a `std::time::Duration` here, but I'm
339 // trying to be conservative when diverging from std.
340}
341
342/// # Operations.
343impl TimeDelta {
344 /// Add two signed durations together. If overflow occurs, then `None` is returned.
345 pub const fn checked_add(self, rhs: TimeDelta) -> Option<TimeDelta> {
346 let Some(mut secs) = self.secs.checked_add(rhs.secs) else {
347 return None;
348 };
349 // OK because `-999_999_999 <= nanos <= 999_999_999`, and so adding
350 // them together will never overflow an i32.
351 let mut nanos = self.nanos + rhs.nanos;
352 // The below is effectively TimeDelta::new, but with checked
353 // arithmetic. My suspicion is that there is probably a better way
354 // to do this. The main complexity here is that 1) `|nanos|` might
355 // now exceed 1 second and 2) the signs of `secs` and `nanos` might
356 // not be the same. The other difference from TimeDelta::new is
357 // that we know that `-1_999_999_998 <= nanos <= 1_999_999_998` since
358 // `|TimeDelta::nanos|` is guaranteed to be less than 1 second. So
359 // we can skip the div and modulus operations.
360
361 // When |nanos| exceeds 1 second, we balance the excess up to seconds.
362 if nanos != 0 {
363 if nanos >= NANOS_PER_SEC {
364 nanos -= NANOS_PER_SEC;
365 secs = match secs.checked_add(1) {
366 None => return None,
367 Some(secs) => secs,
368 };
369 } else if nanos <= -NANOS_PER_SEC {
370 nanos += NANOS_PER_SEC;
371 secs = match secs.checked_sub(1) {
372 None => return None,
373 Some(secs) => secs,
374 };
375 }
376 if secs != 0 && nanos != 0 && secs.signum() != (nanos.signum() as i64) {
377 if secs < 0 {
378 debug_assert!(nanos > 0);
379 // OK because secs<0.
380 secs += 1;
381 // OK because nanos>0.
382 nanos -= NANOS_PER_SEC;
383 } else {
384 debug_assert!(secs > 0);
385 debug_assert!(nanos < 0);
386 // OK because secs>0.
387 secs -= 1;
388 // OK because nanos<0.
389 nanos += NANOS_PER_SEC;
390 }
391 }
392 }
393 Some(TimeDelta::new_unchecked(secs, nanos))
394 }
395
396 /// Add two signed durations together. If overflow occurs, then arithmetic saturates.
397 pub const fn saturating_add(self, rhs: TimeDelta) -> TimeDelta {
398 let Some(sum) = self.checked_add(rhs) else {
399 return if rhs.is_negative() { TimeDelta::MIN } else { TimeDelta::MAX };
400 };
401 sum
402 }
403
404 /// Subtract one signed duration from another. If overflow occurs, then `None` is returned.
405 pub const fn checked_sub(self, rhs: TimeDelta) -> Option<TimeDelta> {
406 let Some(rhs) = rhs.checked_neg() else {
407 return None;
408 };
409 self.checked_add(rhs)
410 }
411
412 /// Add two signed durations together. If overflow occurs, then arithmetic saturates.
413 pub const fn saturating_sub(self, rhs: TimeDelta) -> TimeDelta {
414 let Some(diff) = self.checked_sub(rhs) else {
415 return if rhs.is_positive() { TimeDelta::MIN } else { TimeDelta::MAX };
416 };
417 diff
418 }
419}
420
421/// # Operations involving integers.
422impl TimeDelta {
423 /// Multiply this signed duration by an integer.
424 /// If the multiplication overflows, then `None` is returned.
425 pub const fn checked_mul(self, rhs: i32) -> Option<TimeDelta> {
426 let rhs = rhs as i64;
427 // Multiplying any two i32 values never overflows an i64.
428 let nanos = (self.nanos as i64) * rhs;
429 // OK since NANOS_PER_SEC!={-1,0}.
430 let addsecs = nanos / (NANOS_PER_SEC as i64);
431 // OK since NANOS_PER_SEC!={-1,0}.
432 let nanos = (nanos % (NANOS_PER_SEC as i64)) as i32;
433 let Some(secs) = self.secs.checked_mul(rhs) else {
434 return None;
435 };
436 let Some(secs) = secs.checked_add(addsecs) else {
437 return None;
438 };
439 Some(TimeDelta::new_unchecked(secs, nanos))
440 }
441
442 /// Multiply this signed duration by an integer.
443 ///
444 /// If the multiplication overflows, then the result saturates to either
445 /// the minimum or maximum duration depending on the sign of the product.
446 pub const fn saturating_mul(self, rhs: i32) -> TimeDelta {
447 let Some(product) = self.checked_mul(rhs) else {
448 let sign = (self.signum() as i64) * (rhs as i64).signum();
449 return if sign.is_negative() { TimeDelta::MIN } else { TimeDelta::MAX };
450 };
451 product
452 }
453
454 /// Divide this duration by an integer. If the division overflows, then `None` is returned.
455 pub const fn checked_div(self, rhs: i32) -> Option<TimeDelta> {
456 if rhs == 0 || (self.secs == i64::MIN && rhs == -1) {
457 return None;
458 }
459 // OK since rhs!={-1,0}.
460 let secs = self.secs / (rhs as i64);
461 // OK since rhs!={-1,0}.
462 let addsecs = self.secs % (rhs as i64);
463 // OK since rhs!=0 and self.nanos>i32::MIN.
464 let mut nanos = self.nanos / rhs;
465 // OK since rhs!=0 and self.nanos>i32::MIN.
466 let addnanos = self.nanos % rhs;
467 let leftover_nanos = (addsecs * (NANOS_PER_SEC as i64)) + (addnanos as i64);
468 nanos += (leftover_nanos / (rhs as i64)) as i32;
469 debug_assert!(nanos < NANOS_PER_SEC);
470 Some(TimeDelta::new_unchecked(secs, nanos))
471 }
472}
473
474/// # Operations involving floating-point numbers.
475impl TimeDelta {
476 /// Returns the number of seconds, with a possible fractional nanosecond component.
477 pub fn as_secs_f64(&self) -> f64 {
478 (self.secs as f64) + ((self.nanos as f64) / (NANOS_PER_SEC as f64))
479 }
480
481 /// Returns the number of seconds, with a possible fractional nanosecond component.
482 pub fn as_secs_f32(&self) -> f32 {
483 (self.secs as f32) + ((self.nanos as f32) / (NANOS_PER_SEC as f32))
484 }
485
486 /// Returns the number of milliseconds, with a possible fractional nanosecond component.
487 pub fn as_millis_f64(&self) -> f64 {
488 ((self.secs as f64) * (MILLIS_PER_SEC as f64))
489 + ((self.nanos as f64) / (NANOS_PER_MILLI as f64))
490 }
491
492 /// Returns the number of milliseconds, with a possible fractional nanosecond component.
493 pub fn as_millis_f32(&self) -> f32 {
494 ((self.secs as f32) * (MILLIS_PER_SEC as f32))
495 + ((self.nanos as f32) / (NANOS_PER_MILLI as f32))
496 }
497
498 /// Returns a signed duration corresponding to the number of seconds.
499 ///
500 /// The number given may have a fractional nanosecond component.
501 ///
502 /// # Panics
503 /// Panics if the given float overflows the minimum or maximum signed duration values.
504 #[cfg(any(feature = "std", feature = "_float_f64"))]
505 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f64"))))]
506 pub fn from_secs_f64(secs: f64) -> TimeDelta {
507 TimeDelta::try_from_secs_f64(secs).expect("finite and in-bounds f64")
508 }
509
510 /// Returns a signed duration corresponding to the number of seconds.
511 ///
512 /// The number given may have a fractional nanosecond component.
513 ///
514 /// # Panics
515 /// Panics if the given float overflows the minimum or maximum signed duration values.
516 #[cfg(any(feature = "std", feature = "_float_f32"))]
517 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f32"))))]
518 pub fn from_secs_f32(secs: f32) -> TimeDelta {
519 TimeDelta::try_from_secs_f32(secs).expect("finite and in-bounds f32")
520 }
521
522 /// Returns a signed duration corresponding to the number of seconds.
523 ///
524 /// The number given may have a fractional nanosecond component.
525 ///
526 /// If the given float overflows the minimum or maximum signed duration
527 /// values, then an error is returned.
528 #[cfg(any(feature = "std", feature = "_float_f64"))]
529 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f64"))))]
530 pub fn try_from_secs_f64(secs: f64) -> Result<TimeDelta, &'static str> {
531 if !secs.is_finite() {
532 return Err("could not convert non-finite seconds {secs} to signed duration");
533 }
534 if secs < (i64::MIN as f64) {
535 return Err("floating point seconds {secs} overflows TimeDelta::MIN");
536 }
537 if secs > (i64::MAX as f64) {
538 return Err("floating point seconds {secs} overflows TimeDelta::MAX");
539 }
540 let nanos = (secs.fract() * (NANOS_PER_SEC as f64)).round() as i32;
541 let secs = secs.trunc() as i64;
542 Ok(TimeDelta::new_unchecked(secs, nanos))
543 }
544
545 /// Returns a signed duration corresponding to the number of seconds.
546 ///
547 /// The number given may have a fractional nanosecond component.
548 ///
549 /// If the given float overflows the minimum or maximum signed duration
550 /// values, then an error is returned.
551 #[cfg(any(feature = "std", feature = "_float_f32"))]
552 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f32"))))]
553 pub fn try_from_secs_f32(secs: f32) -> Result<TimeDelta, &'static str> {
554 if !secs.is_finite() {
555 return Err("could not convert non-finite seconds {secs} to signed duration");
556 }
557 if secs < (i64::MIN as f32) {
558 return Err("floating point seconds {secs} overflows TimeDelta::MIN");
559 }
560 if secs > (i64::MAX as f32) {
561 return Err("floating point seconds {secs} overflows TimeDelta::MAX");
562 }
563 let nanos = (secs.fract() * (NANOS_PER_SEC as f32)).round() as i32;
564 let secs = secs.trunc() as i64;
565 Ok(TimeDelta::new_unchecked(secs, nanos))
566 }
567
568 /// Returns the result of multiplying this duration by the given 64-bit float.
569 ///
570 /// # Panics
571 /// This panics if the result is not finite or overflows a `TimeDelta`.
572 #[cfg(any(feature = "std", feature = "_float_f64"))]
573 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f64"))))]
574 pub fn mul_f64(self, rhs: f64) -> TimeDelta {
575 TimeDelta::from_secs_f64(rhs * self.as_secs_f64())
576 }
577
578 /// Returns the result of multiplying this duration by the given 32-bit float.
579 ///
580 /// # Panics
581 /// This panics if the result is not finite or overflows a `TimeDelta`.
582 #[cfg(any(feature = "std", feature = "_float_f32"))]
583 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f32"))))]
584 pub fn mul_f32(self, rhs: f32) -> TimeDelta {
585 TimeDelta::from_secs_f32(rhs * self.as_secs_f32())
586 }
587
588 /// Returns the result of dividing this duration by the given `f64`.
589 ///
590 /// # Panics
591 /// This panics if the result is not finite or overflows a `TimeDelta`.
592 #[cfg(any(feature = "std", feature = "_float_f64"))]
593 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f64"))))]
594 pub fn div_f64(self, rhs: f64) -> TimeDelta {
595 TimeDelta::from_secs_f64(self.as_secs_f64() / rhs)
596 }
597
598 /// Returns the result of dividing this duration by the given `f32`.
599 ///
600 /// # Panics
601 /// This panics if the result is not finite or overflows a `TimeDelta`.
602 #[cfg(any(feature = "std", feature = "_float_f32"))]
603 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f32"))))]
604 pub fn div_f32(self, rhs: f32) -> TimeDelta {
605 TimeDelta::from_secs_f32(self.as_secs_f32() / rhs)
606 }
607
608 /// Divides this signed duration by another signed duration.
609 pub fn div_duration_f64(self, rhs: TimeDelta) -> f64 {
610 let lhs_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos as f64);
611 let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos as f64);
612 lhs_nanos / rhs_nanos
613 }
614
615 /// Divides this signed duration by another signed duration.
616 pub fn div_duration_f32(self, rhs: TimeDelta) -> f32 {
617 let lhs_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos as f32);
618 let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos as f32);
619 lhs_nanos / rhs_nanos
620 }
621}
622
623/// Additional methods not found in the standard library.
624///
625/// In most cases, these APIs exist as a result of the fact that this duration is signed.
626impl TimeDelta {
627 /// Returns the number of whole hours in this duration.
628 ///
629 /// The value returned is negative when the duration is negative.
630 ///
631 /// This does not include any fractional component corresponding to units
632 /// less than an hour.
633 pub const fn as_hours(&self) -> i64 {
634 self.as_secs() / (MINS_PER_HOUR * SECS_PER_MINUTE)
635 }
636
637 /// Returns the number of whole minutes in this duration.
638 ///
639 /// The value returned is negative when the duration is negative.
640 ///
641 /// This does not include any fractional component corresponding to units less than a minute.
642 pub const fn as_mins(&self) -> i64 {
643 self.as_secs() / SECS_PER_MINUTE
644 }
645
646 /// Returns the absolute value of this signed duration.
647 ///
648 /// If this duration is positive, then this returns the original duration unchanged.
649 ///
650 /// # Panics
651 /// This panics when the seconds component of this signed duration is equal to `i64::MIN`.
652 pub const fn abs(self) -> TimeDelta {
653 TimeDelta::new_unchecked(self.secs.abs(), self.nanos.abs())
654 }
655
656 /// Returns the absolute value of this signed duration as a [`Duration`].
657 ///
658 /// This method cannot panic because the absolute value of `TimeDelta::MIN`
659 /// is always representable in a `Duration`.
660 pub const fn abs_duration(self) -> Duration {
661 Duration::new(self.secs.unsigned_abs(), self.nanos.unsigned_abs())
662 }
663
664 /// Returns the negative absolute value of this signed duration.
665 ///
666 /// If this duration is negative, then this returns the original duration unchanged.
667 ///
668 /// # Panics
669 /// This panics when the seconds component of this signed duration is equal to `i64::MIN`.
670 pub const fn neg_abs(self) -> TimeDelta {
671 TimeDelta::new_unchecked(-self.secs.abs(), -self.nanos.abs())
672 }
673
674 /// Returns this duration with its sign flipped.
675 ///
676 /// If this duration is zero, then this returns the duration unchanged.
677 ///
678 /// This returns none if the negation does not exist. This occurs in
679 /// precisely the cases when [`TimeDelta::as_secs`] is equal to `i64::MIN`.
680 pub const fn checked_neg(self) -> Option<TimeDelta> {
681 let Some(secs) = self.secs.checked_neg() else {
682 return None;
683 };
684 Some(TimeDelta::new_unchecked(
685 secs,
686 // Always OK because `-999_999_999 <= self.nanos <= 999_999_999`.
687 -self.nanos,
688 ))
689 }
690
691 /// Returns a number that represents the sign of this duration.
692 ///
693 /// * When [`TimeDelta::is_zero`] is true, this returns `0`.
694 /// * When [`TimeDelta::is_positive`] is true, this returns `1`.
695 /// * When [`TimeDelta::is_negative`] is true, this returns `-1`.
696 ///
697 /// The above cases are mutually exclusive.
698 pub const fn signum(self) -> i8 {
699 if self.is_zero() {
700 0
701 } else if self.is_positive() {
702 1
703 } else {
704 debug_assert!(self.is_negative());
705 -1
706 }
707 }
708
709 /// Returns true when this duration is positive. That is, greater than [`TimeDelta::ZERO`].
710 pub const fn is_positive(&self) -> bool {
711 self.secs.is_positive() || self.nanos.is_positive()
712 }
713
714 /// Returns true when this duration is negative. That is, less than [`TimeDelta::ZERO`].
715 pub const fn is_negative(&self) -> bool {
716 self.secs.is_negative() || self.nanos.is_negative()
717 }
718}
719
720/// Additional APIs involving `SystemInstant`.
721#[cfg(feature = "std")]
722impl TimeDelta {
723 /// Adds a `TimeDelta` to an instant, moving forward or backward in time.
724 pub fn after(&self, _instant: SystemInstant) -> SystemInstant {
725 todo![] // TODO
726 }
727}
728
729// TODO
730// impl Add<TimeDelta> for Instant fn add(self, rhs: TimeDelta) -> Instant Shifts Instant forward or backward.
731// impl Sub<TimeDelta> for Instant fn sub(self, rhs: TimeDelta) -> Instant Moves Instant backward or forward.
732// impl Sub<Instant> for Instant fn sub(self, rhs: Instant) -> TimeDelta Returns a signed TimeDelta.
733
734// TODO IMPROVE
735// impl core::fmt::Display for TimeDelta {
736// #[inline]
737// fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
738// use crate::fmt::StdFmtWrite;
739//
740// if f.alternate() {
741// friendly::DEFAULT_SPAN_PRINTER
742// .print_duration(self, StdFmtWrite(f))
743// .map_err(|_| core::fmt::Error)
744// } else {
745// temporal::DEFAULT_SPAN_PRINTER
746// .print_duration(self, StdFmtWrite(f))
747// .map_err(|_| core::fmt::Error)
748// }
749// }
750// }
751// IMPROVE
752// impl core::fmt::Debug for TimeDelta {
753// #[inline]
754// fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
755// use crate::fmt::StdFmtWrite;
756//
757// friendly::DEFAULT_SPAN_PRINTER
758// .print_duration(self, StdFmtWrite(f))
759// .map_err(|_| core::fmt::Error)
760// }
761// }
762
763// TODO
764impl TryFrom<Duration> for TimeDelta {
765 type Error = &'static str;
766
767 fn try_from(d: Duration) -> Result<TimeDelta, Self::Error> {
768 let secs = i64::try_from(d.as_secs())
769 .map_err(|_| "seconds in unsigned duration {d:?} overflowed i64")?;
770 // Guaranteed to succeed since 0<=nanos<=999,999,999.
771 let nanos = i32::try_from(d.subsec_nanos()).unwrap();
772 Ok(TimeDelta::new_unchecked(secs, nanos))
773 }
774}
775
776/* conversions */
777
778// IMPROVE: Error
779impl TryFrom<TimeDelta> for Duration {
780 type Error = &'static str;
781
782 fn try_from(sd: TimeDelta) -> Result<Duration, Self::Error> {
783 // This isn't needed, but improves error messages.
784 if sd.is_negative() {
785 return Err("cannot convert negative duration `{sd:?}` to \
786 unsigned `std::time::Duration`");
787 }
788 let secs = u64::try_from(sd.as_secs())
789 .map_err(|_| "seconds in signed duration {sd:?} overflowed u64")?;
790 // Guaranteed to succeed because the above only succeeds
791 // when `sd` is non-negative. And when `sd` is non-negative,
792 // we are guaranteed that 0<=nanos<=999,999,999.
793 let nanos = u32::try_from(sd.subsec_nanos()).unwrap();
794 Ok(Duration::new(secs, nanos))
795 }
796}
797
798#[cfg(feature = "dep_jiff")]
799mod impl_jiff {
800 use {super::TimeDelta, ::jiff::SignedDuration};
801 impl From<SignedDuration> for TimeDelta {
802 fn from(from: SignedDuration) -> TimeDelta {
803 TimeDelta::new(from.as_secs(), from.subsec_nanos())
804 }
805 }
806 impl From<TimeDelta> for SignedDuration {
807 fn from(from: TimeDelta) -> SignedDuration {
808 SignedDuration::new(from.secs, from.nanos)
809 }
810 }
811}
812
813// TODO
814// impl core::str::FromStr for TimeDelta {
815// type Err = Error;
816// fn from_str(string: &str) -> Result<TimeDelta, Error> {
817// parse_iso_or_friendly(string.as_bytes())
818// }
819// }
820
821// IMPROVE use num error types
822#[rustfmt::skip]
823mod impl_ops {
824 use crate::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign, TimeDelta};
825 impl Neg for TimeDelta {
826 type Output = TimeDelta;
827 fn neg(self) -> TimeDelta {
828 self.checked_neg().expect("overflow when negating signed duration")
829 }
830 }
831 impl Add for TimeDelta {
832 type Output = TimeDelta;
833 fn add(self, rhs: TimeDelta) -> TimeDelta {
834 self.checked_add(rhs).expect("overflow when adding signed durations")
835 }
836 }
837 impl AddAssign for TimeDelta {
838 fn add_assign(&mut self, rhs: TimeDelta) { *self = *self + rhs; }
839 }
840 impl Sub for TimeDelta {
841 type Output = TimeDelta;
842 fn sub(self, rhs: TimeDelta) -> TimeDelta {
843 self.checked_sub(rhs).expect("overflow when subtracting signed durations")
844 }
845 }
846 impl SubAssign for TimeDelta {
847 fn sub_assign(&mut self, rhs: TimeDelta) { *self = *self - rhs; }
848 }
849 impl Mul<i32> for TimeDelta {
850 type Output = TimeDelta;
851 fn mul(self, rhs: i32) -> TimeDelta {
852 self.checked_mul(rhs).expect("overflow when multiplying signed duration by scalar")
853 }
854 }
855 impl Mul<TimeDelta> for i32 {
856 type Output = TimeDelta;
857 fn mul(self, rhs: TimeDelta) -> TimeDelta { rhs * self }
858 }
859 impl MulAssign<i32> for TimeDelta {
860 fn mul_assign(&mut self, rhs: i32) { *self = *self * rhs; }
861 }
862 impl Div<i32> for TimeDelta {
863 type Output = TimeDelta;
864 fn div(self, rhs: i32) -> TimeDelta {
865 self.checked_div(rhs).expect("overflow when dividing signed duration by scalar")
866 }
867 }
868 impl DivAssign<i32> for TimeDelta {
869 fn div_assign(&mut self, rhs: i32) { *self = *self / rhs; }
870 }
871}