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