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