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}