rustmax::jiff::fmt::serde

Module duration

Source
Expand description

Convenience routines for serializing SignedDuration values.

These convenience routines exist because the Serialize implementation for SignedDuration always uses the ISO 8601 duration format. These routines provide a way to use the “friendly” format.

Only serialization routines are provided because a SignedDuration’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. Beyond that, its utility for SignedDuration is somewhat less compared to Span, since for Span, the friendly format preserves all components of the Span faithfully. But a SignedDuration is just a 96-bit integer of nanoseconds, so there are no individual components to preserve. Still, even with a SignedDuration, you might prefer the friendly format.

§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::SignedDuration;

#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct Data {
    #[serde(serialize_with = "custom_friendly")]
    duration: SignedDuration,
}

let json = r#"{"duration": "36 hours 1100ms"}"#;
let got: Data = serde_json::from_str(&json).unwrap();
assert_eq!(got.duration, SignedDuration::new(36 * 60 * 60 + 1, 100_000_000));

let expected = r#"{"duration":"36:00:01.100"}"#;
assert_eq!(serde_json::to_string(&got).unwrap(), expected);

fn custom_friendly<S: serde::Serializer>(
    duration: &SignedDuration,
    se: S,
) -> Result<S::Ok, S::Error> {
    struct Custom<'a>(&'a SignedDuration);

    impl<'a> std::fmt::Display for Custom<'a> {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            use jiff::fmt::{friendly::SpanPrinter, StdFmtWrite};

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

            PRINTER
                .print_duration(self.0, StdFmtWrite(f))
                .map_err(|_| core::fmt::Error)
        }
    }

    se.collect_str(&Custom(duration))
}

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§