Struct SpanPrinter

struct SpanPrinter { ... }

A printer for Jiff's "friendly" duration format.

This printer provides a lot of different knobs for controlling how durations are formatted. It supports formatting both SignedDuration and Span.

Example: automatic use through Display

The default configuration of this printer is used for "alternate" display formatting for both SignedDuration and [Span]:

use jiff::{SignedDuration, ToSpan};

let span = 1.year().months(2).hours(15).seconds(30).nanoseconds(1);
assert_eq!(format!("{span:#}"), "1y 2mo 15h 30s 1ns");

let sdur = SignedDuration::new(15 * 60 * 60 + 30, 1);
assert_eq!(format!("{sdur:#}"), "15h 30s 1ns");

Example: variety of formatting configurations

This example shows a few different ways of formatting the same Span:

use jiff::{
    fmt::friendly::{Designator, Spacing, SpanPrinter},
    ToSpan,
};

let span = 1.year().months(2).hours(15).seconds(30).nanoseconds(1);

let printer = SpanPrinter::new();
assert_eq!(
    printer.span_to_string(&span),
    "1y 2mo 15h 30s 1ns",
);

let printer = SpanPrinter::new()
    .designator(Designator::Short);
assert_eq!(
    printer.span_to_string(&span),
    "1yr 2mos 15hrs 30secs 1nsec",
);

let printer = SpanPrinter::new()
    .spacing(Spacing::None)
    .designator(Designator::Compact);
assert_eq!(
    printer.span_to_string(&span),
    "1y2mo15h30s1ns",
);

let printer = SpanPrinter::new()
    .spacing(Spacing::BetweenUnitsAndDesignators)
    .comma_after_designator(true)
    .designator(Designator::Verbose);
assert_eq!(
    printer.span_to_string(&span),
    "1 year, 2 months, 15 hours, 30 seconds, 1 nanosecond",
);

let printer = SpanPrinter::new()
    .hours_minutes_seconds(true)
    .spacing(Spacing::BetweenUnitsAndDesignators)
    .comma_after_designator(true)
    .designator(Designator::Verbose);
assert_eq!(
    printer.span_to_string(&span),
    "1 year, 2 months, 15:00:30.000000001",
);

Example: negative durations

By default, a negative duration will be represented with an ago suffix:

use jiff::{fmt::friendly::SpanPrinter, ToSpan};

let span = -1.year().months(2).hours(15).seconds(30).nanoseconds(1);

let printer = SpanPrinter::new();
assert_eq!(
    printer.span_to_string(&span),
    "1y 2mo 15h 30s 1ns ago",
);

But one can also use a prefix - sign instead. Usually this works better without any spacing and compact designators:

use jiff::{fmt::friendly::{Designator, Spacing, SpanPrinter}, ToSpan};

let span = -1.year().months(2).hours(15).seconds(30).nanoseconds(1);

let printer = SpanPrinter::new()
    .spacing(Spacing::None)
    .designator(Designator::Compact);
assert_eq!(
    printer.span_to_string(&span),
    "-1y2mo15h30s1ns",
);

Implementations

impl SpanPrinter

const fn new() -> SpanPrinter

Creates a new printer for the "friendly" duration format.

The printer returned uses the default configuration. This is identical to SpanPrinter::default, but it can be used in a const context.

Example

This example shows how to format a duration directly to a Vec<u8>.

use jiff::{fmt::friendly::SpanPrinter, ToSpan};

static PRINTER: SpanPrinter = SpanPrinter::new();

let span = 1.year().months(2);
let mut buf = vec![];
// Writing to a `Vec<u8>` never fails (aside from OOM).
PRINTER.print_span(&span, &mut buf).unwrap();
assert_eq!(buf, b"1y 2mo");
const fn designator(self: Self, designator: Designator) -> SpanPrinter

Configures the kind of unit designators to use.

There are no specific advantages or disadvantages to the kind of designator you pick other than aesthetic preference. Shorter designators are also likely faster to parse and print.

The default is Designator::Compact, which uses things like yr instead of year (verbose) or y (compact).

Example

use jiff::{
    fmt::friendly::{Designator, SpanPrinter},
    ToSpan,
};

let span = 1.year().months(2);

let printer = SpanPrinter::new();
assert_eq!(printer.span_to_string(&span), "1y 2mo");

let printer = SpanPrinter::new().designator(Designator::Short);
assert_eq!(printer.span_to_string(&span), "1yr 2mos");

let printer = SpanPrinter::new().designator(Designator::Verbose);
assert_eq!(printer.span_to_string(&span), "1year 2months");
const fn spacing(self: Self, spacing: Spacing) -> SpanPrinter

Configures the spacing between the units and the designator labels.

The default is Spacing::BetweenUnits, which results in durations like 1y 2mo. Spacing::None would result in 1y2mo and Spacing::BetweenUnitsAndDesignators would result in 1 y 2 mo.

Example

use jiff::{
    fmt::friendly::{Designator, Spacing, SpanPrinter},
    ToSpan,
};

let span = 1.year().months(2);

// The default tries to balance spacing with compact
// unit designators.
let printer = SpanPrinter::new();
assert_eq!(printer.span_to_string(&span), "1y 2mo");

// But you can use slightly more descriptive
// designators without being too verbose.
let printer = SpanPrinter::new()
    .designator(Designator::Short);
assert_eq!(printer.span_to_string(&span), "1yr 2mos");

// When spacing is removed, it usually looks nicer
// to use compact unit designators.
let printer = SpanPrinter::new()
    .spacing(Spacing::None)
    .designator(Designator::Compact);
assert_eq!(printer.span_to_string(&span), "1y2mo");

// Conversely, when using more spacing, it usually
// looks nicer to use verbose unit designators.
let printer = SpanPrinter::new()
    .spacing(Spacing::BetweenUnitsAndDesignators)
    .designator(Designator::Verbose);
assert_eq!(printer.span_to_string(&span), "1 year 2 months");

Example: Spacing::None can still result in whitespace

In the case that SpanPrinter::hours_minutes_seconds is enabled and one is formatting a span with non-zero calendar units, then an ASCII whitespace is inserted between the calendar and non-calendar units even when Spacing::None is used:

use jiff::{fmt::friendly::{Spacing, SpanPrinter}, ToSpan};

let span = 1.year().months(2).hours(15);

let printer = SpanPrinter::new()
    .spacing(Spacing::None)
    .hours_minutes_seconds(true);
assert_eq!(printer.span_to_string(&span), "1y2mo 15:00:00");
const fn direction(self: Self, direction: Direction) -> SpanPrinter

Configures how and when the sign for the duration is written.

The default is Direction::Auto. In most cases, this results in writing the suffix ago after printing the duration units when the sign of the duration is negative. And when the sign is positive, there is no suffix. However, this can vary based on other settings. For example, when SpanPrinter::spacing is set to Spacing::None, then Direction::Auto is treated as if it were Direction::Sign.

Example

use jiff::{fmt::friendly::{Direction, SpanPrinter}, SignedDuration};

let duration = SignedDuration::from_secs(-1);

let printer = SpanPrinter::new();
assert_eq!(printer.duration_to_string(&duration), "1s ago");

let printer = SpanPrinter::new().direction(Direction::Sign);
assert_eq!(printer.duration_to_string(&duration), "-1s");
const fn fractional(self: Self, unit: Option<FractionalUnit>) -> SpanPrinter

Enable fractional formatting for the given unit.

When SpanPrinter::hours_minutes_seconds is enabled, then this setting is automatically set to FractionalUnit::Second. Otherwise, it defaults to None, which means no fractions are ever written.

Example

This example shows how to write the same duration with different fractional settings:

use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};

let duration = SignedDuration::from_secs(3663);

let printer = SpanPrinter::new()
    .fractional(Some(FractionalUnit::Hour));
assert_eq!(printer.duration_to_string(&duration), "1.0175h");

let printer = SpanPrinter::new()
    .fractional(Some(FractionalUnit::Minute));
assert_eq!(printer.duration_to_string(&duration), "1h 1.05m");

let printer = SpanPrinter::new()
    .fractional(Some(FractionalUnit::Second));
assert_eq!(printer.duration_to_string(&duration), "1h 1m 3s");

Example: precision loss

Because the "friendly" format is limited to 9 decimal places, when using FractionalUnit::Hour or FractionalUnit::Minute, it is possible for precision loss to occur.

use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};

// one nanosecond
let duration = SignedDuration::new(0, 1);

let printer = SpanPrinter::new()
    .fractional(Some(FractionalUnit::Hour));
assert_eq!(printer.duration_to_string(&duration), "0h");

let printer = SpanPrinter::new()
    .fractional(Some(FractionalUnit::Minute));
assert_eq!(printer.duration_to_string(&duration), "0m");
const fn comma_after_designator(self: Self, yes: bool) -> SpanPrinter

When enabled, commas are written after unit designators.

This is disabled by default.

Example

use jiff::{fmt::friendly::{Designator, Spacing, SpanPrinter}, ToSpan};

static PRINTER: SpanPrinter = SpanPrinter::new()
    .designator(Designator::Verbose)
    .spacing(Spacing::BetweenUnitsAndDesignators)
    .comma_after_designator(true);

let span = 5.years().months(3).milliseconds(123);
assert_eq!(
    PRINTER.span_to_string(&span),
    "5 years, 3 months, 123 milliseconds",
);
const fn hours_minutes_seconds(self: Self, yes: bool) -> SpanPrinter

Formats the span or duration into a HH:MM:SS[.fffffffff] format.

When formatting a Span with non-zero calendar units (units of days or greater), then the calendar units are formatted as typical with their corresponding designators. For example, 1d 01:00:00. Note that when formatting a SignedDuration, calendar units are never used.

When this is enabled, many of the other options are either ignored or fixed to a specific setting:

  • Since this format does not use any unit designators for units of hours or smaller, the SpanPrinter::designator setting is ignored for hours or smaller. It is still used when formatting a Span with non-zero calendar units.
  • SpanPrinter::spacing setting is ignored for units of hours or smaller.
  • The SpanPrinter::fractional setting is forcefully set to FractionalUnit::Second. It cannot be changed.
  • The SpanPrinter::comma_after_designator setting is ignored for units of hours or smaller.
  • When the padding is not specified, it defaults to 2 for hours, minutes and seconds and 0 for any calendar units present.
  • The precision setting is respected as documented.

This format is useful in contexts for interfacing with existing systems that require this style of format, or if the HH:MM:SS is just in general preferred.

Loss of fidelity

When using this format with a Span, sub-second units are formatted as a fractional second. This means that 1000 milliseconds and 1 second format to precisely the same string. This is similar to the loss of fidelity when using fmt::temporal to format spans in the ISO 8601 duration format.

Example

This shows how to format a Span in HH:MM:SS format:

use jiff::{fmt::friendly::SpanPrinter, ToSpan};

static PRINTER: SpanPrinter =
    SpanPrinter::new().hours_minutes_seconds(true);

let span = 2.hours().minutes(59).seconds(15).milliseconds(123);
assert_eq!(PRINTER.span_to_string(&span), "02:59:15.123");
assert_eq!(PRINTER.span_to_string(&-span), "-02:59:15.123");

// This shows what happens with calendar units.
let span = 15.days().hours(2).minutes(59).seconds(15).milliseconds(123);
assert_eq!(PRINTER.span_to_string(&span), "15d 02:59:15.123");
// Notice that because calendar units are specified and the sign
// setting is set to "auto" by default, it has switched to a suffix.
assert_eq!(PRINTER.span_to_string(&-span), "15d 02:59:15.123 ago");

And this shows the same, but with a [SignedDuration]:

use jiff::{fmt::friendly::SpanPrinter, SignedDuration};

static PRINTER: SpanPrinter =
    SpanPrinter::new().hours_minutes_seconds(true);

let duration = SignedDuration::new(
    2 * 60 * 60 + 59 * 60 + 15,
    123_000_000,
);
assert_eq!(PRINTER.duration_to_string(&duration), "02:59:15.123");
assert_eq!(PRINTER.duration_to_string(&-duration), "-02:59:15.123");

Example: Span versus SignedDuration

The main advantage of a Span is that, except for fractional components, the unit values emitted correspond precisely to the values in the Span. Where as for a SignedDuration, the units are always computed from a single absolute duration in a way that is always balanced:

use jiff::{fmt::friendly::SpanPrinter, SignedDuration, ToSpan};

static PRINTER: SpanPrinter =
    SpanPrinter::new().hours_minutes_seconds(true);

let span = 120.minutes();
assert_eq!(PRINTER.span_to_string(&span), "00:120:00");

let duration = SignedDuration::from_mins(120);
assert_eq!(PRINTER.duration_to_string(&duration), "02:00:00");

Of course, a balanced duration is sometimes what you want. But Span affords the flexibility of controlling precisely what the unit values are.

const fn padding(self: Self, digits: u8) -> SpanPrinter

The padding to use when writing unit values.

If a unit value has fewer digits than specified here, it is padded to the left with zeroes. (To control precision, i.e., padding to the right when writing fractional values, use SpanPrinter::precision.)

By default, when writing in the hours-minutes-seconds format, a padding of 2 is used for units of hours, minutes and seconds. Otherwise, a padding of 0 is used.

Example

This shows some examples of configuring padding when writing in default format with unit designators:

use jiff::{fmt::friendly::SpanPrinter, ToSpan};

let printer = SpanPrinter::new();
assert_eq!(printer.span_to_string(&1.hour()), "1h");
let printer = SpanPrinter::new().padding(3);
assert_eq!(printer.span_to_string(&1.hour()), "001h");

And this shows some examples with the hours-minutes-seconds format. Notice how padding is enabled by default.

use jiff::{fmt::friendly::SpanPrinter, ToSpan};

let printer = SpanPrinter::new().hours_minutes_seconds(true);
assert_eq!(printer.span_to_string(&1.hour()), "01:00:00");
let printer = SpanPrinter::new().hours_minutes_seconds(true).padding(0);
assert_eq!(printer.span_to_string(&1.hour()), "1:0:0");

// In this case, under the default configuration, the padding
// for calendar units is 0 but the padding for time units is 2.
let printer = SpanPrinter::new().hours_minutes_seconds(true);
assert_eq!(printer.span_to_string(&1.day().hours(1)), "1d 01:00:00");
const fn precision(self: Self, precision: Option<u8>) -> SpanPrinter

The precision to use when writing fractional unit values.

This setting has no effect if fractional formatting isn't enabled. Fractional formatting is only enabled when SpanPrinter::fractional is set or if SpanPrinter::hours_minutes_seconds are enabled. Neither are enabled by default.

A precision of Some(0) implies that truncation of any fractional component always occurs.

The default value is None, which means the precision is automatically determined from the value. If no fractional component is needed, then none will be printed.

Example

use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, ToSpan};

// No effect, because fractions aren't enabled.
let printer = SpanPrinter::new().precision(Some(2));
assert_eq!(printer.span_to_string(&1.hour()), "1h");

// Precision setting takes effect!
let printer = SpanPrinter::new()
    .precision(Some(2))
    .fractional(Some(FractionalUnit::Hour));
assert_eq!(printer.span_to_string(&1.hour()), "1.00h");

// The HH:MM:SS format automatically enables fractional
// second values.
let printer = SpanPrinter::new()
    // Truncate to millisecond precision.
    .precision(Some(3))
    .hours_minutes_seconds(true);
let span = 1.second().milliseconds(1).microseconds(1).nanoseconds(1);
assert_eq!(printer.span_to_string(&span), "00:00:01.001");

// Same as above, but with the designator or "expanded"
// format. This requires explicitly enabling fractional
// units.
let printer = SpanPrinter::new()
    // Truncate to millisecond precision.
    .precision(Some(3))
    .fractional(Some(FractionalUnit::Second));
let span = 1.second().milliseconds(1).microseconds(1).nanoseconds(1);
assert_eq!(printer.span_to_string(&span), "1.001s");
const fn zero_unit(self: Self, unit: Unit) -> SpanPrinter

Sets the unit to use when printing a duration that is zero.

When SpanPrinter::fractional is set, then this setting is ignored and the zero unit corresponds to the fractional unit specified.

This defaults to Unit::Second.

Example

use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, ToSpan, Unit};

// The default just always uses seconds.
let printer = SpanPrinter::new();
assert_eq!(printer.span_to_string(&0.years()), "0s");

// We can set our own unit.
let printer = SpanPrinter::new().zero_unit(Unit::Year);
assert_eq!(printer.span_to_string(&0.years()), "0y");

// But it's overridden if fractional units are set.
let printer = SpanPrinter::new()
    .zero_unit(Unit::Year)
    .fractional(Some(FractionalUnit::Minute));
assert_eq!(printer.span_to_string(&0.years()), "0m");

// One use case for this option is if you're rounding
// spans and want the zero unit to reflect the smallest
// unit you're using.
let printer = SpanPrinter::new().zero_unit(Unit::Minute);
let span = 5.hours().minutes(30).seconds(59);
let rounded = span.round(Unit::Minute)?;
assert_eq!(printer.span_to_string(&rounded), "5h 31m");

let span = 5.seconds();
let rounded = span.round(Unit::Minute)?;
assert_eq!(printer.span_to_string(&rounded), "0m");

# Ok::<(), Box<dyn std::error::Error>>(())

The same applies for SignedDuration:

use jiff::{fmt::friendly::SpanPrinter, SignedDuration, Unit};

// The default just always uses seconds.
let printer = SpanPrinter::new();
assert_eq!(printer.duration_to_string(&SignedDuration::ZERO), "0s");

// We can set our own unit.
let printer = SpanPrinter::new().zero_unit(Unit::Minute);
assert_eq!(printer.duration_to_string(&SignedDuration::ZERO), "0m");
fn span_to_string(self: &Self, span: &Span) -> String

Format a Span into a string using the "friendly" format.

This is a convenience routine for SpanPrinter::print_span with a String.

Example

use jiff::{fmt::friendly::SpanPrinter, ToSpan};

static PRINTER: SpanPrinter = SpanPrinter::new();

let span = 3.years().months(5);
assert_eq!(PRINTER.span_to_string(&span), "3y 5mo");
fn duration_to_string(self: &Self, duration: &SignedDuration) -> String

Format a SignedDuration into a string using the "friendly" format.

This balances the units of the duration up to at most hours automatically.

This is a convenience routine for SpanPrinter::print_duration with a String.

Example

use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};

static PRINTER: SpanPrinter = SpanPrinter::new();

let dur = SignedDuration::new(86_525, 123_000_789);
assert_eq!(
    PRINTER.duration_to_string(&dur),
    "24h 2m 5s 123ms 789ns",
);
assert_eq!(
    PRINTER.duration_to_string(&-dur),
    "24h 2m 5s 123ms 789ns ago",
);

// Or, if you prefer fractional seconds:
static PRINTER_FRACTIONAL: SpanPrinter = SpanPrinter::new()
    .fractional(Some(FractionalUnit::Second));
assert_eq!(
    PRINTER_FRACTIONAL.duration_to_string(&-dur),
    "24h 2m 5.123000789s ago",
);
fn print_span<W: Write>(self: &Self, span: &Span, wtr: W) -> Result<(), Error>

Print a Span to the given writer using the "friendly" format.

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::friendly::SpanPrinter, ToSpan};

static PRINTER: SpanPrinter = SpanPrinter::new();

let span = 3.years().months(5);

let mut buf = String::new();
// Printing to a `String` can never fail.
PRINTER.print_span(&span, &mut buf).unwrap();
assert_eq!(buf, "3y 5mo");
fn print_duration<W: Write>(self: &Self, duration: &SignedDuration, wtr: W) -> Result<(), Error>

Print a SignedDuration to the given writer using the "friendly" format.

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::friendly::SpanPrinter, SignedDuration};

static 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, "24h 2m 5s 123ms 789ns");

// Negative durations are supported.
buf.clear();
PRINTER.print_duration(&-dur, &mut buf).unwrap();
assert_eq!(buf, "24h 2m 5s 123ms 789ns ago");

impl Clone for SpanPrinter

fn clone(self: &Self) -> SpanPrinter

impl Debug for SpanPrinter

fn fmt(self: &Self, f: &mut Formatter<'_>) -> Result

impl Default for SpanPrinter

fn default() -> SpanPrinter

impl Freeze for SpanPrinter

impl RefUnwindSafe for SpanPrinter

impl Send for SpanPrinter

impl Sync for SpanPrinter

impl Unpin for SpanPrinter

impl UnsafeUnpin for SpanPrinter

impl UnwindSafe for SpanPrinter

impl<T> Any for SpanPrinter

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for SpanPrinter

fn borrow(self: &Self) -> &T

impl<T> BorrowMut for SpanPrinter

fn borrow_mut(self: &mut Self) -> &mut T

impl<T> CloneToUninit for SpanPrinter

unsafe fn clone_to_uninit(self: &Self, dest: *mut u8)

impl<T> From for SpanPrinter

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> ToOwned for SpanPrinter

fn to_owned(self: &Self) -> T
fn clone_into(self: &Self, target: &mut T)

impl<T, U> Into for SpanPrinter

fn into(self: Self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

impl<T, U> TryFrom for SpanPrinter

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

impl<T, U> TryInto for SpanPrinter

fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>