Skip to main content

jiff/
signed_duration.rs

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