devela::_dep::jiff::fmt

Module temporal

Available on crate features dep_jiff and alloc only.
Expand description

A hybrid format derived from RFC 3339, RFC 9557 and ISO 8601.

This module provides an implementation of the Temporal ISO 8601 grammar. The API is spread out over parsers and printers for datetimes and spans.

Note that for most use cases, you should be using the corresponding Display or FromStr trait implementations for printing and parsing respectively. This module provides a “lower level” API for configuring the behavior of printing and parsing, including the ability to parse from byte strings (i.e., &[u8]).

§Date and time format

The specific format supported depends on what kind of type you’re trying to parse into. Here are some examples to give a general idea:

  • 02:21:58 parses into a civil::Time.
  • 2020-08-21 parses into a civil::Date.
  • 2020-08-21T02:21:58 and 2020-08-21 02:21:58 both parse into a civil::DateTime.
  • 2020-08-21T02:21:58-04 parses into an Timestamp.
  • 2020-08-21T02:21:58-04[America/New_York] parses into a Zoned.

Smaller types can generally be parsed from strings representing a bigger type. For example, a civil::Date can be parsed from 2020-08-21T02:21:58.

As mentioned above, the datetime format supported by Jiff is a hybrid of the “best” parts of RFC 3339, RFC 9557 and ISO 8601. Generally speaking, RFC 3339 and RFC 9557 are supported in their entirety, but not all of ISO 8601 is. For example, 2024-06-16T10.5 is a valid ISO 8601 datetime, but isn’t supported by Jiff. (Only fractional seconds are supported.)

Some additional details worth noting:

  • Parsing Zoned values requires a datetime string with a time zone annotation like [America/New_York] or [-07:00]. If you need to parse a datetime without a time zone annotation (but with an offset), then you should parse it as an Timestamp. From there, it can be converted to a Zoned via Timestamp::to_zoned.
  • When parsing Zoned values, ambiguous datetimes are handled via the DateTimeParser::disambiguation configuration. By default, a “compatible” mode is used where the earlier time is selected in a backward transition, while the later time is selected in a forward transition.
  • When parsing Zoned values, conflicts between the offset and the time zone in the datetime string are handled via the DateTimeParser::offset_conflict configuration. By default, any inconsistency between the offset and the time zone results in a parse error.
  • When parsing civil types like civil::DateTime, it’s always an error if the datetime string has a Z (Zulu) offset. It’s an error since interpreting such strings as civil time is usually a bug.
  • In all cases, the T designator separating the date and time may be an ASCII space instead.

The complete datetime format supported is described by the Temporal ISO 8601 grammar.

§Span format

To a first approximation, the span format supported roughly corresponds to this regular expression:

P(\d+y)?(\d+m)?(\d+w)?(\d+d)?(T(\d+h)?(\d+m)?(\d+s)?)?

But there are some details not easily captured by a simple regular expression:

  • At least one unit must be specified. To write a zero span, specify 0 for any unit. For example, P0d and PT0s are equivalent.
  • The format is case insensitive. The printer will by default capitalize all designators, but the unit designators can be configured to use lowercase with SpanPrinter::lowercase. For example, P3y1m10dT5h instead of P3Y1M10DT5H. You might prefer lowercase since you may find it easier to read. However, it is an extension to ISO 8601 and isn’t as broadly supported.
  • Hours, minutes or seconds may be fractional. And the only units that may be fractional are the lowest units.
  • A span like P99999999999y is invalid because it exceeds the allowable range of time representable by a Span.

This is, roughly speaking, a subset of what ISO 8601 specifies. It isn’t strictly speaking a subset since Jiff (like Temporal) permits week units to be mixed with other units.

Here are some examples:

use jiff::{Span, ToSpan};

let spans = [
    ("P40D", 40.days()),
    ("P1y1d", 1.year().days(1)),
    ("P3dT4h59m", 3.days().hours(4).minutes(59)),
    ("PT2H30M", 2.hours().minutes(30)),
    ("P1m", 1.month()),
    ("P1w", 1.week()),
    ("P1w4d", 1.week().days(4)),
    ("PT1m", 1.minute()),
    ("PT0.0021s", 2.milliseconds().microseconds(100)),
    ("PT0s", 0.seconds()),
    ("P0d", 0.seconds()),
    (
        "P1y1m1dT1h1m1.1s",
        1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
    ),
];
for (string, span) in spans {
    let parsed: Span = string.parse()?;
    assert_eq!(span, parsed, "result of parsing {string:?}");
}

One can also parse ISO 8601 durations into a SignedDuration, but units are limited to hours or smaller:

use jiff::SignedDuration;

let durations = [
    ("PT2H30M", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
    ("PT2.5h", SignedDuration::from_secs(2 * 60 * 60 + 30 * 60)),
    ("PT1m", SignedDuration::from_mins(1)),
    ("PT1.5m", SignedDuration::from_secs(90)),
    ("PT0.0021s", SignedDuration::new(0, 2_100_000)),
    ("PT0s", SignedDuration::ZERO),
    ("PT0.000000001s", SignedDuration::from_nanos(1)),
];
for (string, duration) in durations {
    let parsed: SignedDuration = string.parse()?;
    assert_eq!(duration, parsed, "result of parsing {string:?}");
}

The complete span format supported is described by the Temporal ISO 8601 grammar.

§Differences with Temporal

Jiff implements Temporal’s grammar pretty closely, but there are a few differences at the time of writing. It is a specific goal that all differences should be rooted in what Jiff itself supports, and not in the grammar itself.

  • The maximum UTC offset value expressible is 25:59:59 in Jiff, where as in Temporal it’s 23:59:59.999999999. Jiff supports a slightly bigger maximum to account for all valid values of POSIX time zone strings. Jiff also lacks nanosecond precision for UTC offsets, as it’s not clear how useful that is in practice.
  • Jiff doesn’t support a datetime range as big as Temporal. For example, in Temporal, +202024-06-14T17:30[America/New_York] is valid. But in Jiff, since the maximum supported year is 9999, parsing will fail. Jiff’s datetime range may be expanded in the future, but it is a non-goal to match Temporal’s range precisely.
  • Jiff doesn’t support RFC 9557 calendar annotations because Jiff only supports the Gregorian calendar.

There is some more background on Temporal’s format available.

Structs§

Enums§