jiff/signed_duration.rs
1use core::time::Duration;
2
3use crate::{
4 civil::{Date, DateTime, Time},
5 error::{err, ErrorContext},
6 fmt::{friendly, temporal},
7 tz::Offset,
8 util::{escape, rangeint::TryRFrom, t},
9 Error, RoundMode, Timestamp, Unit, Zoned,
10};
11
12#[cfg(not(feature = "std"))]
13use crate::util::libm::Float;
14
15/// A signed duration of time represented as a 96-bit integer of nanoseconds.
16///
17/// Each duration is made up of a 64-bit integer of whole seconds and a
18/// 32-bit integer of fractional nanoseconds less than 1 whole second. Unlike
19/// [`std::time::Duration`], this duration is signed. The sign applies
20/// to the entire duration. That is, either _both_ the seconds and the
21/// fractional nanoseconds are negative or _neither_ are. Stated differently,
22/// it is guaranteed that the signs of [`SignedDuration::as_secs`] and
23/// [`SignedDuration::subsec_nanos`] are always the same, or one component is
24/// zero. (For example, `-1 seconds` and `0 nanoseconds`, or `0 seconds` and
25/// `-1 nanoseconds`.)
26///
27/// # Parsing and printing
28///
29/// Like the [`Span`](crate::Span) type, the `SignedDuration` type
30/// provides convenient trait implementations of [`std::str::FromStr`] and
31/// [`std::fmt::Display`]:
32///
33/// ```
34/// use jiff::SignedDuration;
35///
36/// let duration: SignedDuration = "PT2h30m".parse()?;
37/// assert_eq!(duration.to_string(), "PT2H30M");
38///
39/// // Or use the "friendly" format by invoking the alternate:
40/// assert_eq!(format!("{duration:#}"), "2h 30m");
41///
42/// // Parsing automatically supports both the ISO 8601 and "friendly" formats:
43/// let duration: SignedDuration = "2h 30m".parse()?;
44/// assert_eq!(duration, SignedDuration::new(2 * 60 * 60 + 30 * 60, 0));
45/// let duration: SignedDuration = "2 hours, 30 minutes".parse()?;
46/// assert_eq!(duration, SignedDuration::new(2 * 60 * 60 + 30 * 60, 0));
47///
48/// # Ok::<(), Box<dyn std::error::Error>>(())
49/// ```
50///
51/// Unlike the `Span` type, though, only uniform units are supported. This
52/// means that ISO 8601 durations with non-zero units of days or greater cannot
53/// be parsed directly into a `SignedDuration`:
54///
55/// ```
56/// use jiff::SignedDuration;
57///
58/// assert_eq!(
59/// "P1d".parse::<SignedDuration>().unwrap_err().to_string(),
60/// "failed to parse ISO 8601 duration string into `SignedDuration`: \
61/// parsing ISO 8601 duration into SignedDuration requires that the \
62/// duration contain a time component and no components of days or \
63/// greater",
64/// );
65///
66/// # Ok::<(), Box<dyn std::error::Error>>(())
67/// ```
68///
69/// To parse such durations, one should first parse them into a `Span` and
70/// then convert them to a `SignedDuration` by providing a relative date:
71///
72/// ```
73/// use jiff::{civil::date, SignedDuration, Span};
74///
75/// let span: Span = "P1d".parse()?;
76/// let relative = date(2024, 11, 3).in_tz("US/Eastern")?;
77/// let duration = span.to_duration(&relative)?;
78/// // This example also motivates *why* a relative date
79/// // is required. Not all days are the same length!
80/// assert_eq!(duration.to_string(), "PT25H");
81///
82/// # Ok::<(), Box<dyn std::error::Error>>(())
83/// ```
84///
85/// The format supported is a variation (nearly a subset) of the duration
86/// format specified in [ISO 8601] _and_ a Jiff-specific "friendly" format.
87/// Here are more examples:
88///
89/// ```
90/// use jiff::SignedDuration;
91///
92/// let durations = [
93/// // ISO 8601
94/// ("PT2H30M", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
95/// ("PT2.5h", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
96/// ("PT1m", SignedDuration::from_mins(1)),
97/// ("PT1.5m", SignedDuration::from_secs(90)),
98/// ("PT0.0021s", SignedDuration::new(0, 2_100_000)),
99/// ("PT0s", SignedDuration::ZERO),
100/// ("PT0.000000001s", SignedDuration::from_nanos(1)),
101/// // Jiff's "friendly" format
102/// ("2h30m", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
103/// ("2 hrs 30 mins", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
104/// ("2 hours 30 minutes", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
105/// ("2.5h", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
106/// ("1m", SignedDuration::from_mins(1)),
107/// ("1.5m", SignedDuration::from_secs(90)),
108/// ("0.0021s", SignedDuration::new(0, 2_100_000)),
109/// ("0s", SignedDuration::ZERO),
110/// ("0.000000001s", SignedDuration::from_nanos(1)),
111/// ];
112/// for (string, duration) in durations {
113/// let parsed: SignedDuration = string.parse()?;
114/// assert_eq!(duration, parsed, "result of parsing {string:?}");
115/// }
116///
117/// # Ok::<(), Box<dyn std::error::Error>>(())
118/// ```
119///
120/// For more details, see the [`fmt::temporal`](temporal) and
121/// [`fmt::friendly`](friendly) modules.
122///
123/// [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
124///
125/// # API design
126///
127/// A `SignedDuration` is, as much as is possible, a replica of the
128/// `std::time::Duration` API. While there are probably some quirks in the API
129/// of `std::time::Duration` that could have been fixed here, it is probably
130/// more important that it behave "exactly like a `std::time::Duration` but
131/// with a sign." That is, this type mirrors the parallels between signed and
132/// unsigned integer types.
133///
134/// While the goal was to match the `std::time::Duration` API as much as
135/// possible, there are some differences worth highlighting:
136///
137/// * As stated, a `SignedDuration` has a sign. Therefore, it uses `i64` and
138/// `i32` instead of `u64` and `u32` to represent its 96-bit integer.
139/// * Because it's signed, the range of possible values is different. For
140/// example, a `SignedDuration::MAX` has a whole number of seconds equivalent
141/// to `i64::MAX`, which is less than `u64::MAX`.
142/// * There are some additional APIs that don't make sense on an unsigned
143/// duration, like [`SignedDuration::abs`] and [`SignedDuration::checked_neg`].
144/// * A [`SignedDuration::system_until`] routine is provided as a replacement
145/// for [`std::time::SystemTime::duration_since`], but with signed durations.
146/// * Constructors and getters for units of hours and minutes are provided,
147/// where as these routines are unstable in the standard library.
148/// * Unlike the standard library, this type implements the `std::fmt::Display`
149/// and `std::str::FromStr` traits via the ISO 8601 duration format, just
150/// like the [`Span`](crate::Span) type does. Also like `Span`, the ISO
151/// 8601 duration format is used to implement the serde `Serialize` and
152/// `Deserialize` traits when the `serde` crate feature is enabled.
153/// * The `std::fmt::Debug` trait implementation is a bit different. If you
154/// have a problem with it, please file an issue.
155/// * At present, there is no `SignedDuration::abs_diff` since there are some
156/// API design questions. If you want it, please file an issue.
157///
158/// # When should I use `SignedDuration` versus [`Span`](crate::Span)?
159///
160/// Jiff's primary duration type is `Span`. The key differences between it and
161/// `SignedDuration` are:
162///
163/// * A `Span` keeps track of each individual unit separately. That is, even
164/// though `1 hour 60 minutes` and `2 hours` are equivalent durations
165/// of time, representing each as a `Span` corresponds to two distinct values
166/// in memory. And serializing them to the ISO 8601 duration format will also
167/// preserve the units, for example, `PT1h60m` and `PT2h`.
168/// * A `Span` supports non-uniform units like days, weeks, months and years.
169/// Since not all days, weeks, months and years have the same length, they
170/// cannot be represented by a `SignedDuration`. In some cases, it may be
171/// appropriate, for example, to assume that all days are 24 hours long. But
172/// since Jiff sometimes assumes all days are 24 hours (for civil time) and
173/// sometimes doesn't (like for `Zoned` when respecting time zones), it would
174/// be inappropriate to bake one of those assumptions into a `SignedDuration`.
175/// * A `SignedDuration` is a much smaller type than a `Span`. Specifically,
176/// it's a 96-bit integer. In contrast, a `Span` is much larger since it needs
177/// to track each individual unit separately.
178///
179/// Those differences in turn motivate some approximate reasoning for when to
180/// use `Span` and when to use `SignedDuration`:
181///
182/// * If you don't care about keeping track of individual units separately or
183/// don't need the sophisticated rounding options available on a `Span`, it
184/// might be simpler and faster to use a `SignedDuration`.
185/// * If you specifically need performance on arithmetic operations involving
186/// datetimes and durations, even if it's not as convenient or correct, then it
187/// might make sense to use a `SignedDuration`.
188/// * If you need to perform arithmetic using a `std::time::Duration` and
189/// otherwise don't need the functionality of a `Span`, it might make sense
190/// to first convert the `std::time::Duration` to a `SignedDuration`, and then
191/// use one of the corresponding operations defined for `SignedDuration` on
192/// the datetime types. (They all support it.)
193///
194/// In general, a `Span` provides more functionality and is overall more
195/// flexible. A `Span` can also deserialize all forms of ISO 8601 durations
196/// (as long as they're within Jiff's limits), including durations with units
197/// of years, months, weeks and days. A `SignedDuration`, by contrast, only
198/// supports units up to and including hours.
199///
200/// # Integration with datetime types
201///
202/// All datetime types that support arithmetic using [`Span`](crate::Span) also
203/// support arithmetic using `SignedDuration` (and [`std::time::Duration`]).
204/// For example, here's how to add an absolute duration to a [`Timestamp`]:
205///
206/// ```
207/// use jiff::{SignedDuration, Timestamp};
208///
209/// let ts1 = Timestamp::from_second(1_123_456_789)?;
210/// assert_eq!(ts1.to_string(), "2005-08-07T23:19:49Z");
211///
212/// let duration = SignedDuration::new(59, 999_999_999);
213/// // Timestamp::checked_add is polymorphic! It can accept a
214/// // span or a duration.
215/// let ts2 = ts1.checked_add(duration)?;
216/// assert_eq!(ts2.to_string(), "2005-08-07T23:20:48.999999999Z");
217///
218/// # Ok::<(), Box<dyn std::error::Error>>(())
219/// ```
220///
221/// The same API pattern works with [`Zoned`], [`DateTime`], [`Date`] and
222/// [`Time`].
223///
224/// # Interaction with daylight saving time and time zone transitions
225///
226/// A `SignedDuration` always corresponds to a specific number of nanoseconds.
227/// Since a [`Zoned`] is always a precise instant in time, adding a `SignedDuration`
228/// to a `Zoned` always behaves by adding the nanoseconds from the duration to
229/// the timestamp inside of `Zoned`. Consider `2024-03-10` in `US/Eastern`.
230/// At `02:00:00`, daylight saving time came into effect, switching the UTC
231/// offset for the region from `-05` to `-04`. This has the effect of skipping
232/// an hour on the clocks:
233///
234/// ```
235/// use jiff::{civil::date, SignedDuration};
236///
237/// let zdt = date(2024, 3, 10).at(1, 59, 0, 0).in_tz("US/Eastern")?;
238/// assert_eq!(
239/// zdt.checked_add(SignedDuration::from_hours(1))?,
240/// // Time on the clock skipped an hour, but in this time
241/// // zone, 03:59 is actually precisely 1 hour later than
242/// // 01:59.
243/// date(2024, 3, 10).at(3, 59, 0, 0).in_tz("US/Eastern")?,
244/// );
245/// // The same would apply if you used a `Span`:
246/// assert_eq!(
247/// zdt.checked_add(jiff::Span::new().hours(1))?,
248/// // Time on the clock skipped an hour, but in this time
249/// // zone, 03:59 is actually precisely 1 hour later than
250/// // 01:59.
251/// date(2024, 3, 10).at(3, 59, 0, 0).in_tz("US/Eastern")?,
252/// );
253///
254/// # Ok::<(), Box<dyn std::error::Error>>(())
255/// ```
256///
257/// Where time zones might have a more interesting effect is in the definition
258/// of the "day" itself. If, for example, you encode the notion that a day is
259/// always 24 hours into your arithmetic, you might get unexpected results.
260/// For example, let's say you want to find the datetime precisely one week
261/// after `2024-03-08T17:00` in the `US/Eastern` time zone. You might be
262/// tempted to just ask for the time that is `7 * 24` hours later:
263///
264/// ```
265/// use jiff::{civil::date, SignedDuration};
266///
267/// let zdt = date(2024, 3, 8).at(17, 0, 0, 0).in_tz("US/Eastern")?;
268/// assert_eq!(
269/// zdt.checked_add(SignedDuration::from_hours(7 * 24))?,
270/// date(2024, 3, 15).at(18, 0, 0, 0).in_tz("US/Eastern")?,
271/// );
272///
273/// # Ok::<(), Box<dyn std::error::Error>>(())
274/// ```
275///
276/// Notice that you get `18:00` and not `17:00`! That's because, as shown
277/// in the previous example, `2024-03-10` was only 23 hours long. That in turn
278/// implies that the week starting from `2024-03-08` is only `7 * 24 - 1` hours
279/// long. This can be tricky to get correct with absolute durations like
280/// `SignedDuration`, but a `Span` will handle this for you automatically:
281///
282/// ```
283/// use jiff::{civil::date, ToSpan};
284///
285/// let zdt = date(2024, 3, 8).at(17, 0, 0, 0).in_tz("US/Eastern")?;
286/// assert_eq!(
287/// zdt.checked_add(1.week())?,
288/// // The expected time!
289/// date(2024, 3, 15).at(17, 0, 0, 0).in_tz("US/Eastern")?,
290/// );
291///
292/// # Ok::<(), Box<dyn std::error::Error>>(())
293/// ```
294///
295/// A `Span` achieves this by keeping track of individual units. Unlike a
296/// `SignedDuration`, it is not just a simple count of nanoseconds. It is a
297/// "bag" of individual units, and the arithmetic operations defined on a
298/// `Span` for `Zoned` know how to interpret "day" in a particular time zone
299/// at a particular instant in time.
300///
301/// With that said, the above does not mean that using a `SignedDuration` is
302/// always wrong. For example, if you're dealing with units of hours or lower,
303/// then all such units are uniform and so you'll always get the same results
304/// as with a `Span`. And using a `SignedDuration` can sometimes be simpler
305/// or faster.
306#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
307pub struct SignedDuration {
308 secs: i64,
309 nanos: i32,
310}
311
312const NANOS_PER_SEC: i32 = 1_000_000_000;
313const NANOS_PER_MILLI: i32 = 1_000_000;
314const NANOS_PER_MICRO: i32 = 1_000;
315const MILLIS_PER_SEC: i64 = 1_000;
316const MICROS_PER_SEC: i64 = 1_000_000;
317const SECS_PER_MINUTE: i64 = 60;
318const MINS_PER_HOUR: i64 = 60;
319
320impl SignedDuration {
321 /// A duration of zero time.
322 ///
323 /// # Example
324 ///
325 /// ```
326 /// use jiff::SignedDuration;
327 ///
328 /// let duration = SignedDuration::ZERO;
329 /// assert!(duration.is_zero());
330 /// assert_eq!(duration.as_secs(), 0);
331 /// assert_eq!(duration.subsec_nanos(), 0);
332 /// ```
333 pub const ZERO: SignedDuration = SignedDuration { secs: 0, nanos: 0 };
334
335 /// The minimum possible duration. Or the "most negative" duration.
336 ///
337 /// # Example
338 ///
339 /// ```
340 /// use jiff::SignedDuration;
341 ///
342 /// let duration = SignedDuration::MIN;
343 /// assert_eq!(duration.as_secs(), i64::MIN);
344 /// assert_eq!(duration.subsec_nanos(), -999_999_999);
345 /// ```
346 pub const MIN: SignedDuration =
347 SignedDuration { secs: i64::MIN, nanos: -(NANOS_PER_SEC - 1) };
348
349 /// The maximum possible duration.
350 ///
351 /// # Example
352 ///
353 /// ```
354 /// use jiff::SignedDuration;
355 ///
356 /// let duration = SignedDuration::MAX;
357 /// assert_eq!(duration.as_secs(), i64::MAX);
358 /// assert_eq!(duration.subsec_nanos(), 999_999_999);
359 /// ```
360 pub const MAX: SignedDuration =
361 SignedDuration { secs: i64::MAX, nanos: NANOS_PER_SEC - 1 };
362
363 /// Creates a new `SignedDuration` from the given number of whole seconds
364 /// and additional nanoseconds.
365 ///
366 /// If the absolute value of the nanoseconds is greater than or equal to
367 /// 1 second, then the excess balances into the number of whole seconds.
368 ///
369 /// # Panics
370 ///
371 /// When the absolute value of the nanoseconds is greater than or equal
372 /// to 1 second and the excess that carries over to the number of whole
373 /// seconds overflows `i64`.
374 ///
375 /// This never panics when `nanos` is less than `1_000_000_000`.
376 ///
377 /// # Example
378 ///
379 /// ```
380 /// use jiff::SignedDuration;
381 ///
382 /// let duration = SignedDuration::new(12, 0);
383 /// assert_eq!(duration.as_secs(), 12);
384 /// assert_eq!(duration.subsec_nanos(), 0);
385 ///
386 /// let duration = SignedDuration::new(12, -1);
387 /// assert_eq!(duration.as_secs(), 11);
388 /// assert_eq!(duration.subsec_nanos(), 999_999_999);
389 ///
390 /// let duration = SignedDuration::new(12, 1_000_000_000);
391 /// assert_eq!(duration.as_secs(), 13);
392 /// assert_eq!(duration.subsec_nanos(), 0);
393 /// ```
394 #[inline]
395 pub const fn new(mut secs: i64, mut nanos: i32) -> SignedDuration {
396 // When |nanos| exceeds 1 second, we balance the excess up to seconds.
397 if !(-NANOS_PER_SEC < nanos && nanos < NANOS_PER_SEC) {
398 // Never wraps or panics because NANOS_PER_SEC!={0,-1}.
399 let addsecs = nanos / NANOS_PER_SEC;
400 secs = match secs.checked_add(addsecs as i64) {
401 Some(secs) => secs,
402 None => panic!(
403 "nanoseconds overflowed seconds in SignedDuration::new"
404 ),
405 };
406 // Never wraps or panics because NANOS_PER_SEC!={0,-1}.
407 nanos = nanos % NANOS_PER_SEC;
408 }
409 // At this point, we're done if either unit is zero or if they have the
410 // same sign.
411 if nanos == 0 || secs == 0 || secs.signum() == (nanos.signum() as i64)
412 {
413 return SignedDuration::new_unchecked(secs, nanos);
414 }
415 // Otherwise, the only work we have to do is to balance negative nanos
416 // into positive seconds, or positive nanos into negative seconds.
417 if secs < 0 {
418 debug_assert!(nanos > 0);
419 // Never wraps because adding +1 to a negative i64 never overflows.
420 //
421 // MSRV(1.79): Consider using `unchecked_add` here.
422 secs += 1;
423 // Never wraps because subtracting +1_000_000_000 from a positive
424 // i32 never overflows.
425 //
426 // MSRV(1.79): Consider using `unchecked_sub` here.
427 nanos -= NANOS_PER_SEC;
428 } else {
429 debug_assert!(secs > 0);
430 debug_assert!(nanos < 0);
431 // Never wraps because subtracting +1 from a positive i64 never
432 // overflows.
433 //
434 // MSRV(1.79): Consider using `unchecked_add` here.
435 secs -= 1;
436 // Never wraps because adding +1_000_000_000 to a negative i32
437 // never overflows.
438 //
439 // MSRV(1.79): Consider using `unchecked_add` here.
440 nanos += NANOS_PER_SEC;
441 }
442 SignedDuration::new_unchecked(secs, nanos)
443 }
444
445 /// Creates a new signed duration without handling nanosecond overflow.
446 ///
447 /// This might produce tighter code in some cases.
448 ///
449 /// # Panics
450 ///
451 /// When `|nanos|` is greater than or equal to 1 second.
452 #[inline]
453 pub(crate) const fn new_without_nano_overflow(
454 secs: i64,
455 nanos: i32,
456 ) -> SignedDuration {
457 assert!(nanos <= 999_999_999);
458 assert!(nanos >= -999_999_999);
459 SignedDuration::new_unchecked(secs, nanos)
460 }
461
462 /// Creates a new signed duration without handling nanosecond overflow.
463 ///
464 /// This might produce tighter code in some cases.
465 ///
466 /// # Panics
467 ///
468 /// In debug mode only, when `|nanos|` is greater than or equal to 1
469 /// second.
470 ///
471 /// This is not exported so that code outside this module can rely on
472 /// `|nanos|` being less than a second for purposes of memory safety.
473 #[inline]
474 const fn new_unchecked(secs: i64, nanos: i32) -> SignedDuration {
475 debug_assert!(nanos <= 999_999_999);
476 debug_assert!(nanos >= -999_999_999);
477 SignedDuration { secs, nanos }
478 }
479
480 /// Creates a new `SignedDuration` from the given number of whole seconds.
481 ///
482 /// # Example
483 ///
484 /// ```
485 /// use jiff::SignedDuration;
486 ///
487 /// let duration = SignedDuration::from_secs(12);
488 /// assert_eq!(duration.as_secs(), 12);
489 /// assert_eq!(duration.subsec_nanos(), 0);
490 /// ```
491 #[inline]
492 pub const fn from_secs(secs: i64) -> SignedDuration {
493 SignedDuration::new_unchecked(secs, 0)
494 }
495
496 /// Creates a new `SignedDuration` from the given number of whole
497 /// milliseconds.
498 ///
499 /// Note that since this accepts an `i64`, this method cannot be used
500 /// to construct the full range of possible signed duration values. In
501 /// particular, [`SignedDuration::as_millis`] returns an `i128`, and this
502 /// may be a value that would otherwise overflow an `i64`.
503 ///
504 /// # Example
505 ///
506 /// ```
507 /// use jiff::SignedDuration;
508 ///
509 /// let duration = SignedDuration::from_millis(12_456);
510 /// assert_eq!(duration.as_secs(), 12);
511 /// assert_eq!(duration.subsec_nanos(), 456_000_000);
512 ///
513 /// let duration = SignedDuration::from_millis(-12_456);
514 /// assert_eq!(duration.as_secs(), -12);
515 /// assert_eq!(duration.subsec_nanos(), -456_000_000);
516 /// ```
517 #[inline]
518 pub const fn from_millis(millis: i64) -> SignedDuration {
519 // OK because MILLIS_PER_SEC!={-1,0}.
520 let secs = millis / MILLIS_PER_SEC;
521 // OK because MILLIS_PER_SEC!={-1,0} and because
522 // millis % MILLIS_PER_SEC can be at most 999, and 999 * 1_000_000
523 // never overflows i32.
524 let nanos = (millis % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI;
525 SignedDuration::new_unchecked(secs, nanos)
526 }
527
528 /// Creates a new `SignedDuration` from the given number of whole
529 /// microseconds.
530 ///
531 /// Note that since this accepts an `i64`, this method cannot be used
532 /// to construct the full range of possible signed duration values. In
533 /// particular, [`SignedDuration::as_micros`] returns an `i128`, and this
534 /// may be a value that would otherwise overflow an `i64`.
535 ///
536 /// # Example
537 ///
538 /// ```
539 /// use jiff::SignedDuration;
540 ///
541 /// let duration = SignedDuration::from_micros(12_000_456);
542 /// assert_eq!(duration.as_secs(), 12);
543 /// assert_eq!(duration.subsec_nanos(), 456_000);
544 ///
545 /// let duration = SignedDuration::from_micros(-12_000_456);
546 /// assert_eq!(duration.as_secs(), -12);
547 /// assert_eq!(duration.subsec_nanos(), -456_000);
548 /// ```
549 #[inline]
550 pub const fn from_micros(micros: i64) -> SignedDuration {
551 // OK because MICROS_PER_SEC!={-1,0}.
552 let secs = micros / MICROS_PER_SEC;
553 // OK because MICROS_PER_SEC!={-1,0} and because
554 // millis % MICROS_PER_SEC can be at most 999, and 999 * 1_000_000
555 // never overflows i32.
556 let nanos = (micros % MICROS_PER_SEC) as i32 * NANOS_PER_MICRO;
557 SignedDuration::new_unchecked(secs, nanos)
558 }
559
560 /// Creates a new `SignedDuration` from the given number of whole
561 /// nanoseconds.
562 ///
563 /// Note that since this accepts an `i64`, this method cannot be used
564 /// to construct the full range of possible signed duration values. In
565 /// particular, [`SignedDuration::as_nanos`] returns an `i128`, which may
566 /// be a value that would otherwise overflow an `i64`.
567 ///
568 /// # Example
569 ///
570 /// ```
571 /// use jiff::SignedDuration;
572 ///
573 /// let duration = SignedDuration::from_nanos(12_000_000_456);
574 /// assert_eq!(duration.as_secs(), 12);
575 /// assert_eq!(duration.subsec_nanos(), 456);
576 ///
577 /// let duration = SignedDuration::from_nanos(-12_000_000_456);
578 /// assert_eq!(duration.as_secs(), -12);
579 /// assert_eq!(duration.subsec_nanos(), -456);
580 /// ```
581 #[inline]
582 pub const fn from_nanos(nanos: i64) -> SignedDuration {
583 // OK because NANOS_PER_SEC!={-1,0}.
584 let secs = nanos / (NANOS_PER_SEC as i64);
585 // OK because NANOS_PER_SEC!={-1,0}.
586 let nanos = (nanos % (NANOS_PER_SEC as i64)) as i32;
587 SignedDuration::new_unchecked(secs, nanos)
588 }
589
590 /// Creates a new `SignedDuration` from the given number of hours. Every
591 /// hour is exactly `3,600` seconds.
592 ///
593 /// # Panics
594 ///
595 /// Panics if the number of hours, after being converted to nanoseconds,
596 /// overflows the minimum or maximum `SignedDuration` values.
597 ///
598 /// # Example
599 ///
600 /// ```
601 /// use jiff::SignedDuration;
602 ///
603 /// let duration = SignedDuration::from_hours(24);
604 /// assert_eq!(duration.as_secs(), 86_400);
605 /// assert_eq!(duration.subsec_nanos(), 0);
606 ///
607 /// let duration = SignedDuration::from_hours(-24);
608 /// assert_eq!(duration.as_secs(), -86_400);
609 /// assert_eq!(duration.subsec_nanos(), 0);
610 /// ```
611 #[inline]
612 pub const fn from_hours(hours: i64) -> SignedDuration {
613 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
614 const MIN_HOUR: i64 = i64::MIN / (SECS_PER_MINUTE * MINS_PER_HOUR);
615 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
616 const MAX_HOUR: i64 = i64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR);
617 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
618 if hours < MIN_HOUR {
619 panic!("hours overflowed minimum number of SignedDuration seconds")
620 }
621 // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
622 if hours > MAX_HOUR {
623 panic!("hours overflowed maximum number of SignedDuration seconds")
624 }
625 SignedDuration::from_secs(hours * MINS_PER_HOUR * SECS_PER_MINUTE)
626 }
627
628 /// Creates a new `SignedDuration` from the given number of minutes. Every
629 /// minute is exactly `60` seconds.
630 ///
631 /// # Panics
632 ///
633 /// Panics if the number of minutes, after being converted to nanoseconds,
634 /// overflows the minimum or maximum `SignedDuration` values.
635 ///
636 /// # Example
637 ///
638 /// ```
639 /// use jiff::SignedDuration;
640 ///
641 /// let duration = SignedDuration::from_mins(1_440);
642 /// assert_eq!(duration.as_secs(), 86_400);
643 /// assert_eq!(duration.subsec_nanos(), 0);
644 ///
645 /// let duration = SignedDuration::from_mins(-1_440);
646 /// assert_eq!(duration.as_secs(), -86_400);
647 /// assert_eq!(duration.subsec_nanos(), 0);
648 /// ```
649 #[inline]
650 pub const fn from_mins(minutes: i64) -> SignedDuration {
651 // OK because SECS_PER_MINUTE!={-1,0}.
652 const MIN_MINUTE: i64 = i64::MIN / SECS_PER_MINUTE;
653 // OK because SECS_PER_MINUTE!={-1,0}.
654 const MAX_MINUTE: i64 = i64::MAX / SECS_PER_MINUTE;
655 // OK because SECS_PER_MINUTE!={-1,0}.
656 if minutes < MIN_MINUTE {
657 panic!(
658 "minutes overflowed minimum number of SignedDuration seconds"
659 )
660 }
661 // OK because SECS_PER_MINUTE!={-1,0}.
662 if minutes > MAX_MINUTE {
663 panic!(
664 "minutes overflowed maximum number of SignedDuration seconds"
665 )
666 }
667 SignedDuration::from_secs(minutes * SECS_PER_MINUTE)
668 }
669
670 /// Converts the given timestamp into a signed duration.
671 ///
672 /// This isn't exported because it's not clear that it makes semantic
673 /// sense, since it somewhat encodes the assumption that the "desired"
674 /// duration is relative to the Unix epoch. Which is... probably fine?
675 /// But I'm not sure.
676 ///
677 /// But the point of this is to make the conversion a little cheaper.
678 /// Namely, since a `Timestamp` internally uses same representation as a
679 /// `SignedDuration` with the same guarantees (except with smaller limits),
680 /// we can avoid a fair bit of case analysis done in `SignedDuration::new`.
681 pub(crate) fn from_timestamp(timestamp: Timestamp) -> SignedDuration {
682 SignedDuration::new_unchecked(
683 timestamp.as_second(),
684 timestamp.subsec_nanosecond(),
685 )
686 }
687
688 /// Returns true if this duration spans no time.
689 ///
690 /// # Example
691 ///
692 /// ```
693 /// use jiff::SignedDuration;
694 ///
695 /// assert!(SignedDuration::ZERO.is_zero());
696 /// assert!(!SignedDuration::MIN.is_zero());
697 /// assert!(!SignedDuration::MAX.is_zero());
698 /// ```
699 #[inline]
700 pub const fn is_zero(&self) -> bool {
701 self.secs == 0 && self.nanos == 0
702 }
703
704 /// Returns the number of whole seconds in this duration.
705 ///
706 /// The value returned is negative when the duration is negative.
707 ///
708 /// This does not include any fractional component corresponding to units
709 /// less than a second. To access those, use one of the `subsec` methods
710 /// such as [`SignedDuration::subsec_nanos`].
711 ///
712 /// # Example
713 ///
714 /// ```
715 /// use jiff::SignedDuration;
716 ///
717 /// let duration = SignedDuration::new(12, 999_999_999);
718 /// assert_eq!(duration.as_secs(), 12);
719 ///
720 /// let duration = SignedDuration::new(-12, -999_999_999);
721 /// assert_eq!(duration.as_secs(), -12);
722 /// ```
723 #[inline]
724 pub const fn as_secs(&self) -> i64 {
725 self.secs
726 }
727
728 /// Returns the fractional part of this duration in whole milliseconds.
729 ///
730 /// The value returned is negative when the duration is negative. It is
731 /// guaranteed that the range of the value returned is in the inclusive
732 /// range `-999..=999`.
733 ///
734 /// To get the length of the total duration represented in milliseconds,
735 /// use [`SignedDuration::as_millis`].
736 ///
737 /// # Example
738 ///
739 /// ```
740 /// use jiff::SignedDuration;
741 ///
742 /// let duration = SignedDuration::new(12, 123_456_789);
743 /// assert_eq!(duration.subsec_millis(), 123);
744 ///
745 /// let duration = SignedDuration::new(-12, -123_456_789);
746 /// assert_eq!(duration.subsec_millis(), -123);
747 /// ```
748 #[inline]
749 pub const fn subsec_millis(&self) -> i32 {
750 // OK because NANOS_PER_MILLI!={-1,0}.
751 self.nanos / NANOS_PER_MILLI
752 }
753
754 /// Returns the fractional part of this duration in whole microseconds.
755 ///
756 /// The value returned is negative when the duration is negative. It is
757 /// guaranteed that the range of the value returned is in the inclusive
758 /// range `-999_999..=999_999`.
759 ///
760 /// To get the length of the total duration represented in microseconds,
761 /// use [`SignedDuration::as_micros`].
762 ///
763 /// # Example
764 ///
765 /// ```
766 /// use jiff::SignedDuration;
767 ///
768 /// let duration = SignedDuration::new(12, 123_456_789);
769 /// assert_eq!(duration.subsec_micros(), 123_456);
770 ///
771 /// let duration = SignedDuration::new(-12, -123_456_789);
772 /// assert_eq!(duration.subsec_micros(), -123_456);
773 /// ```
774 #[inline]
775 pub const fn subsec_micros(&self) -> i32 {
776 // OK because NANOS_PER_MICRO!={-1,0}.
777 self.nanos / NANOS_PER_MICRO
778 }
779
780 /// Returns the fractional part of this duration in whole nanoseconds.
781 ///
782 /// The value returned is negative when the duration is negative. It is
783 /// guaranteed that the range of the value returned is in the inclusive
784 /// range `-999_999_999..=999_999_999`.
785 ///
786 /// To get the length of the total duration represented in nanoseconds,
787 /// use [`SignedDuration::as_nanos`].
788 ///
789 /// # Example
790 ///
791 /// ```
792 /// use jiff::SignedDuration;
793 ///
794 /// let duration = SignedDuration::new(12, 123_456_789);
795 /// assert_eq!(duration.subsec_nanos(), 123_456_789);
796 ///
797 /// let duration = SignedDuration::new(-12, -123_456_789);
798 /// assert_eq!(duration.subsec_nanos(), -123_456_789);
799 /// ```
800 #[inline]
801 pub const fn subsec_nanos(&self) -> i32 {
802 self.nanos
803 }
804
805 /// Returns the total duration in units of whole milliseconds.
806 ///
807 /// The value returned is negative when the duration is negative.
808 ///
809 /// To get only the fractional component of this duration in units of
810 /// whole milliseconds, use [`SignedDuration::subsec_millis`].
811 ///
812 /// # Example
813 ///
814 /// ```
815 /// use jiff::SignedDuration;
816 ///
817 /// let duration = SignedDuration::new(12, 123_456_789);
818 /// assert_eq!(duration.as_millis(), 12_123);
819 ///
820 /// let duration = SignedDuration::new(-12, -123_456_789);
821 /// assert_eq!(duration.as_millis(), -12_123);
822 /// ```
823 #[inline]
824 pub const fn as_millis(&self) -> i128 {
825 // OK because 1_000 times any i64 will never overflow i128.
826 let millis = (self.secs as i128) * (MILLIS_PER_SEC as i128);
827 // OK because NANOS_PER_MILLI!={-1,0}.
828 let subsec_millis = (self.nanos / NANOS_PER_MILLI) as i128;
829 // OK because subsec_millis maxes out at 999, and adding that to
830 // i64::MAX*1_000 will never overflow a i128.
831 millis + subsec_millis
832 }
833
834 /// Returns the total duration in units of whole microseconds.
835 ///
836 /// The value returned is negative when the duration is negative.
837 ///
838 /// To get only the fractional component of this duration in units of
839 /// whole microseconds, use [`SignedDuration::subsec_micros`].
840 ///
841 /// # Example
842 ///
843 /// ```
844 /// use jiff::SignedDuration;
845 ///
846 /// let duration = SignedDuration::new(12, 123_456_789);
847 /// assert_eq!(duration.as_micros(), 12_123_456);
848 ///
849 /// let duration = SignedDuration::new(-12, -123_456_789);
850 /// assert_eq!(duration.as_micros(), -12_123_456);
851 /// ```
852 #[inline]
853 pub const fn as_micros(&self) -> i128 {
854 // OK because 1_000_000 times any i64 will never overflow i128.
855 let micros = (self.secs as i128) * (MICROS_PER_SEC as i128);
856 // OK because NANOS_PER_MICRO!={-1,0}.
857 let subsec_micros = (self.nanos / NANOS_PER_MICRO) as i128;
858 // OK because subsec_micros maxes out at 999_999, and adding that to
859 // i64::MAX*1_000_000 will never overflow a i128.
860 micros + subsec_micros
861 }
862
863 /// Returns the total duration in units of whole nanoseconds.
864 ///
865 /// The value returned is negative when the duration is negative.
866 ///
867 /// To get only the fractional component of this duration in units of
868 /// whole nanoseconds, use [`SignedDuration::subsec_nanos`].
869 ///
870 /// # Example
871 ///
872 /// ```
873 /// use jiff::SignedDuration;
874 ///
875 /// let duration = SignedDuration::new(12, 123_456_789);
876 /// assert_eq!(duration.as_nanos(), 12_123_456_789);
877 ///
878 /// let duration = SignedDuration::new(-12, -123_456_789);
879 /// assert_eq!(duration.as_nanos(), -12_123_456_789);
880 /// ```
881 #[inline]
882 pub const fn as_nanos(&self) -> i128 {
883 // OK because 1_000_000_000 times any i64 will never overflow i128.
884 let nanos = (self.secs as i128) * (NANOS_PER_SEC as i128);
885 // OK because subsec_nanos maxes out at 999_999_999, and adding that to
886 // i64::MAX*1_000_000_000 will never overflow a i128.
887 nanos + (self.nanos as i128)
888 }
889
890 // NOTE: We don't provide `abs_diff` here because we can't represent the
891 // difference between all possible durations. For example,
892 // `abs_diff(SignedDuration::MAX, SignedDuration::MIN)`. It therefore seems
893 // like we should actually return a `std::time::Duration` here, but I'm
894 // trying to be conservative when divering from std.
895
896 /// Add two signed durations together. If overflow occurs, then `None` is
897 /// returned.
898 ///
899 /// # Example
900 ///
901 /// ```
902 /// use jiff::SignedDuration;
903 ///
904 /// let duration1 = SignedDuration::new(12, 500_000_000);
905 /// let duration2 = SignedDuration::new(0, 500_000_000);
906 /// assert_eq!(
907 /// duration1.checked_add(duration2),
908 /// Some(SignedDuration::new(13, 0)),
909 /// );
910 ///
911 /// let duration1 = SignedDuration::MAX;
912 /// let duration2 = SignedDuration::new(0, 1);
913 /// assert_eq!(duration1.checked_add(duration2), None);
914 /// ```
915 #[inline]
916 pub const fn checked_add(
917 self,
918 rhs: SignedDuration,
919 ) -> Option<SignedDuration> {
920 let Some(mut secs) = self.secs.checked_add(rhs.secs) else {
921 return None;
922 };
923 // OK because `-999_999_999 <= nanos <= 999_999_999`, and so adding
924 // them together will never overflow an i32.
925 let mut nanos = self.nanos + rhs.nanos;
926 // The below is effectively SignedDuration::new, but with checked
927 // arithmetic. My suspicion is that there is probably a better way
928 // to do this. The main complexity here is that 1) `|nanos|` might
929 // now exceed 1 second and 2) the signs of `secs` and `nanos` might
930 // not be the same. The other difference from SignedDuration::new is
931 // that we know that `-1_999_999_998 <= nanos <= 1_999_999_998` since
932 // `|SignedDuration::nanos|` is guaranteed to be less than 1 second. So
933 // we can skip the div and modulus operations.
934
935 // When |nanos| exceeds 1 second, we balance the excess up to seconds.
936 if nanos != 0 {
937 if nanos >= NANOS_PER_SEC {
938 nanos -= NANOS_PER_SEC;
939 secs = match secs.checked_add(1) {
940 None => return None,
941 Some(secs) => secs,
942 };
943 } else if nanos <= -NANOS_PER_SEC {
944 nanos += NANOS_PER_SEC;
945 secs = match secs.checked_sub(1) {
946 None => return None,
947 Some(secs) => secs,
948 };
949 }
950 if secs != 0
951 && nanos != 0
952 && secs.signum() != (nanos.signum() as i64)
953 {
954 if secs < 0 {
955 debug_assert!(nanos > 0);
956 // OK because secs<0.
957 secs += 1;
958 // OK because nanos>0.
959 nanos -= NANOS_PER_SEC;
960 } else {
961 debug_assert!(secs > 0);
962 debug_assert!(nanos < 0);
963 // OK because secs>0.
964 secs -= 1;
965 // OK because nanos<0.
966 nanos += NANOS_PER_SEC;
967 }
968 }
969 }
970 Some(SignedDuration::new_unchecked(secs, nanos))
971 }
972
973 /// Add two signed durations together. If overflow occurs, then arithmetic
974 /// saturates.
975 ///
976 /// # Example
977 ///
978 /// ```
979 /// use jiff::SignedDuration;
980 ///
981 /// let duration1 = SignedDuration::MAX;
982 /// let duration2 = SignedDuration::new(0, 1);
983 /// assert_eq!(duration1.saturating_add(duration2), SignedDuration::MAX);
984 ///
985 /// let duration1 = SignedDuration::MIN;
986 /// let duration2 = SignedDuration::new(0, -1);
987 /// assert_eq!(duration1.saturating_add(duration2), SignedDuration::MIN);
988 /// ```
989 #[inline]
990 pub const fn saturating_add(self, rhs: SignedDuration) -> SignedDuration {
991 let Some(sum) = self.checked_add(rhs) else {
992 return if rhs.is_negative() {
993 SignedDuration::MIN
994 } else {
995 SignedDuration::MAX
996 };
997 };
998 sum
999 }
1000
1001 /// Subtract one signed duration from another. If overflow occurs, then
1002 /// `None` is returned.
1003 ///
1004 /// # Example
1005 ///
1006 /// ```
1007 /// use jiff::SignedDuration;
1008 ///
1009 /// let duration1 = SignedDuration::new(12, 500_000_000);
1010 /// let duration2 = SignedDuration::new(0, 500_000_000);
1011 /// assert_eq!(
1012 /// duration1.checked_sub(duration2),
1013 /// Some(SignedDuration::new(12, 0)),
1014 /// );
1015 ///
1016 /// let duration1 = SignedDuration::MIN;
1017 /// let duration2 = SignedDuration::new(0, 1);
1018 /// assert_eq!(duration1.checked_sub(duration2), None);
1019 /// ```
1020 #[inline]
1021 pub const fn checked_sub(
1022 self,
1023 rhs: SignedDuration,
1024 ) -> Option<SignedDuration> {
1025 let Some(rhs) = rhs.checked_neg() else { return None };
1026 self.checked_add(rhs)
1027 }
1028
1029 /// Add two signed durations together. If overflow occurs, then arithmetic
1030 /// saturates.
1031 ///
1032 /// # Example
1033 ///
1034 /// ```
1035 /// use jiff::SignedDuration;
1036 ///
1037 /// let duration1 = SignedDuration::MAX;
1038 /// let duration2 = SignedDuration::new(0, -1);
1039 /// assert_eq!(duration1.saturating_sub(duration2), SignedDuration::MAX);
1040 ///
1041 /// let duration1 = SignedDuration::MIN;
1042 /// let duration2 = SignedDuration::new(0, 1);
1043 /// assert_eq!(duration1.saturating_sub(duration2), SignedDuration::MIN);
1044 /// ```
1045 #[inline]
1046 pub const fn saturating_sub(self, rhs: SignedDuration) -> SignedDuration {
1047 let Some(diff) = self.checked_sub(rhs) else {
1048 return if rhs.is_positive() {
1049 SignedDuration::MIN
1050 } else {
1051 SignedDuration::MAX
1052 };
1053 };
1054 diff
1055 }
1056
1057 /// Multiply this signed duration by an integer. If the multiplication
1058 /// overflows, then `None` is returned.
1059 ///
1060 /// # Example
1061 ///
1062 /// ```
1063 /// use jiff::SignedDuration;
1064 ///
1065 /// let duration = SignedDuration::new(12, 500_000_000);
1066 /// assert_eq!(
1067 /// duration.checked_mul(2),
1068 /// Some(SignedDuration::new(25, 0)),
1069 /// );
1070 /// ```
1071 #[inline]
1072 pub const fn checked_mul(self, rhs: i32) -> Option<SignedDuration> {
1073 let rhs = rhs as i64;
1074 // Multiplying any two i32 values never overflows an i64.
1075 let nanos = (self.nanos as i64) * rhs;
1076 // OK since NANOS_PER_SEC!={-1,0}.
1077 let addsecs = nanos / (NANOS_PER_SEC as i64);
1078 // OK since NANOS_PER_SEC!={-1,0}.
1079 let nanos = (nanos % (NANOS_PER_SEC as i64)) as i32;
1080 let Some(secs) = self.secs.checked_mul(rhs) else { return None };
1081 let Some(secs) = secs.checked_add(addsecs) else { return None };
1082 Some(SignedDuration::new_unchecked(secs, nanos))
1083 }
1084
1085 /// Multiply this signed duration by an integer. If the multiplication
1086 /// overflows, then the result saturates to either the minimum or maximum
1087 /// duration depending on the sign of the product.
1088 ///
1089 /// # Example
1090 ///
1091 /// ```
1092 /// use jiff::SignedDuration;
1093 ///
1094 /// let duration = SignedDuration::new(i64::MAX, 0);
1095 /// assert_eq!(duration.saturating_mul(2), SignedDuration::MAX);
1096 /// assert_eq!(duration.saturating_mul(-2), SignedDuration::MIN);
1097 ///
1098 /// let duration = SignedDuration::new(i64::MIN, 0);
1099 /// assert_eq!(duration.saturating_mul(2), SignedDuration::MIN);
1100 /// assert_eq!(duration.saturating_mul(-2), SignedDuration::MAX);
1101 /// ```
1102 #[inline]
1103 pub const fn saturating_mul(self, rhs: i32) -> SignedDuration {
1104 let Some(product) = self.checked_mul(rhs) else {
1105 let sign = (self.signum() as i64) * (rhs as i64).signum();
1106 return if sign.is_negative() {
1107 SignedDuration::MIN
1108 } else {
1109 SignedDuration::MAX
1110 };
1111 };
1112 product
1113 }
1114
1115 /// Divide this duration by an integer. If the division overflows, then
1116 /// `None` is returned.
1117 ///
1118 /// # Example
1119 ///
1120 /// ```
1121 /// use jiff::SignedDuration;
1122 ///
1123 /// let duration = SignedDuration::new(12, 500_000_000);
1124 /// assert_eq!(
1125 /// duration.checked_div(2),
1126 /// Some(SignedDuration::new(6, 250_000_000)),
1127 /// );
1128 /// assert_eq!(
1129 /// duration.checked_div(-2),
1130 /// Some(SignedDuration::new(-6, -250_000_000)),
1131 /// );
1132 ///
1133 /// let duration = SignedDuration::new(-12, -500_000_000);
1134 /// assert_eq!(
1135 /// duration.checked_div(2),
1136 /// Some(SignedDuration::new(-6, -250_000_000)),
1137 /// );
1138 /// assert_eq!(
1139 /// duration.checked_div(-2),
1140 /// Some(SignedDuration::new(6, 250_000_000)),
1141 /// );
1142 /// ```
1143 #[inline]
1144 pub const fn checked_div(self, rhs: i32) -> Option<SignedDuration> {
1145 if rhs == 0 || (self.secs == i64::MIN && rhs == -1) {
1146 return None;
1147 }
1148 // OK since rhs!={-1,0}.
1149 let secs = self.secs / (rhs as i64);
1150 // OK since rhs!={-1,0}.
1151 let addsecs = self.secs % (rhs as i64);
1152 // OK since rhs!=0 and self.nanos>i32::MIN.
1153 let mut nanos = self.nanos / rhs;
1154 // OK since rhs!=0 and self.nanos>i32::MIN.
1155 let addnanos = self.nanos % rhs;
1156 let leftover_nanos =
1157 (addsecs * (NANOS_PER_SEC as i64)) + (addnanos as i64);
1158 nanos += (leftover_nanos / (rhs as i64)) as i32;
1159 debug_assert!(nanos < NANOS_PER_SEC);
1160 Some(SignedDuration::new_unchecked(secs, nanos))
1161 }
1162
1163 /// Returns the number of seconds, with a possible fractional nanosecond
1164 /// component, represented by this signed duration as a 64-bit float.
1165 ///
1166 /// # Example
1167 ///
1168 /// ```
1169 /// use jiff::SignedDuration;
1170 ///
1171 /// let duration = SignedDuration::new(12, 123_456_789);
1172 /// assert_eq!(duration.as_secs_f64(), 12.123456789);
1173 ///
1174 /// let duration = SignedDuration::new(-12, -123_456_789);
1175 /// assert_eq!(duration.as_secs_f64(), -12.123456789);
1176 /// ```
1177 #[inline]
1178 pub fn as_secs_f64(&self) -> f64 {
1179 (self.secs as f64) + ((self.nanos as f64) / (NANOS_PER_SEC as f64))
1180 }
1181
1182 /// Returns the number of seconds, with a possible fractional nanosecond
1183 /// component, represented by this signed duration as a 32-bit float.
1184 ///
1185 /// # Example
1186 ///
1187 /// ```
1188 /// use jiff::SignedDuration;
1189 ///
1190 /// let duration = SignedDuration::new(12, 123_456_789);
1191 /// assert_eq!(duration.as_secs_f32(), 12.123456789);
1192 ///
1193 /// let duration = SignedDuration::new(-12, -123_456_789);
1194 /// assert_eq!(duration.as_secs_f32(), -12.123456789);
1195 /// ```
1196 #[inline]
1197 pub fn as_secs_f32(&self) -> f32 {
1198 (self.secs as f32) + ((self.nanos as f32) / (NANOS_PER_SEC as f32))
1199 }
1200
1201 /// Returns the number of milliseconds, with a possible fractional
1202 /// nanosecond component, represented by this signed duration as a 64-bit
1203 /// float.
1204 ///
1205 /// # Example
1206 ///
1207 /// ```
1208 /// use jiff::SignedDuration;
1209 ///
1210 /// let duration = SignedDuration::new(12, 123_456_789);
1211 /// assert_eq!(duration.as_millis_f64(), 12123.456789);
1212 ///
1213 /// let duration = SignedDuration::new(-12, -123_456_789);
1214 /// assert_eq!(duration.as_millis_f64(), -12123.456789);
1215 /// ```
1216 #[inline]
1217 pub fn as_millis_f64(&self) -> f64 {
1218 ((self.secs as f64) * (MILLIS_PER_SEC as f64))
1219 + ((self.nanos as f64) / (NANOS_PER_MILLI as f64))
1220 }
1221
1222 /// Returns the number of milliseconds, with a possible fractional
1223 /// nanosecond component, represented by this signed duration as a 32-bit
1224 /// float.
1225 ///
1226 /// # Example
1227 ///
1228 /// ```
1229 /// use jiff::SignedDuration;
1230 ///
1231 /// let duration = SignedDuration::new(12, 123_456_789);
1232 /// assert_eq!(duration.as_millis_f32(), 12123.456789);
1233 ///
1234 /// let duration = SignedDuration::new(-12, -123_456_789);
1235 /// assert_eq!(duration.as_millis_f32(), -12123.456789);
1236 /// ```
1237 #[inline]
1238 pub fn as_millis_f32(&self) -> f32 {
1239 ((self.secs as f32) * (MILLIS_PER_SEC as f32))
1240 + ((self.nanos as f32) / (NANOS_PER_MILLI as f32))
1241 }
1242
1243 /// Returns a signed duration corresponding to the number of seconds
1244 /// represented as a 64-bit float. The number given may have a fractional
1245 /// nanosecond component.
1246 ///
1247 /// # Panics
1248 ///
1249 /// If the given float overflows the minimum or maximum signed duration
1250 /// values, then this panics.
1251 ///
1252 /// # Example
1253 ///
1254 /// ```
1255 /// use jiff::SignedDuration;
1256 ///
1257 /// let duration = SignedDuration::from_secs_f64(12.123456789);
1258 /// assert_eq!(duration.as_secs(), 12);
1259 /// assert_eq!(duration.subsec_nanos(), 123_456_789);
1260 ///
1261 /// let duration = SignedDuration::from_secs_f64(-12.123456789);
1262 /// assert_eq!(duration.as_secs(), -12);
1263 /// assert_eq!(duration.subsec_nanos(), -123_456_789);
1264 ///
1265 /// # Ok::<(), Box<dyn std::error::Error>>(())
1266 /// ```
1267 #[inline]
1268 pub fn from_secs_f64(secs: f64) -> SignedDuration {
1269 SignedDuration::try_from_secs_f64(secs)
1270 .expect("finite and in-bounds f64")
1271 }
1272
1273 /// Returns a signed duration corresponding to the number of seconds
1274 /// represented as a 32-bit float. The number given may have a fractional
1275 /// nanosecond component.
1276 ///
1277 /// # Panics
1278 ///
1279 /// If the given float overflows the minimum or maximum signed duration
1280 /// values, then this panics.
1281 ///
1282 /// # Example
1283 ///
1284 /// ```
1285 /// use jiff::SignedDuration;
1286 ///
1287 /// let duration = SignedDuration::from_secs_f32(12.123456789);
1288 /// assert_eq!(duration.as_secs(), 12);
1289 /// // loss of precision!
1290 /// assert_eq!(duration.subsec_nanos(), 123_456_952);
1291 ///
1292 /// let duration = SignedDuration::from_secs_f32(-12.123456789);
1293 /// assert_eq!(duration.as_secs(), -12);
1294 /// // loss of precision!
1295 /// assert_eq!(duration.subsec_nanos(), -123_456_952);
1296 ///
1297 /// # Ok::<(), Box<dyn std::error::Error>>(())
1298 /// ```
1299 #[inline]
1300 pub fn from_secs_f32(secs: f32) -> SignedDuration {
1301 SignedDuration::try_from_secs_f32(secs)
1302 .expect("finite and in-bounds f32")
1303 }
1304
1305 /// Returns a signed duration corresponding to the number of seconds
1306 /// represented as a 64-bit float. The number given may have a fractional
1307 /// nanosecond component.
1308 ///
1309 /// If the given float overflows the minimum or maximum signed duration
1310 /// values, then an error is returned.
1311 ///
1312 /// # Example
1313 ///
1314 /// ```
1315 /// use jiff::SignedDuration;
1316 ///
1317 /// let duration = SignedDuration::try_from_secs_f64(12.123456789)?;
1318 /// assert_eq!(duration.as_secs(), 12);
1319 /// assert_eq!(duration.subsec_nanos(), 123_456_789);
1320 ///
1321 /// let duration = SignedDuration::try_from_secs_f64(-12.123456789)?;
1322 /// assert_eq!(duration.as_secs(), -12);
1323 /// assert_eq!(duration.subsec_nanos(), -123_456_789);
1324 ///
1325 /// assert!(SignedDuration::try_from_secs_f64(f64::NAN).is_err());
1326 /// assert!(SignedDuration::try_from_secs_f64(f64::INFINITY).is_err());
1327 /// assert!(SignedDuration::try_from_secs_f64(f64::NEG_INFINITY).is_err());
1328 /// assert!(SignedDuration::try_from_secs_f64(f64::MIN).is_err());
1329 /// assert!(SignedDuration::try_from_secs_f64(f64::MAX).is_err());
1330 ///
1331 /// # Ok::<(), Box<dyn std::error::Error>>(())
1332 /// ```
1333 #[inline]
1334 pub fn try_from_secs_f64(secs: f64) -> Result<SignedDuration, Error> {
1335 if !secs.is_finite() {
1336 return Err(err!(
1337 "could not convert non-finite seconds \
1338 {secs} to signed duration",
1339 ));
1340 }
1341 if secs < (i64::MIN as f64) {
1342 return Err(err!(
1343 "floating point seconds {secs} overflows signed duration \
1344 minimum value of {:?}",
1345 SignedDuration::MIN,
1346 ));
1347 }
1348 if secs > (i64::MAX as f64) {
1349 return Err(err!(
1350 "floating point seconds {secs} overflows signed duration \
1351 maximum value of {:?}",
1352 SignedDuration::MAX,
1353 ));
1354 }
1355
1356 let mut int_secs = secs.trunc() as i64;
1357 let mut int_nanos =
1358 (secs.fract() * (NANOS_PER_SEC as f64)).round() as i32;
1359 if int_nanos.unsigned_abs() == 1_000_000_000 {
1360 let increment = i64::from(int_nanos.signum());
1361 int_secs = int_secs.checked_add(increment).ok_or_else(|| {
1362 err!(
1363 "floating point seconds {secs} overflows signed duration \
1364 maximum value of {max:?} after rounding its fractional \
1365 component of {fract:?}",
1366 max = SignedDuration::MAX,
1367 fract = secs.fract(),
1368 )
1369 })?;
1370 int_nanos = 0;
1371 }
1372 Ok(SignedDuration::new_unchecked(int_secs, int_nanos))
1373 }
1374
1375 /// Returns a signed duration corresponding to the number of seconds
1376 /// represented as a 32-bit float. The number given may have a fractional
1377 /// nanosecond component.
1378 ///
1379 /// If the given float overflows the minimum or maximum signed duration
1380 /// values, then an error is returned.
1381 ///
1382 /// # Example
1383 ///
1384 /// ```
1385 /// use jiff::SignedDuration;
1386 ///
1387 /// let duration = SignedDuration::try_from_secs_f32(12.123456789)?;
1388 /// assert_eq!(duration.as_secs(), 12);
1389 /// // loss of precision!
1390 /// assert_eq!(duration.subsec_nanos(), 123_456_952);
1391 ///
1392 /// let duration = SignedDuration::try_from_secs_f32(-12.123456789)?;
1393 /// assert_eq!(duration.as_secs(), -12);
1394 /// // loss of precision!
1395 /// assert_eq!(duration.subsec_nanos(), -123_456_952);
1396 ///
1397 /// assert!(SignedDuration::try_from_secs_f32(f32::NAN).is_err());
1398 /// assert!(SignedDuration::try_from_secs_f32(f32::INFINITY).is_err());
1399 /// assert!(SignedDuration::try_from_secs_f32(f32::NEG_INFINITY).is_err());
1400 /// assert!(SignedDuration::try_from_secs_f32(f32::MIN).is_err());
1401 /// assert!(SignedDuration::try_from_secs_f32(f32::MAX).is_err());
1402 ///
1403 /// # Ok::<(), Box<dyn std::error::Error>>(())
1404 /// ```
1405 #[inline]
1406 pub fn try_from_secs_f32(secs: f32) -> Result<SignedDuration, Error> {
1407 if !secs.is_finite() {
1408 return Err(err!(
1409 "could not convert non-finite seconds \
1410 {secs} to signed duration",
1411 ));
1412 }
1413 if secs < (i64::MIN as f32) {
1414 return Err(err!(
1415 "floating point seconds {secs} overflows signed duration \
1416 minimum value of {:?}",
1417 SignedDuration::MIN,
1418 ));
1419 }
1420 if secs > (i64::MAX as f32) {
1421 return Err(err!(
1422 "floating point seconds {secs} overflows signed duration \
1423 maximum value of {:?}",
1424 SignedDuration::MAX,
1425 ));
1426 }
1427 let mut int_nanos =
1428 (secs.fract() * (NANOS_PER_SEC as f32)).round() as i32;
1429 let mut int_secs = secs.trunc() as i64;
1430 if int_nanos.unsigned_abs() == 1_000_000_000 {
1431 let increment = i64::from(int_nanos.signum());
1432 // N.B. I haven't found a way to trigger this error path in tests.
1433 int_secs = int_secs.checked_add(increment).ok_or_else(|| {
1434 err!(
1435 "floating point seconds {secs} overflows signed duration \
1436 maximum value of {max:?} after rounding its fractional \
1437 component of {fract:?}",
1438 max = SignedDuration::MAX,
1439 fract = secs.fract(),
1440 )
1441 })?;
1442 int_nanos = 0;
1443 }
1444 Ok(SignedDuration::new_unchecked(int_secs, int_nanos))
1445 }
1446
1447 /// Returns the result of multiplying this duration by the given 64-bit
1448 /// float.
1449 ///
1450 /// # Panics
1451 ///
1452 /// This panics if the result is not finite or overflows a
1453 /// `SignedDuration`.
1454 ///
1455 /// # Example
1456 ///
1457 /// ```
1458 /// use jiff::SignedDuration;
1459 ///
1460 /// let duration = SignedDuration::new(12, 300_000_000);
1461 /// assert_eq!(
1462 /// duration.mul_f64(2.0),
1463 /// SignedDuration::new(24, 600_000_000),
1464 /// );
1465 /// assert_eq!(
1466 /// duration.mul_f64(-2.0),
1467 /// SignedDuration::new(-24, -600_000_000),
1468 /// );
1469 /// ```
1470 #[inline]
1471 pub fn mul_f64(self, rhs: f64) -> SignedDuration {
1472 SignedDuration::from_secs_f64(rhs * self.as_secs_f64())
1473 }
1474
1475 /// Returns the result of multiplying this duration by the given 32-bit
1476 /// float.
1477 ///
1478 /// # Panics
1479 ///
1480 /// This panics if the result is not finite or overflows a
1481 /// `SignedDuration`.
1482 ///
1483 /// # Example
1484 ///
1485 /// ```
1486 /// use jiff::SignedDuration;
1487 ///
1488 /// let duration = SignedDuration::new(12, 300_000_000);
1489 /// assert_eq!(
1490 /// duration.mul_f32(2.0),
1491 /// // loss of precision!
1492 /// SignedDuration::new(24, 600_000_384),
1493 /// );
1494 /// assert_eq!(
1495 /// duration.mul_f32(-2.0),
1496 /// // loss of precision!
1497 /// SignedDuration::new(-24, -600_000_384),
1498 /// );
1499 /// ```
1500 #[inline]
1501 pub fn mul_f32(self, rhs: f32) -> SignedDuration {
1502 SignedDuration::from_secs_f32(rhs * self.as_secs_f32())
1503 }
1504
1505 /// Returns the result of dividing this duration by the given 64-bit
1506 /// float.
1507 ///
1508 /// # Panics
1509 ///
1510 /// This panics if the result is not finite or overflows a
1511 /// `SignedDuration`.
1512 ///
1513 /// # Example
1514 ///
1515 /// ```
1516 /// use jiff::SignedDuration;
1517 ///
1518 /// let duration = SignedDuration::new(12, 300_000_000);
1519 /// assert_eq!(
1520 /// duration.div_f64(2.0),
1521 /// SignedDuration::new(6, 150_000_000),
1522 /// );
1523 /// assert_eq!(
1524 /// duration.div_f64(-2.0),
1525 /// SignedDuration::new(-6, -150_000_000),
1526 /// );
1527 /// ```
1528 #[inline]
1529 pub fn div_f64(self, rhs: f64) -> SignedDuration {
1530 SignedDuration::from_secs_f64(self.as_secs_f64() / rhs)
1531 }
1532
1533 /// Returns the result of dividing this duration by the given 32-bit
1534 /// float.
1535 ///
1536 /// # Panics
1537 ///
1538 /// This panics if the result is not finite or overflows a
1539 /// `SignedDuration`.
1540 ///
1541 /// # Example
1542 ///
1543 /// ```
1544 /// use jiff::SignedDuration;
1545 ///
1546 /// let duration = SignedDuration::new(12, 300_000_000);
1547 /// assert_eq!(
1548 /// duration.div_f32(2.0),
1549 /// // loss of precision!
1550 /// SignedDuration::new(6, 150_000_096),
1551 /// );
1552 /// assert_eq!(
1553 /// duration.div_f32(-2.0),
1554 /// // loss of precision!
1555 /// SignedDuration::new(-6, -150_000_096),
1556 /// );
1557 /// ```
1558 #[inline]
1559 pub fn div_f32(self, rhs: f32) -> SignedDuration {
1560 SignedDuration::from_secs_f32(self.as_secs_f32() / rhs)
1561 }
1562
1563 /// Divides this signed duration by another signed duration and returns the
1564 /// corresponding 64-bit float result.
1565 ///
1566 /// # Example
1567 ///
1568 /// ```
1569 /// use jiff::SignedDuration;
1570 ///
1571 /// let duration1 = SignedDuration::new(12, 600_000_000);
1572 /// let duration2 = SignedDuration::new(6, 300_000_000);
1573 /// assert_eq!(duration1.div_duration_f64(duration2), 2.0);
1574 ///
1575 /// let duration1 = SignedDuration::new(-12, -600_000_000);
1576 /// let duration2 = SignedDuration::new(6, 300_000_000);
1577 /// assert_eq!(duration1.div_duration_f64(duration2), -2.0);
1578 ///
1579 /// let duration1 = SignedDuration::new(-12, -600_000_000);
1580 /// let duration2 = SignedDuration::new(-6, -300_000_000);
1581 /// assert_eq!(duration1.div_duration_f64(duration2), 2.0);
1582 /// ```
1583 #[inline]
1584 pub fn div_duration_f64(self, rhs: SignedDuration) -> f64 {
1585 let lhs_nanos =
1586 (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos as f64);
1587 let rhs_nanos =
1588 (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos as f64);
1589 lhs_nanos / rhs_nanos
1590 }
1591
1592 /// Divides this signed duration by another signed duration and returns the
1593 /// corresponding 32-bit float result.
1594 ///
1595 /// # Example
1596 ///
1597 /// ```
1598 /// use jiff::SignedDuration;
1599 ///
1600 /// let duration1 = SignedDuration::new(12, 600_000_000);
1601 /// let duration2 = SignedDuration::new(6, 300_000_000);
1602 /// assert_eq!(duration1.div_duration_f32(duration2), 2.0);
1603 ///
1604 /// let duration1 = SignedDuration::new(-12, -600_000_000);
1605 /// let duration2 = SignedDuration::new(6, 300_000_000);
1606 /// assert_eq!(duration1.div_duration_f32(duration2), -2.0);
1607 ///
1608 /// let duration1 = SignedDuration::new(-12, -600_000_000);
1609 /// let duration2 = SignedDuration::new(-6, -300_000_000);
1610 /// assert_eq!(duration1.div_duration_f32(duration2), 2.0);
1611 /// ```
1612 #[inline]
1613 pub fn div_duration_f32(self, rhs: SignedDuration) -> f32 {
1614 let lhs_nanos =
1615 (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos as f32);
1616 let rhs_nanos =
1617 (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos as f32);
1618 lhs_nanos / rhs_nanos
1619 }
1620}
1621
1622/// Additional APIs not found in the standard library.
1623///
1624/// In most cases, these APIs exist as a result of the fact that this duration
1625/// is signed.
1626impl SignedDuration {
1627 /// Returns the number of whole hours in this duration.
1628 ///
1629 /// The value returned is negative when the duration is negative.
1630 ///
1631 /// This does not include any fractional component corresponding to units
1632 /// less than an hour.
1633 ///
1634 /// # Example
1635 ///
1636 /// ```
1637 /// use jiff::SignedDuration;
1638 ///
1639 /// let duration = SignedDuration::new(86_400, 999_999_999);
1640 /// assert_eq!(duration.as_hours(), 24);
1641 ///
1642 /// let duration = SignedDuration::new(-86_400, -999_999_999);
1643 /// assert_eq!(duration.as_hours(), -24);
1644 /// ```
1645 #[inline]
1646 pub const fn as_hours(&self) -> i64 {
1647 self.as_secs() / (MINS_PER_HOUR * SECS_PER_MINUTE)
1648 }
1649
1650 /// Returns the number of whole minutes in this duration.
1651 ///
1652 /// The value returned is negative when the duration is negative.
1653 ///
1654 /// This does not include any fractional component corresponding to units
1655 /// less than a minute.
1656 ///
1657 /// # Example
1658 ///
1659 /// ```
1660 /// use jiff::SignedDuration;
1661 ///
1662 /// let duration = SignedDuration::new(3_600, 999_999_999);
1663 /// assert_eq!(duration.as_mins(), 60);
1664 ///
1665 /// let duration = SignedDuration::new(-3_600, -999_999_999);
1666 /// assert_eq!(duration.as_mins(), -60);
1667 /// ```
1668 #[inline]
1669 pub const fn as_mins(&self) -> i64 {
1670 self.as_secs() / SECS_PER_MINUTE
1671 }
1672
1673 /// Returns the absolute value of this signed duration.
1674 ///
1675 /// If this duration isn't negative, then this returns the original
1676 /// duration unchanged.
1677 ///
1678 /// # Panics
1679 ///
1680 /// This panics when the seconds component of this signed duration is
1681 /// equal to `i64::MIN`.
1682 ///
1683 /// # Example
1684 ///
1685 /// ```
1686 /// use jiff::SignedDuration;
1687 ///
1688 /// let duration = SignedDuration::new(1, -1_999_999_999);
1689 /// assert_eq!(duration.abs(), SignedDuration::new(0, 999_999_999));
1690 /// ```
1691 #[inline]
1692 pub const fn abs(self) -> SignedDuration {
1693 SignedDuration::new_unchecked(self.secs.abs(), self.nanos.abs())
1694 }
1695
1696 /// Returns the absolute value of this signed duration as a
1697 /// [`std::time::Duration`]. More specifically, this routine cannot
1698 /// panic because the absolute value of `SignedDuration::MIN` is
1699 /// representable in a `std::time::Duration`.
1700 ///
1701 /// # Example
1702 ///
1703 /// ```
1704 /// use std::time::Duration;
1705 ///
1706 /// use jiff::SignedDuration;
1707 ///
1708 /// let duration = SignedDuration::MIN;
1709 /// assert_eq!(
1710 /// duration.unsigned_abs(),
1711 /// Duration::new(i64::MIN.unsigned_abs(), 999_999_999),
1712 /// );
1713 /// ```
1714 #[inline]
1715 pub const fn unsigned_abs(self) -> Duration {
1716 Duration::new(self.secs.unsigned_abs(), self.nanos.unsigned_abs())
1717 }
1718
1719 /// Returns this duration with its sign flipped.
1720 ///
1721 /// If this duration is zero, then this returns the duration unchanged.
1722 ///
1723 /// This returns none if the negation does not exist. This occurs in
1724 /// precisely the cases when [`SignedDuration::as_secs`] is equal to
1725 /// `i64::MIN`.
1726 ///
1727 /// # Example
1728 ///
1729 /// ```
1730 /// use jiff::SignedDuration;
1731 ///
1732 /// let duration = SignedDuration::new(12, 123_456_789);
1733 /// assert_eq!(
1734 /// duration.checked_neg(),
1735 /// Some(SignedDuration::new(-12, -123_456_789)),
1736 /// );
1737 ///
1738 /// let duration = SignedDuration::new(-12, -123_456_789);
1739 /// assert_eq!(
1740 /// duration.checked_neg(),
1741 /// Some(SignedDuration::new(12, 123_456_789)),
1742 /// );
1743 ///
1744 /// // Negating the minimum seconds isn't possible.
1745 /// assert_eq!(SignedDuration::MIN.checked_neg(), None);
1746 /// ```
1747 #[inline]
1748 pub const fn checked_neg(self) -> Option<SignedDuration> {
1749 let Some(secs) = self.secs.checked_neg() else { return None };
1750 Some(SignedDuration::new_unchecked(
1751 secs,
1752 // Always OK because `-999_999_999 <= self.nanos <= 999_999_999`.
1753 -self.nanos,
1754 ))
1755 }
1756
1757 /// Returns a number that represents the sign of this duration.
1758 ///
1759 /// * When [`SignedDuration::is_zero`] is true, this returns `0`.
1760 /// * When [`SignedDuration::is_positive`] is true, this returns `1`.
1761 /// * When [`SignedDuration::is_negative`] is true, this returns `-1`.
1762 ///
1763 /// The above cases are mutually exclusive.
1764 ///
1765 /// # Example
1766 ///
1767 /// ```
1768 /// use jiff::SignedDuration;
1769 ///
1770 /// assert_eq!(0, SignedDuration::ZERO.signum());
1771 /// ```
1772 #[inline]
1773 pub const fn signum(self) -> i8 {
1774 if self.is_zero() {
1775 0
1776 } else if self.is_positive() {
1777 1
1778 } else {
1779 debug_assert!(self.is_negative());
1780 -1
1781 }
1782 }
1783
1784 /// Returns true when this duration is positive. That is, greater than
1785 /// [`SignedDuration::ZERO`].
1786 ///
1787 /// # Example
1788 ///
1789 /// ```
1790 /// use jiff::SignedDuration;
1791 ///
1792 /// let duration = SignedDuration::new(0, 1);
1793 /// assert!(duration.is_positive());
1794 /// ```
1795 #[inline]
1796 pub const fn is_positive(&self) -> bool {
1797 self.secs.is_positive() || self.nanos.is_positive()
1798 }
1799
1800 /// Returns true when this duration is negative. That is, less than
1801 /// [`SignedDuration::ZERO`].
1802 ///
1803 /// # Example
1804 ///
1805 /// ```
1806 /// use jiff::SignedDuration;
1807 ///
1808 /// let duration = SignedDuration::new(0, -1);
1809 /// assert!(duration.is_negative());
1810 /// ```
1811 #[inline]
1812 pub const fn is_negative(&self) -> bool {
1813 self.secs.is_negative() || self.nanos.is_negative()
1814 }
1815}
1816
1817/// Additional APIs for computing the duration between date and time values.
1818impl SignedDuration {
1819 pub(crate) fn zoned_until(
1820 zoned1: &Zoned,
1821 zoned2: &Zoned,
1822 ) -> SignedDuration {
1823 SignedDuration::timestamp_until(zoned1.timestamp(), zoned2.timestamp())
1824 }
1825
1826 pub(crate) fn timestamp_until(
1827 timestamp1: Timestamp,
1828 timestamp2: Timestamp,
1829 ) -> SignedDuration {
1830 // OK because all the difference between any two timestamp values can
1831 // fit into a signed duration.
1832 timestamp2.as_duration() - timestamp1.as_duration()
1833 }
1834
1835 pub(crate) fn datetime_until(
1836 datetime1: DateTime,
1837 datetime2: DateTime,
1838 ) -> SignedDuration {
1839 let date_until =
1840 SignedDuration::date_until(datetime1.date(), datetime2.date());
1841 let time_until =
1842 SignedDuration::time_until(datetime1.time(), datetime2.time());
1843 // OK because the difference between any two datetimes can bit into a
1844 // 96-bit integer of nanoseconds.
1845 date_until + time_until
1846 }
1847
1848 pub(crate) fn date_until(date1: Date, date2: Date) -> SignedDuration {
1849 let days = date1.until_days_ranged(date2);
1850 // OK because difference in days fits in an i32, and multiplying an
1851 // i32 by 24 will never overflow an i64.
1852 let hours = 24 * i64::from(days.get());
1853 SignedDuration::from_hours(hours)
1854 }
1855
1856 pub(crate) fn time_until(time1: Time, time2: Time) -> SignedDuration {
1857 let nanos = time1.until_nanoseconds(time2);
1858 SignedDuration::from_nanos(nanos.get())
1859 }
1860
1861 pub(crate) fn offset_until(
1862 offset1: Offset,
1863 offset2: Offset,
1864 ) -> SignedDuration {
1865 let secs1 = i64::from(offset1.seconds());
1866 let secs2 = i64::from(offset2.seconds());
1867 // OK because subtracting any two i32 values will
1868 // never overflow an i64.
1869 let diff = secs2 - secs1;
1870 SignedDuration::from_secs(diff)
1871 }
1872
1873 /// Returns the duration from `time1` until `time2` where the times are
1874 /// [`std::time::SystemTime`] values from the standard library.
1875 ///
1876 /// # Errors
1877 ///
1878 /// This returns an error if the difference between the two time values
1879 /// overflows the signed duration limits.
1880 ///
1881 /// # Example
1882 ///
1883 /// ```
1884 /// use std::time::{Duration, SystemTime};
1885 /// use jiff::SignedDuration;
1886 ///
1887 /// let time1 = SystemTime::UNIX_EPOCH;
1888 /// let time2 = time1.checked_add(Duration::from_secs(86_400)).unwrap();
1889 /// assert_eq!(
1890 /// SignedDuration::system_until(time1, time2)?,
1891 /// SignedDuration::from_hours(24),
1892 /// );
1893 ///
1894 /// # Ok::<(), Box<dyn std::error::Error>>(())
1895 /// ```
1896 #[cfg(feature = "std")]
1897 #[inline]
1898 pub fn system_until(
1899 time1: std::time::SystemTime,
1900 time2: std::time::SystemTime,
1901 ) -> Result<SignedDuration, Error> {
1902 match time2.duration_since(time1) {
1903 Ok(dur) => SignedDuration::try_from(dur).with_context(|| {
1904 err!(
1905 "unsigned duration {dur:?} for system time since \
1906 Unix epoch overflowed signed duration"
1907 )
1908 }),
1909 Err(err) => {
1910 let dur = err.duration();
1911 let dur =
1912 SignedDuration::try_from(dur).with_context(|| {
1913 err!(
1914 "unsigned duration {dur:?} for system time before \
1915 Unix epoch overflowed signed duration"
1916 )
1917 })?;
1918 dur.checked_neg().ok_or_else(|| {
1919 err!("negating duration {dur:?} from before the Unix epoch \
1920 overflowed signed duration")
1921 })
1922 }
1923 }
1924 }
1925}
1926
1927/// Jiff specific APIs.
1928impl SignedDuration {
1929 /// Returns a new signed duration that is rounded according to the given
1930 /// configuration.
1931 ///
1932 /// Rounding a duration has a number of parameters, all of which are
1933 /// optional. When no parameters are given, then no rounding is done, and
1934 /// the duration as given is returned. That is, it's a no-op.
1935 ///
1936 /// As is consistent with `SignedDuration` itself, rounding only supports
1937 /// time units, i.e., units of hours or smaller. If a calendar `Unit` is
1938 /// provided, then an error is returned. In order to round a duration with
1939 /// calendar units, you must use [`Span::round`](crate::Span::round) and
1940 /// provide a relative datetime.
1941 ///
1942 /// The parameters are, in brief:
1943 ///
1944 /// * [`SignedDurationRound::smallest`] sets the smallest [`Unit`] that
1945 /// is allowed to be non-zero in the duration returned. By default, it
1946 /// is set to [`Unit::Nanosecond`], i.e., no rounding occurs. When the
1947 /// smallest unit is set to something bigger than nanoseconds, then the
1948 /// non-zero units in the duration smaller than the smallest unit are used
1949 /// to determine how the duration should be rounded. For example, rounding
1950 /// `1 hour 59 minutes` to the nearest hour using the default rounding mode
1951 /// would produce `2 hours`.
1952 /// * [`SignedDurationRound::mode`] determines how to handle the remainder
1953 /// when rounding. The default is [`RoundMode::HalfExpand`], which
1954 /// corresponds to how you were likely taught to round in school.
1955 /// Alternative modes, like [`RoundMode::Trunc`], exist too. For example,
1956 /// a truncating rounding of `1 hour 59 minutes` to the nearest hour would
1957 /// produce `1 hour`.
1958 /// * [`SignedDurationRound::increment`] sets the rounding granularity to
1959 /// use for the configured smallest unit. For example, if the smallest unit
1960 /// is minutes and the increment is 5, then the duration returned will
1961 /// always have its minute units set to a multiple of `5`.
1962 ///
1963 /// # Errors
1964 ///
1965 /// In general, there are two main ways for rounding to fail: an improper
1966 /// configuration like trying to round a duration to the nearest calendar
1967 /// unit, or when overflow occurs. Overflow can occur when the duration
1968 /// would exceed the minimum or maximum `SignedDuration` values. Typically,
1969 /// this can only realistically happen if the duration before rounding is
1970 /// already close to its minimum or maximum value.
1971 ///
1972 /// # Example: round to the nearest second
1973 ///
1974 /// This shows how to round a duration to the nearest second. This might
1975 /// be useful when you want to chop off any sub-second component in a way
1976 /// that depends on how close it is (or not) to the next second.
1977 ///
1978 /// ```
1979 /// use jiff::{SignedDuration, Unit};
1980 ///
1981 /// // rounds up
1982 /// let dur = SignedDuration::new(4 * 60 * 60 + 50 * 60 + 32, 500_000_000);
1983 /// assert_eq!(
1984 /// dur.round(Unit::Second)?,
1985 /// SignedDuration::new(4 * 60 * 60 + 50 * 60 + 33, 0),
1986 /// );
1987 /// // rounds down
1988 /// let dur = SignedDuration::new(4 * 60 * 60 + 50 * 60 + 32, 499_999_999);
1989 /// assert_eq!(
1990 /// dur.round(Unit::Second)?,
1991 /// SignedDuration::new(4 * 60 * 60 + 50 * 60 + 32, 0),
1992 /// );
1993 ///
1994 /// # Ok::<(), Box<dyn std::error::Error>>(())
1995 /// ```
1996 ///
1997 /// # Example: round to the nearest half minute
1998 ///
1999 /// One can use [`SignedDurationRound::increment`] to set the rounding
2000 /// increment:
2001 ///
2002 /// ```
2003 /// use jiff::{SignedDuration, SignedDurationRound, Unit};
2004 ///
2005 /// let options = SignedDurationRound::new()
2006 /// .smallest(Unit::Second)
2007 /// .increment(30);
2008 ///
2009 /// // rounds up
2010 /// let dur = SignedDuration::from_secs(4 * 60 * 60 + 50 * 60 + 15);
2011 /// assert_eq!(
2012 /// dur.round(options)?,
2013 /// SignedDuration::from_secs(4 * 60 * 60 + 50 * 60 + 30),
2014 /// );
2015 /// // rounds down
2016 /// let dur = SignedDuration::from_secs(4 * 60 * 60 + 50 * 60 + 14);
2017 /// assert_eq!(
2018 /// dur.round(options)?,
2019 /// SignedDuration::from_secs(4 * 60 * 60 + 50 * 60),
2020 /// );
2021 ///
2022 /// # Ok::<(), Box<dyn std::error::Error>>(())
2023 /// ```
2024 ///
2025 /// # Example: overflow results in an error
2026 ///
2027 /// If rounding would result in a value that exceeds a `SignedDuration`'s
2028 /// minimum or maximum values, then an error occurs:
2029 ///
2030 /// ```
2031 /// use jiff::{SignedDuration, Unit};
2032 ///
2033 /// assert_eq!(
2034 /// SignedDuration::MAX.round(Unit::Hour).unwrap_err().to_string(),
2035 /// "rounding `2562047788015215h 30m 7s 999ms 999µs 999ns` to \
2036 /// nearest hour in increments of 1 resulted in \
2037 /// 9223372036854777600 seconds, which does not fit into an i64 \
2038 /// and thus overflows `SignedDuration`",
2039 /// );
2040 /// assert_eq!(
2041 /// SignedDuration::MIN.round(Unit::Hour).unwrap_err().to_string(),
2042 /// "rounding `2562047788015215h 30m 8s 999ms 999µs 999ns ago` to \
2043 /// nearest hour in increments of 1 resulted in \
2044 /// -9223372036854777600 seconds, which does not fit into an i64 \
2045 /// and thus overflows `SignedDuration`",
2046 /// );
2047 /// ```
2048 ///
2049 /// # Example: rounding with a calendar unit results in an error
2050 ///
2051 /// ```
2052 /// use jiff::{SignedDuration, Unit};
2053 ///
2054 /// assert_eq!(
2055 /// SignedDuration::ZERO.round(Unit::Day).unwrap_err().to_string(),
2056 /// "rounding `SignedDuration` failed \
2057 /// because a calendar unit of days was provided \
2058 /// (to round by calendar units, you must use a `Span`)",
2059 /// );
2060 /// ```
2061 #[inline]
2062 pub fn round<R: Into<SignedDurationRound>>(
2063 self,
2064 options: R,
2065 ) -> Result<SignedDuration, Error> {
2066 let options: SignedDurationRound = options.into();
2067 options.round(self)
2068 }
2069}
2070
2071impl core::fmt::Display for SignedDuration {
2072 #[inline]
2073 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2074 use crate::fmt::StdFmtWrite;
2075
2076 if f.alternate() {
2077 friendly::DEFAULT_SPAN_PRINTER
2078 .print_duration(self, StdFmtWrite(f))
2079 .map_err(|_| core::fmt::Error)
2080 } else {
2081 temporal::DEFAULT_SPAN_PRINTER
2082 .print_duration(self, StdFmtWrite(f))
2083 .map_err(|_| core::fmt::Error)
2084 }
2085 }
2086}
2087
2088impl core::fmt::Debug for SignedDuration {
2089 #[inline]
2090 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2091 use crate::fmt::StdFmtWrite;
2092
2093 if f.alternate() {
2094 if self.subsec_nanos() == 0 {
2095 write!(f, "{}s", self.as_secs())
2096 } else if self.as_secs() == 0 {
2097 write!(f, "{}ns", self.subsec_nanos())
2098 } else {
2099 write!(
2100 f,
2101 "{}s {}ns",
2102 self.as_secs(),
2103 self.subsec_nanos().unsigned_abs()
2104 )
2105 }
2106 } else {
2107 friendly::DEFAULT_SPAN_PRINTER
2108 .print_duration(self, StdFmtWrite(f))
2109 .map_err(|_| core::fmt::Error)
2110 }
2111 }
2112}
2113
2114impl TryFrom<Duration> for SignedDuration {
2115 type Error = Error;
2116
2117 fn try_from(d: Duration) -> Result<SignedDuration, Error> {
2118 let secs = i64::try_from(d.as_secs()).map_err(|_| {
2119 err!("seconds in unsigned duration {d:?} overflowed i64")
2120 })?;
2121 // Guaranteed to succeed since 0<=nanos<=999,999,999.
2122 let nanos = i32::try_from(d.subsec_nanos()).unwrap();
2123 Ok(SignedDuration::new_unchecked(secs, nanos))
2124 }
2125}
2126
2127impl TryFrom<SignedDuration> for Duration {
2128 type Error = Error;
2129
2130 fn try_from(sd: SignedDuration) -> Result<Duration, Error> {
2131 // This isn't needed, but improves error messages.
2132 if sd.is_negative() {
2133 return Err(err!(
2134 "cannot convert negative duration `{sd:?}` to \
2135 unsigned `std::time::Duration`",
2136 ));
2137 }
2138 let secs = u64::try_from(sd.as_secs()).map_err(|_| {
2139 err!("seconds in signed duration {sd:?} overflowed u64")
2140 })?;
2141 // Guaranteed to succeed because the above only succeeds
2142 // when `sd` is non-negative. And when `sd` is non-negative,
2143 // we are guaranteed that 0<=nanos<=999,999,999.
2144 let nanos = u32::try_from(sd.subsec_nanos()).unwrap();
2145 Ok(Duration::new(secs, nanos))
2146 }
2147}
2148
2149impl From<Offset> for SignedDuration {
2150 fn from(offset: Offset) -> SignedDuration {
2151 SignedDuration::from_secs(i64::from(offset.seconds()))
2152 }
2153}
2154
2155impl core::str::FromStr for SignedDuration {
2156 type Err = Error;
2157
2158 #[inline]
2159 fn from_str(string: &str) -> Result<SignedDuration, Error> {
2160 parse_iso_or_friendly(string.as_bytes())
2161 }
2162}
2163
2164impl core::ops::Neg for SignedDuration {
2165 type Output = SignedDuration;
2166
2167 #[inline]
2168 fn neg(self) -> SignedDuration {
2169 self.checked_neg().expect("overflow when negating signed duration")
2170 }
2171}
2172
2173impl core::ops::Add for SignedDuration {
2174 type Output = SignedDuration;
2175
2176 #[inline]
2177 fn add(self, rhs: SignedDuration) -> SignedDuration {
2178 self.checked_add(rhs).expect("overflow when adding signed durations")
2179 }
2180}
2181
2182impl core::ops::AddAssign for SignedDuration {
2183 #[inline]
2184 fn add_assign(&mut self, rhs: SignedDuration) {
2185 *self = *self + rhs;
2186 }
2187}
2188
2189impl core::ops::Sub for SignedDuration {
2190 type Output = SignedDuration;
2191
2192 #[inline]
2193 fn sub(self, rhs: SignedDuration) -> SignedDuration {
2194 self.checked_sub(rhs)
2195 .expect("overflow when subtracting signed durations")
2196 }
2197}
2198
2199impl core::ops::SubAssign for SignedDuration {
2200 #[inline]
2201 fn sub_assign(&mut self, rhs: SignedDuration) {
2202 *self = *self - rhs;
2203 }
2204}
2205
2206impl core::ops::Mul<i32> for SignedDuration {
2207 type Output = SignedDuration;
2208
2209 #[inline]
2210 fn mul(self, rhs: i32) -> SignedDuration {
2211 self.checked_mul(rhs)
2212 .expect("overflow when multiplying signed duration by scalar")
2213 }
2214}
2215
2216impl core::iter::Sum for SignedDuration {
2217 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
2218 iter.fold(Self::new(0, 0), |acc, d| acc + d)
2219 }
2220}
2221
2222impl<'a> core::iter::Sum<&'a Self> for SignedDuration {
2223 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
2224 iter.fold(Self::new(0, 0), |acc, d| acc + *d)
2225 }
2226}
2227
2228impl core::ops::Mul<SignedDuration> for i32 {
2229 type Output = SignedDuration;
2230
2231 #[inline]
2232 fn mul(self, rhs: SignedDuration) -> SignedDuration {
2233 rhs * self
2234 }
2235}
2236
2237impl core::ops::MulAssign<i32> for SignedDuration {
2238 #[inline]
2239 fn mul_assign(&mut self, rhs: i32) {
2240 *self = *self * rhs;
2241 }
2242}
2243
2244impl core::ops::Div<i32> for SignedDuration {
2245 type Output = SignedDuration;
2246
2247 #[inline]
2248 fn div(self, rhs: i32) -> SignedDuration {
2249 self.checked_div(rhs)
2250 .expect("overflow when dividing signed duration by scalar")
2251 }
2252}
2253
2254impl core::ops::DivAssign<i32> for SignedDuration {
2255 #[inline]
2256 fn div_assign(&mut self, rhs: i32) {
2257 *self = *self / rhs;
2258 }
2259}
2260
2261#[cfg(feature = "serde")]
2262impl serde::Serialize for SignedDuration {
2263 #[inline]
2264 fn serialize<S: serde::Serializer>(
2265 &self,
2266 serializer: S,
2267 ) -> Result<S::Ok, S::Error> {
2268 serializer.collect_str(self)
2269 }
2270}
2271
2272#[cfg(feature = "serde")]
2273impl<'de> serde::Deserialize<'de> for SignedDuration {
2274 #[inline]
2275 fn deserialize<D: serde::Deserializer<'de>>(
2276 deserializer: D,
2277 ) -> Result<SignedDuration, D::Error> {
2278 use serde::de;
2279
2280 struct SignedDurationVisitor;
2281
2282 impl<'de> de::Visitor<'de> for SignedDurationVisitor {
2283 type Value = SignedDuration;
2284
2285 fn expecting(
2286 &self,
2287 f: &mut core::fmt::Formatter,
2288 ) -> core::fmt::Result {
2289 f.write_str("a signed duration string")
2290 }
2291
2292 #[inline]
2293 fn visit_bytes<E: de::Error>(
2294 self,
2295 value: &[u8],
2296 ) -> Result<SignedDuration, E> {
2297 parse_iso_or_friendly(value).map_err(de::Error::custom)
2298 }
2299
2300 #[inline]
2301 fn visit_str<E: de::Error>(
2302 self,
2303 value: &str,
2304 ) -> Result<SignedDuration, E> {
2305 self.visit_bytes(value.as_bytes())
2306 }
2307 }
2308
2309 deserializer.deserialize_str(SignedDurationVisitor)
2310 }
2311}
2312
2313/// Options for [`SignedDuration::round`].
2314///
2315/// This type provides a way to configure the rounding of a duration. This
2316/// includes setting the smallest unit (i.e., the unit to round), the rounding
2317/// increment and the rounding mode (e.g., "ceil" or "truncate").
2318///
2319/// `SignedDuration::round` accepts anything that implements
2320/// `Into<SignedDurationRound>`. There are a few key trait implementations that
2321/// make this convenient:
2322///
2323/// * `From<Unit> for SignedDurationRound` will construct a rounding
2324/// configuration where the smallest unit is set to the one given.
2325/// * `From<(Unit, i64)> for SignedDurationRound` will construct a rounding
2326/// configuration where the smallest unit and the rounding increment are set to
2327/// the ones given.
2328///
2329/// In order to set other options (like the rounding mode), one must explicitly
2330/// create a `SignedDurationRound` and pass it to `SignedDuration::round`.
2331///
2332/// # Example
2333///
2334/// This example shows how to always round up to the nearest half-minute:
2335///
2336/// ```
2337/// use jiff::{RoundMode, SignedDuration, SignedDurationRound, Unit};
2338///
2339/// let dur = SignedDuration::new(4 * 60 * 60 + 17 * 60 + 1, 123_456_789);
2340/// let rounded = dur.round(
2341/// SignedDurationRound::new()
2342/// .smallest(Unit::Second)
2343/// .increment(30)
2344/// .mode(RoundMode::Expand),
2345/// )?;
2346/// assert_eq!(rounded, SignedDuration::from_secs(4 * 60 * 60 + 17 * 60 + 30));
2347///
2348/// # Ok::<(), Box<dyn std::error::Error>>(())
2349/// ```
2350#[derive(Clone, Copy, Debug)]
2351pub struct SignedDurationRound {
2352 smallest: Unit,
2353 mode: RoundMode,
2354 increment: i64,
2355}
2356
2357impl SignedDurationRound {
2358 /// Create a new default configuration for rounding a signed duration via
2359 /// [`SignedDuration::round`].
2360 ///
2361 /// The default configuration does no rounding.
2362 #[inline]
2363 pub fn new() -> SignedDurationRound {
2364 SignedDurationRound {
2365 smallest: Unit::Nanosecond,
2366 mode: RoundMode::HalfExpand,
2367 increment: 1,
2368 }
2369 }
2370
2371 /// Set the smallest units allowed in the duration returned. These are the
2372 /// units that the duration is rounded to.
2373 ///
2374 /// # Errors
2375 ///
2376 /// The unit must be [`Unit::Hour`] or smaller.
2377 ///
2378 /// # Example
2379 ///
2380 /// A basic example that rounds to the nearest minute:
2381 ///
2382 /// ```
2383 /// use jiff::{SignedDuration, Unit};
2384 ///
2385 /// let duration = SignedDuration::new(15 * 60 + 46, 0);
2386 /// assert_eq!(duration.round(Unit::Minute)?, SignedDuration::from_mins(16));
2387 ///
2388 /// # Ok::<(), Box<dyn std::error::Error>>(())
2389 /// ```
2390 #[inline]
2391 pub fn smallest(self, unit: Unit) -> SignedDurationRound {
2392 SignedDurationRound { smallest: unit, ..self }
2393 }
2394
2395 /// Set the rounding mode.
2396 ///
2397 /// This defaults to [`RoundMode::HalfExpand`], which makes rounding work
2398 /// like how you were taught in school.
2399 ///
2400 /// # Example
2401 ///
2402 /// A basic example that rounds to the nearest minute, but changing its
2403 /// rounding mode to truncation:
2404 ///
2405 /// ```
2406 /// use jiff::{RoundMode, SignedDuration, SignedDurationRound, Unit};
2407 ///
2408 /// let duration = SignedDuration::new(15 * 60 + 46, 0);
2409 /// assert_eq!(
2410 /// duration.round(SignedDurationRound::new()
2411 /// .smallest(Unit::Minute)
2412 /// .mode(RoundMode::Trunc),
2413 /// )?,
2414 /// // The default round mode does rounding like
2415 /// // how you probably learned in school, and would
2416 /// // result in rounding up to 16 minutes. But we
2417 /// // change it to truncation here, which makes it
2418 /// // round down.
2419 /// SignedDuration::from_mins(15),
2420 /// );
2421 ///
2422 /// # Ok::<(), Box<dyn std::error::Error>>(())
2423 /// ```
2424 #[inline]
2425 pub fn mode(self, mode: RoundMode) -> SignedDurationRound {
2426 SignedDurationRound { mode, ..self }
2427 }
2428
2429 /// Set the rounding increment for the smallest unit.
2430 ///
2431 /// The default value is `1`. Other values permit rounding the smallest
2432 /// unit to the nearest integer increment specified. For example, if the
2433 /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
2434 /// `30` would result in rounding in increments of a half hour. That is,
2435 /// the only minute value that could result would be `0` or `30`.
2436 ///
2437 /// # Errors
2438 ///
2439 /// The rounding increment must divide evenly into the next highest unit
2440 /// after the smallest unit configured (and must not be equivalent to it).
2441 /// For example, if the smallest unit is [`Unit::Nanosecond`], then *some*
2442 /// of the valid values for the rounding increment are `1`, `2`, `4`, `5`,
2443 /// `100` and `500`. Namely, any integer that divides evenly into `1,000`
2444 /// nanoseconds since there are `1,000` nanoseconds in the next highest
2445 /// unit (microseconds).
2446 ///
2447 /// # Example
2448 ///
2449 /// This shows how to round a duration to the nearest 5 minute increment:
2450 ///
2451 /// ```
2452 /// use jiff::{SignedDuration, Unit};
2453 ///
2454 /// let duration = SignedDuration::new(4 * 60 * 60 + 2 * 60 + 30, 0);
2455 /// assert_eq!(
2456 /// duration.round((Unit::Minute, 5))?,
2457 /// SignedDuration::new(4 * 60 * 60 + 5 * 60, 0),
2458 /// );
2459 ///
2460 /// # Ok::<(), Box<dyn std::error::Error>>(())
2461 /// ```
2462 #[inline]
2463 pub fn increment(self, increment: i64) -> SignedDurationRound {
2464 SignedDurationRound { increment, ..self }
2465 }
2466
2467 /// Returns the `smallest` unit configuration.
2468 pub(crate) fn get_smallest(&self) -> Unit {
2469 self.smallest
2470 }
2471
2472 /// Does the actual duration rounding.
2473 fn round(&self, dur: SignedDuration) -> Result<SignedDuration, Error> {
2474 if self.smallest > Unit::Hour {
2475 return Err(err!(
2476 "rounding `SignedDuration` failed because \
2477 a calendar unit of {plural} was provided \
2478 (to round by calendar units, you must use a `Span`)",
2479 plural = self.smallest.plural(),
2480 ));
2481 }
2482 let nanos = t::NoUnits128::new_unchecked(dur.as_nanos());
2483 let increment = t::NoUnits::new_unchecked(self.increment);
2484 let rounded = self.mode.round_by_unit_in_nanoseconds(
2485 nanos,
2486 self.smallest,
2487 increment,
2488 );
2489
2490 let seconds = rounded / t::NANOS_PER_SECOND;
2491 let seconds =
2492 t::NoUnits::try_rfrom("seconds", seconds).map_err(|_| {
2493 err!(
2494 "rounding `{dur:#}` to nearest {singular} in increments \
2495 of {increment} resulted in {seconds} seconds, which does \
2496 not fit into an i64 and thus overflows `SignedDuration`",
2497 singular = self.smallest.singular(),
2498 )
2499 })?;
2500 let subsec_nanos = rounded % t::NANOS_PER_SECOND;
2501 // OK because % 1_000_000_000 above guarantees that the result fits
2502 // in a i32.
2503 let subsec_nanos = i32::try_from(subsec_nanos).unwrap();
2504 Ok(SignedDuration::new(seconds.get(), subsec_nanos))
2505 }
2506}
2507
2508impl Default for SignedDurationRound {
2509 fn default() -> SignedDurationRound {
2510 SignedDurationRound::new()
2511 }
2512}
2513
2514impl From<Unit> for SignedDurationRound {
2515 fn from(unit: Unit) -> SignedDurationRound {
2516 SignedDurationRound::default().smallest(unit)
2517 }
2518}
2519
2520impl From<(Unit, i64)> for SignedDurationRound {
2521 fn from((unit, increment): (Unit, i64)) -> SignedDurationRound {
2522 SignedDurationRound::default().smallest(unit).increment(increment)
2523 }
2524}
2525
2526/// A common parsing function that works in bytes.
2527///
2528/// Specifically, this parses either an ISO 8601 duration into a
2529/// `SignedDuration` or a "friendly" duration into a `SignedDuration`. It also
2530/// tries to give decent error messages.
2531///
2532/// This works because the friendly and ISO 8601 formats have non-overlapping
2533/// prefixes. Both can start with a `+` or `-`, but aside from that, an ISO
2534/// 8601 duration _always_ has to start with a `P` or `p`. We can utilize this
2535/// property to very quickly determine how to parse the input. We just need to
2536/// handle the possibly ambiguous case with a leading sign a little carefully
2537/// in order to ensure good error messages.
2538///
2539/// (We do the same thing for `Span`.)
2540#[cfg_attr(feature = "perf-inline", inline(always))]
2541fn parse_iso_or_friendly(bytes: &[u8]) -> Result<SignedDuration, Error> {
2542 if bytes.is_empty() {
2543 return Err(err!(
2544 "an empty string is not a valid `SignedDuration`, \
2545 expected either a ISO 8601 or Jiff's 'friendly' \
2546 format",
2547 ));
2548 }
2549 let mut first = bytes[0];
2550 if first == b'+' || first == b'-' {
2551 if bytes.len() == 1 {
2552 return Err(err!(
2553 "found nothing after sign `{sign}`, \
2554 which is not a valid `SignedDuration`, \
2555 expected either a ISO 8601 or Jiff's 'friendly' \
2556 format",
2557 sign = escape::Byte(first),
2558 ));
2559 }
2560 first = bytes[1];
2561 }
2562 if first == b'P' || first == b'p' {
2563 temporal::DEFAULT_SPAN_PARSER.parse_duration(bytes)
2564 } else {
2565 friendly::DEFAULT_SPAN_PARSER.parse_duration(bytes)
2566 }
2567}
2568
2569#[cfg(test)]
2570mod tests {
2571 use std::io::Cursor;
2572
2573 use alloc::string::ToString;
2574
2575 use super::*;
2576
2577 #[test]
2578 fn new() {
2579 let d = SignedDuration::new(12, i32::MAX);
2580 assert_eq!(d.as_secs(), 14);
2581 assert_eq!(d.subsec_nanos(), 147_483_647);
2582
2583 let d = SignedDuration::new(-12, i32::MIN);
2584 assert_eq!(d.as_secs(), -14);
2585 assert_eq!(d.subsec_nanos(), -147_483_648);
2586
2587 let d = SignedDuration::new(i64::MAX, i32::MIN);
2588 assert_eq!(d.as_secs(), i64::MAX - 3);
2589 assert_eq!(d.subsec_nanos(), 852_516_352);
2590
2591 let d = SignedDuration::new(i64::MIN, i32::MAX);
2592 assert_eq!(d.as_secs(), i64::MIN + 3);
2593 assert_eq!(d.subsec_nanos(), -852_516_353);
2594 }
2595
2596 #[test]
2597 #[should_panic]
2598 fn new_fail_positive() {
2599 SignedDuration::new(i64::MAX, 1_000_000_000);
2600 }
2601
2602 #[test]
2603 #[should_panic]
2604 fn new_fail_negative() {
2605 SignedDuration::new(i64::MIN, -1_000_000_000);
2606 }
2607
2608 #[test]
2609 fn from_hours_limits() {
2610 let d = SignedDuration::from_hours(2_562_047_788_015_215);
2611 assert_eq!(d.as_secs(), 9223372036854774000);
2612
2613 let d = SignedDuration::from_hours(-2_562_047_788_015_215);
2614 assert_eq!(d.as_secs(), -9223372036854774000);
2615 }
2616
2617 #[test]
2618 #[should_panic]
2619 fn from_hours_fail_positive() {
2620 SignedDuration::from_hours(2_562_047_788_015_216);
2621 }
2622
2623 #[test]
2624 #[should_panic]
2625 fn from_hours_fail_negative() {
2626 SignedDuration::from_hours(-2_562_047_788_015_216);
2627 }
2628
2629 #[test]
2630 fn from_minutes_limits() {
2631 let d = SignedDuration::from_mins(153_722_867_280_912_930);
2632 assert_eq!(d.as_secs(), 9223372036854775800);
2633
2634 let d = SignedDuration::from_mins(-153_722_867_280_912_930);
2635 assert_eq!(d.as_secs(), -9223372036854775800);
2636 }
2637
2638 #[test]
2639 #[should_panic]
2640 fn from_minutes_fail_positive() {
2641 SignedDuration::from_mins(153_722_867_280_912_931);
2642 }
2643
2644 #[test]
2645 #[should_panic]
2646 fn from_minutes_fail_negative() {
2647 SignedDuration::from_mins(-153_722_867_280_912_931);
2648 }
2649
2650 #[test]
2651 fn add() {
2652 let add = |(secs1, nanos1): (i64, i32),
2653 (secs2, nanos2): (i64, i32)|
2654 -> (i64, i32) {
2655 let d1 = SignedDuration::new(secs1, nanos1);
2656 let d2 = SignedDuration::new(secs2, nanos2);
2657 let sum = d1.checked_add(d2).unwrap();
2658 (sum.as_secs(), sum.subsec_nanos())
2659 };
2660
2661 assert_eq!(add((1, 1), (1, 1)), (2, 2));
2662 assert_eq!(add((1, 1), (-1, -1)), (0, 0));
2663 assert_eq!(add((-1, -1), (1, 1)), (0, 0));
2664 assert_eq!(add((-1, -1), (-1, -1)), (-2, -2));
2665
2666 assert_eq!(add((1, 500_000_000), (1, 500_000_000)), (3, 0));
2667 assert_eq!(add((-1, -500_000_000), (-1, -500_000_000)), (-3, 0));
2668 assert_eq!(
2669 add((5, 200_000_000), (-1, -500_000_000)),
2670 (3, 700_000_000)
2671 );
2672 assert_eq!(
2673 add((-5, -200_000_000), (1, 500_000_000)),
2674 (-3, -700_000_000)
2675 );
2676 }
2677
2678 #[test]
2679 fn add_overflow() {
2680 let add = |(secs1, nanos1): (i64, i32),
2681 (secs2, nanos2): (i64, i32)|
2682 -> Option<(i64, i32)> {
2683 let d1 = SignedDuration::new(secs1, nanos1);
2684 let d2 = SignedDuration::new(secs2, nanos2);
2685 d1.checked_add(d2).map(|d| (d.as_secs(), d.subsec_nanos()))
2686 };
2687 assert_eq!(None, add((i64::MAX, 0), (1, 0)));
2688 assert_eq!(None, add((i64::MIN, 0), (-1, 0)));
2689 assert_eq!(None, add((i64::MAX, 1), (0, 999_999_999)));
2690 assert_eq!(None, add((i64::MIN, -1), (0, -999_999_999)));
2691 }
2692
2693 /// # `serde` deserializer compatibility test
2694 ///
2695 /// Serde YAML used to be unable to deserialize `jiff` types,
2696 /// as deserializing from bytes is not supported by the deserializer.
2697 ///
2698 /// - <https://github.com/BurntSushi/jiff/issues/138>
2699 /// - <https://github.com/BurntSushi/jiff/discussions/148>
2700 #[test]
2701 fn signed_duration_deserialize_yaml() {
2702 let expected = SignedDuration::from_secs(123456789);
2703
2704 let deserialized: SignedDuration =
2705 serde_yaml::from_str("PT34293h33m9s").unwrap();
2706
2707 assert_eq!(deserialized, expected);
2708
2709 let deserialized: SignedDuration =
2710 serde_yaml::from_slice("PT34293h33m9s".as_bytes()).unwrap();
2711
2712 assert_eq!(deserialized, expected);
2713
2714 let cursor = Cursor::new(b"PT34293h33m9s");
2715 let deserialized: SignedDuration =
2716 serde_yaml::from_reader(cursor).unwrap();
2717
2718 assert_eq!(deserialized, expected);
2719 }
2720
2721 #[test]
2722 fn from_str() {
2723 let p = |s: &str| -> Result<SignedDuration, Error> { s.parse() };
2724
2725 insta::assert_snapshot!(
2726 p("1 hour").unwrap(),
2727 @"PT1H",
2728 );
2729 insta::assert_snapshot!(
2730 p("+1 hour").unwrap(),
2731 @"PT1H",
2732 );
2733 insta::assert_snapshot!(
2734 p("-1 hour").unwrap(),
2735 @"-PT1H",
2736 );
2737 insta::assert_snapshot!(
2738 p("PT1h").unwrap(),
2739 @"PT1H",
2740 );
2741 insta::assert_snapshot!(
2742 p("+PT1h").unwrap(),
2743 @"PT1H",
2744 );
2745 insta::assert_snapshot!(
2746 p("-PT1h").unwrap(),
2747 @"-PT1H",
2748 );
2749
2750 insta::assert_snapshot!(
2751 p("").unwrap_err(),
2752 @"an empty string is not a valid `SignedDuration`, expected either a ISO 8601 or Jiff's 'friendly' format",
2753 );
2754 insta::assert_snapshot!(
2755 p("+").unwrap_err(),
2756 @"found nothing after sign `+`, which is not a valid `SignedDuration`, expected either a ISO 8601 or Jiff's 'friendly' format",
2757 );
2758 insta::assert_snapshot!(
2759 p("-").unwrap_err(),
2760 @"found nothing after sign `-`, which is not a valid `SignedDuration`, expected either a ISO 8601 or Jiff's 'friendly' format",
2761 );
2762 }
2763
2764 #[test]
2765 fn serde_deserialize() {
2766 let p = |s: &str| -> Result<SignedDuration, serde_json::Error> {
2767 serde_json::from_str(&alloc::format!("\"{s}\""))
2768 };
2769
2770 insta::assert_snapshot!(
2771 p("1 hour").unwrap(),
2772 @"PT1H",
2773 );
2774 insta::assert_snapshot!(
2775 p("+1 hour").unwrap(),
2776 @"PT1H",
2777 );
2778 insta::assert_snapshot!(
2779 p("-1 hour").unwrap(),
2780 @"-PT1H",
2781 );
2782 insta::assert_snapshot!(
2783 p("PT1h").unwrap(),
2784 @"PT1H",
2785 );
2786 insta::assert_snapshot!(
2787 p("+PT1h").unwrap(),
2788 @"PT1H",
2789 );
2790 insta::assert_snapshot!(
2791 p("-PT1h").unwrap(),
2792 @"-PT1H",
2793 );
2794
2795 insta::assert_snapshot!(
2796 p("").unwrap_err(),
2797 @"an empty string is not a valid `SignedDuration`, expected either a ISO 8601 or Jiff's 'friendly' format at line 1 column 2",
2798 );
2799 insta::assert_snapshot!(
2800 p("+").unwrap_err(),
2801 @"found nothing after sign `+`, which is not a valid `SignedDuration`, expected either a ISO 8601 or Jiff's 'friendly' format at line 1 column 3",
2802 );
2803 insta::assert_snapshot!(
2804 p("-").unwrap_err(),
2805 @"found nothing after sign `-`, which is not a valid `SignedDuration`, expected either a ISO 8601 or Jiff's 'friendly' format at line 1 column 3",
2806 );
2807 }
2808
2809 /// This test ensures that we can parse `humantime` formatted durations.
2810 #[test]
2811 fn humantime_compatibility_parse() {
2812 let dur = std::time::Duration::new(26_784, 123_456_789);
2813 let formatted = humantime::format_duration(dur).to_string();
2814 assert_eq!(formatted, "7h 26m 24s 123ms 456us 789ns");
2815
2816 let expected = SignedDuration::try_from(dur).unwrap();
2817 assert_eq!(formatted.parse::<SignedDuration>().unwrap(), expected);
2818 }
2819
2820 /// This test ensures that we can print a `SignedDuration` that `humantime`
2821 /// can parse.
2822 ///
2823 /// Note that this isn't the default since `humantime`'s parser is
2824 /// pretty limited. e.g., It doesn't support things like `nsecs`
2825 /// despite supporting `secs`. And other reasons. See the docs on
2826 /// `Designator::HumanTime` for why we sadly provide a custom variant for
2827 /// it.
2828 #[test]
2829 fn humantime_compatibility_print() {
2830 static PRINTER: friendly::SpanPrinter = friendly::SpanPrinter::new()
2831 .designator(friendly::Designator::HumanTime);
2832
2833 let sdur = SignedDuration::new(26_784, 123_456_789);
2834 let formatted = PRINTER.duration_to_string(&sdur);
2835 assert_eq!(formatted, "7h 26m 24s 123ms 456us 789ns");
2836
2837 let dur = humantime::parse_duration(&formatted).unwrap();
2838 let expected = std::time::Duration::try_from(sdur).unwrap();
2839 assert_eq!(dur, expected);
2840 }
2841
2842 #[test]
2843 fn using_sum() {
2844 let signed_durations = [
2845 SignedDuration::new(12, 600_000_000),
2846 SignedDuration::new(13, 400_000_000),
2847 ];
2848 let sum1: SignedDuration = signed_durations.iter().sum();
2849 let sum2: SignedDuration = signed_durations.into_iter().sum();
2850
2851 assert_eq!(sum1, SignedDuration::new(26, 0));
2852 assert_eq!(sum2, SignedDuration::new(26, 0));
2853 }
2854
2855 #[test]
2856 #[should_panic]
2857 fn using_sum_when_max_exceeds() {
2858 [
2859 SignedDuration::new(i64::MAX, 0),
2860 SignedDuration::new(0, 1_000_000_000),
2861 ]
2862 .iter()
2863 .sum::<SignedDuration>();
2864 }
2865
2866 /// Regression test for a case where this routine could panic, even though
2867 /// it is fallible and should never panic.
2868 ///
2869 /// This occurred when rounding the fractional part of f64 could result in
2870 /// a number of nanoseconds equivalent to 1 second. This was then fed to
2871 /// a `SignedDuration` constructor that expected no nanosecond overflow.
2872 /// And this triggered a panic in debug mode (and an incorrect result in
2873 /// release mode).
2874 ///
2875 /// See: https://github.com/BurntSushi/jiff/issues/324
2876 #[test]
2877 fn panic_try_from_secs_f64() {
2878 let sdur = SignedDuration::try_from_secs_f64(0.999999999999).unwrap();
2879 assert_eq!(sdur, SignedDuration::from_secs(1));
2880
2881 let sdur = SignedDuration::try_from_secs_f64(-0.999999999999).unwrap();
2882 assert_eq!(sdur, SignedDuration::from_secs(-1));
2883
2884 let max = 9223372036854775807.999999999f64;
2885 let sdur = SignedDuration::try_from_secs_f64(max).unwrap();
2886 assert_eq!(sdur, SignedDuration::new(9223372036854775807, 0));
2887
2888 let min = -9223372036854775808.999999999f64;
2889 let sdur = SignedDuration::try_from_secs_f64(min).unwrap();
2890 assert_eq!(sdur, SignedDuration::new(-9223372036854775808, 0));
2891 }
2892
2893 /// See `panic_try_from_secs_f32`.
2894 ///
2895 /// Although note that I could never get this to panic. Perhaps the
2896 /// particulars of f32 prevent the fractional part from rounding up to
2897 /// 1_000_000_000?
2898 #[test]
2899 fn panic_try_from_secs_f32() {
2900 let sdur = SignedDuration::try_from_secs_f32(0.999999999).unwrap();
2901 assert_eq!(sdur, SignedDuration::from_secs(1));
2902
2903 let sdur = SignedDuration::try_from_secs_f32(-0.999999999).unwrap();
2904 assert_eq!(sdur, SignedDuration::from_secs(-1));
2905
2906 // Indeed, this is why the above never panicked.
2907 let x: f32 = 1.0;
2908 let y: f32 = 0.999999999;
2909 assert_eq!(x, y);
2910 assert_eq!(y.fract(), 0.0f32);
2911 }
2912}