jiff::fmt::friendly

Struct SpanPrinter

Source
pub struct SpanPrinter { /* private fields */ }
Expand description

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§

Source§

impl SpanPrinter

Source

pub 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");
Source

pub const fn designator(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");
Source

pub const fn spacing(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");
Source

pub const fn direction(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");
Source

pub const fn fractional(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");
Source

pub const fn comma_after_designator(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",
);
Source

pub const fn hours_minutes_seconds(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.

Source

pub const fn padding(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");
Source

pub const fn precision(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");
Source

pub const fn zero_unit(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");

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");
Source

pub fn span_to_string(&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");
Source

pub fn duration_to_string(&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",
);
Source

pub fn print_span<W: Write>(&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");
Source

pub fn print_duration<W: Write>( &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");

Trait Implementations§

Source§

impl Clone for SpanPrinter

Source§

fn clone(&self) -> SpanPrinter

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SpanPrinter

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Default for SpanPrinter

Source§

fn default() -> SpanPrinter

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

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

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

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

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

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

Performs the conversion.