Expand description
Convenience routines for serializing Span
values.
These convenience routines exist because the Serialize
implementation for
Span
always uses the ISO 8601 duration format. These routines provide a
way to use the “friendly” format.
Only serialization routines are provided because a Span
’s Deserialize
implementation automatically handles both the ISO 8601 duration format and
the “friendly” format.
§Advice
The Serialize
implementation uses ISO 8601 because it is a widely
accepted interchange format for communicating durations. If you need to
inter-operate with other systems, it is almost certainly the correct choice.
The “friendly” format does not adhere to any universal specified format.
However, it is perhaps easier to read, and crucially, unambiguously
represents all components of a Span
faithfully. (In contrast, the ISO
8601 format always normalizes sub-second durations into fractional seconds,
which means durations like 1100ms
and 1s100ms
are alwasys considered
equivalent.)
§Available routines
A SpanPrinter
has a lot of different
configuration options. The convenience routines provided by this module
only cover a small space of those options since it isn’t feasible to
provide a convenience routine for every possible set of configuration
options.
While more convenience routines could be added (please file an issue), only
the most common or popular such routines can be feasibly added. So in the
case where a convenience routine isn’t available for the configuration you
want, you can very easily define your own serialize_with
routine.
The recommended approach is to define a function and a type that
implements the std::fmt::Display
trait. This way, if a serializer can
efficiently support Display
implementations, then an allocation can be
avoided.
use jiff::{Span, ToSpan};
#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct Data {
#[serde(serialize_with = "custom_friendly")]
duration: Span,
}
let json = r#"{"duration": "1 year 2 months 36 hours 1100ms"}"#;
let got: Data = serde_json::from_str(&json).unwrap();
assert_eq!(
got.duration,
1.year().months(2).hours(36).milliseconds(1100).fieldwise(),
);
let expected = r#"{"duration":"1 year, 2 months, 36:00:01.100"}"#;
assert_eq!(serde_json::to_string(&got).unwrap(), expected);
fn custom_friendly<S: serde::Serializer>(
span: &Span,
se: S,
) -> Result<S::Ok, S::Error> {
struct Custom<'a>(&'a Span);
impl<'a> std::fmt::Display for Custom<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use jiff::fmt::{
friendly::{Designator, Spacing, SpanPrinter},
StdFmtWrite,
};
static PRINTER: SpanPrinter = SpanPrinter::new()
.designator(Designator::Verbose)
.comma_after_designator(true)
.spacing(Spacing::BetweenUnitsAndDesignators)
.hours_minutes_seconds(true)
.precision(Some(3));
PRINTER
.print_span(self.0, StdFmtWrite(f))
.map_err(|_| core::fmt::Error)
}
}
se.collect_str(&Custom(span))
}
Recall from above that you only need a custom serialization routine for this. Namely, deserialization automatically supports parsing all configuration options for serialization unconditionally.
Modules§
- Serialize a
Span
in thefriendly
duration format.