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}