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