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