Skip to content

Commit

Permalink
api: add new top-level SignedDuration type
Browse files Browse the repository at this point in the history
It is meant to be as close of a mimic of `std::time::Duration` as
possible, but signed instead of unsigned. In most cases, it's a drop-in
replacement.

This type fits more naturally with Jiff's signed `Span` type.

We call methods using this type "jiff_duration," which is a bit of a
mouthful. In `jiff 0.2`, we'll plan to drop the `jiff_` prefix and
remove the corresponding `std::time::Duration` methods. Instead, callers
can use `SignedDuration` and then convert to-and-from
`std::time::Duration` from there.
  • Loading branch information
BurntSushi committed Aug 9, 2024
1 parent 7853ead commit 2929067
Show file tree
Hide file tree
Showing 8 changed files with 2,848 additions and 99 deletions.
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
0.1.6 (TBD)
===========
This release includes a new top-level type, `SignedDuration`, that provides a
near exact replica of `std::time::Duration`, but signed. It is meant to provide
alternative APIs for working with durations at a lower level than what `Span`
provides, and to facilitate better integration with the standard library.

This marks an initial integration phase with `SignedDuration`. It is planned
to integrate it more with the datetime types. Currently, there are integrations
on `Timestamp` and `Span`, but more will be added in the future.

This release also includes a few deprecations.

Deprecations:

* `Timestamp::as_duration`: to be replaced with `SignedDuration` in `jiff 0.2`.
* `Timestamp::from_duration`: to be replaced with `SignedDuration` in
`jiff 0.2`.
* `Timestamp::from_signed_duration`: to be replaced with `SignedDuration` in
`jiff 0.2`.
* `Span::to_duration`: to be replaced with `SignedDuration` in `jiff 0.2`.

Basically, all of the above APIs either accept or return a
`std::time::Duration`. To avoid breaking chnages at this point, new methods
for `SignedDuration` were added. For example, `Timestamp::as_jiff_duration`.
In `jiff 0.2`, the above deprecated methods will be removed and replaced with
equivalent methods that accept or return a `SignedDuration` instead. Callers
can then convert between a `SignedDuration` and a `std::time::Duration` using
appropriate `TryFrom` trait implementations.

Enhancements:

* [#XXX](https://github.com/BurntSushi/jiff/issues/XXX):
Add new top-level `SignedDuration` type.


0.1.5 (2024-08-09)
==================
This release includes some improvements and bug fixes, particularly for Jiff's
Expand Down
91 changes: 90 additions & 1 deletion src/fmt/temporal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ use crate::{
fmt::Write,
span::Span,
tz::{Disambiguation, OffsetConflict, TimeZoneDatabase},
Timestamp, Zoned,
SignedDuration, Timestamp, Zoned,
};

mod parser;
Expand Down Expand Up @@ -1102,6 +1102,54 @@ impl SpanParser {
let span = parsed.into_full()?;
Ok(span)
}

/// Parse an ISO 8601 duration string into a [`SignedDuration`] value.
///
/// # Errors
///
/// This returns an error if the span string given is invalid or if it is
/// valid but can't be converted to a `SignedDuration`. This can occur
/// when the parsed time exceeds the minimum and maximum `SignedDuration`
/// values, or if there are any non-zero units greater than hours.
///
/// # Example
///
/// This shows a basic example of using this routine.
///
/// ```
/// use jiff::{fmt::temporal::SpanParser, SignedDuration};
///
/// static PARSER: SpanParser = SpanParser::new();
///
/// let duration = PARSER.parse_duration(b"PT48m")?;
/// assert_eq!(duration, SignedDuration::from_mins(48));
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// Note that unless you need to parse a span from a byte string,
/// at time of writing, there is no other advantage to using this
/// parser directly. It is likely more convenient to just use
/// the [`FromStr`](std::str::FromStr) trait implementation on
/// [`SignedDuration`]:
///
/// ```ignore
/// use jiff::SignedDuration;
///
/// let duration = "PT48m".parse::<SignedDuration>()?;
/// assert_eq!(duration, SignedDuration::from_mins(48));
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn parse_duration<I: AsRef<[u8]>>(
&self,
input: I,
) -> Result<SignedDuration, Error> {
let input = input.as_ref();
let parsed = self.p.parse_signed_duration(input)?;
let dur = parsed.into_full()?;
Ok(dur)
}
}

/// A printer for Temporal durations.
Expand Down Expand Up @@ -1203,6 +1251,47 @@ impl SpanPrinter {
) -> Result<(), Error> {
self.p.print_span(span, wtr)
}

/// Print a `SignedDuration` to the given writer.
///
/// This balances the units of the duration up to at most hours
/// automatically.
///
/// # Errors
///
/// This only returns an error when writing to the given [`Write`]
/// implementation would fail. Some such implementations, like for `String`
/// and `Vec<u8>`, never fail (unless memory allocation fails). In such
/// cases, it would be appropriate to call `unwrap()` on the result.
///
/// # Example
///
/// ```
/// use jiff::{fmt::temporal::SpanPrinter, SignedDuration};
///
/// const PRINTER: SpanPrinter = SpanPrinter::new();
///
/// let dur = SignedDuration::new(86_525, 123_000_789);
///
/// let mut buf = String::new();
/// // Printing to a `String` can never fail.
/// PRINTER.print_duration(&dur, &mut buf).unwrap();
/// assert_eq!(buf, "PT24h2m5.123000789s");
///
/// // Negative durations are supported.
/// buf.clear();
/// PRINTER.print_duration(&-dur, &mut buf).unwrap();
/// assert_eq!(buf, "-PT24h2m5.123000789s");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn print_duration<W: Write>(
&self,
duration: &SignedDuration,
wtr: W,
) -> Result<(), Error> {
self.p.print_duration(duration, wtr)
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 2929067

Please sign in to comment.