mirror of
https://github.com/BurntSushi/jiff.git
synced 2025-12-23 08:47:45 +00:00
fmt: add table of different formats supported by Jiff
I think someone asked for this when `jiff 0.1` was released, and I thought it was a good idea then. But I hadn't gotten around to it. With the number of formats Jiff supports having expanded a bit since then, I think now is a good time to get this documented.
This commit is contained in:
parent
65bac81c84
commit
c587fddbb7
2 changed files with 195 additions and 14 deletions
158
src/fmt/mod.rs
158
src/fmt/mod.rs
|
|
@ -5,6 +5,164 @@ Note that for most use cases, you should be using the corresponding
|
|||
[`Display`](std::fmt::Display) or [`FromStr`](std::str::FromStr) trait
|
||||
implementations for printing and parsing respectively. The APIs in this module
|
||||
provide more configurable support for printing and parsing.
|
||||
|
||||
# Tables of examples
|
||||
|
||||
The tables below attempt to show some examples of datetime and duration
|
||||
formatting, along with names and links to relevant routines and types. The
|
||||
point of these tables is to give a general overview of the formatting and
|
||||
parsing functionality in these sub-modules.
|
||||
|
||||
## Support for `FromStr` and `Display`
|
||||
|
||||
This table lists the formats supported by the [`FromStr`] and [`Display`]
|
||||
trait implementations on the datetime and duration types in Jiff.
|
||||
|
||||
In all of these cases, the trait implementations are mere conveniences for
|
||||
functionality provided by the [`temporal`] sub-module (and, in a couple cases,
|
||||
the [`friendly`] sub-module). The sub-modules provide lower level control
|
||||
(such as parsing from `&[u8]`) and more configuration (such as controlling the
|
||||
disambiguation strategy used when parsing zoned datetime [RFC-9557] strings).
|
||||
|
||||
| Example | Format | Links |
|
||||
| ------- | ------ | ----- |
|
||||
| `2025-08-20T17:35:00Z` | [RFC-3339] | [`Timestamp`] |
|
||||
| `2025-08-20T17:35:00-05` | [RFC-3339] | [`FromStr`] impl and<br>[`Timestamp::display_with_offset`] |
|
||||
| `2025-08-20T17:35:00+02[Poland]` | [RFC-9557] | [`Zoned`] |
|
||||
| `2025-08-20T17:35:00+02:00[+02:00]` | [RFC-9557] | [`Zoned`] |
|
||||
| `2025-08-20T17:35:00` | [ISO-8601] | [`civil::DateTime`] |
|
||||
| `2025-08-20` | [ISO-8601] | [`civil::Date`] |
|
||||
| `17:35:00` | [ISO-8601] | [`civil::Time`] |
|
||||
| `P1Y2M3W4DT5H6M7S` | [ISO-8601], [Temporal] | [`Span`] |
|
||||
| `PT1H2M3S` | [ISO-8601] | [`SignedDuration`], [`Span`] |
|
||||
| `PT1H2M3.123456789S` | [ISO-8601] | [`SignedDuration`], [`Span`] |
|
||||
| `1d 2h 3m 5s` | [`friendly`] | [`FromStr`] impl and alternative [`Display`]<br>via `{:#}` for [`SignedDuration`], [`Span`] |
|
||||
|
||||
Note that for datetimes like `2025-08-20T17:35:00`, the following variants are
|
||||
also accepted:
|
||||
|
||||
```text
|
||||
2025-08-20 17:35:00
|
||||
2025-08-20T17:35:00.123456789
|
||||
2025-08-20T17:35
|
||||
2025-08-20T17
|
||||
```
|
||||
|
||||
This applies to RFC 3339 and RFC 9557 timestamps as well.
|
||||
|
||||
Also, for ISO 8601 durations, the unit designator labels are matched
|
||||
case insensitively. For example, `PT1h2m3s` is recognized by Jiff.
|
||||
|
||||
## The "friendly" duration format
|
||||
|
||||
This table lists a few examples of the [`friendly`] duration format. Briefly,
|
||||
it is a bespoke format for Jiff, but is meant to match similar bespoke formats
|
||||
used elsewhere and be easier to read than the standard ISO 8601 duration
|
||||
format.
|
||||
|
||||
All examples below can be parsed via a [`Span`]'s [`FromStr`] trait
|
||||
implementation. All examples with units no bigger than hours can be parsed via
|
||||
a [`SignedDuration`]'s [`FromStr`] trait implementation. This table otherwise
|
||||
shows the options for printing durations in the format shown.
|
||||
|
||||
| Example | Print configuration |
|
||||
| ------- | ------------------- |
|
||||
| `1year 2months` | [`Designator::Verbose`] via [`SpanPrinter::designator`] |
|
||||
| `1yr 2mos` | [`Designator::Short`] via [`SpanPrinter::designator`] |
|
||||
| `1y 2mo` | [`Designator::Compact`] via [`SpanPrinter::designator`] (default) |
|
||||
| `1h2m3s` | [`Spacing::None`] via [`SpanPrinter::spacing`] |
|
||||
| `1h 2m 3s` | [`Spacing::BetweenUnits`] via [`SpanPrinter::spacing`] (default) |
|
||||
| `1 h 2 m 3 s` | [`Spacing::BetweenUnitsAndDesignators`] via [`SpanPrinter::spacing`] |
|
||||
| `2d 3h ago` | [`Direction::Auto`] via [`SpanPrinter::direction`] (default) |
|
||||
| `-2d 3h` | [`Direction::Sign`] via [`SpanPrinter::direction`] |
|
||||
| `+2d 3h` | [`Direction::ForceSign`] via [`SpanPrinter::direction`] |
|
||||
| `2d 3h ago` | [`Direction::Suffix`] via [`SpanPrinter::direction`] |
|
||||
| `9.123456789s` | [`FractionalUnit::Second`] via [`SpanPrinter::fractional`] |
|
||||
| `1y, 2mo` | [`SpanPrinter::comma_after_designator`] |
|
||||
| `15d 02:59:15.123` | [`SpanPrinter::hours_minutes_seconds`] |
|
||||
|
||||
## Bespoke datetime formats via `strptime` and `strftime`
|
||||
|
||||
Every datetime type has bespoke formatting routines defined on it. For
|
||||
example, [`Zoned::strptime`] and [`civil::Date::strftime`]. Additionally, the
|
||||
[`strtime`] sub-module also provides convenience routines, [`strtime::format`]
|
||||
and [`strtime::parse`], where the former is generic over any datetime type in
|
||||
Jiff and the latter provides a [`BrokenDownTime`] for granular parsing.
|
||||
|
||||
| Example | Format string |
|
||||
| ------- | ------------- |
|
||||
| `2025-05-20` | `%Y-%m-%d` |
|
||||
| `2025-05-20` | `%F` |
|
||||
| `2025-W21-2` | `%G-W%V-%w` |
|
||||
| `05/20/25` | `%m/%d/%y` |
|
||||
| `Monday, February 10, 2025 at 9:01pm -0500` | `%A, %B %d, %Y at %-I:%M%P %z` |
|
||||
| `Monday, February 10, 2025 at 9:01pm EST` | `%A, %B %d, %Y at %-I:%M%P %Z` |
|
||||
| `Monday, February 10, 2025 at 9:01pm America/New_York` | `%A, %B %d, %Y at %-I:%M%P %Q` |
|
||||
|
||||
The specific conversion specifiers supported are documented in the [`strtime`]
|
||||
sub-module. While precise POSIX compatibility is not guaranteed, the conversion
|
||||
specifiers are generally meant to match prevailing implementations. (Although
|
||||
there are many such implementations and they each tend to have their own quirks
|
||||
and features.)
|
||||
|
||||
## RFC 2822 parsing and printing
|
||||
|
||||
[RFC-2822] support is provided by the [`rfc2822`] sub-module.
|
||||
|
||||
| Example | Links |
|
||||
| ------- | ----- |
|
||||
| `Thu, 29 Feb 2024 05:34 -0500` | [`rfc2822::parse`] and [`rfc2822::to_string`] |
|
||||
| `Thu, 01 Jan 1970 00:00:01 GMT` | [`DateTimePrinter::timestamp_to_rfc9110_string`] |
|
||||
|
||||
[Temporal]: https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
|
||||
[ISO-8601]: https://www.iso.org/iso-8601-date-and-time-format.html
|
||||
[RFC-3339]: https://www.rfc-editor.org/rfc/rfc3339
|
||||
[RFC-9557]: https://www.rfc-editor.org/rfc/rfc9557.html
|
||||
[ISO-8601]: https://www.iso.org/iso-8601-date-and-time-format.html
|
||||
[RFC-2822]: https://datatracker.ietf.org/doc/html/rfc2822
|
||||
[RFC-9110]: https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.7-15
|
||||
[`Display`]: std::fmt::Display
|
||||
[`FromStr`]: std::str::FromStr
|
||||
[`friendly`]: crate::fmt::friendly
|
||||
[`temporal`]: crate::fmt::temporal
|
||||
[`rfc2822`]: crate::fmt::rfc2822
|
||||
[`strtime`]: crate::fmt::strtime
|
||||
[`civil::DateTime`]: crate::civil::DateTime
|
||||
[`civil::Date`]: crate::civil::Date
|
||||
[`civil::Date::strftime`]: crate::civil::Date::strftime
|
||||
[`civil::Time`]: crate::civil::Time
|
||||
[`SignedDuration`]: crate::SignedDuration
|
||||
[`Span`]: crate::Span
|
||||
[`Timestamp`]: crate::Timestamp
|
||||
[`Timestamp::display_with_offset`]: crate::Timestamp::display_with_offset
|
||||
[`Zoned`]: crate::Zoned
|
||||
[`Zoned::strptime`]: crate::Zoned::strptime
|
||||
|
||||
[`Designator::Verbose`]: crate::fmt::friendly::Designator::Verbose
|
||||
[`Designator::Short`]: crate::fmt::friendly::Designator::Short
|
||||
[`Designator::Compact`]: crate::fmt::friendly::Designator::Compact
|
||||
[`Spacing::None`]: crate::fmt::friendly::Spacing::None
|
||||
[`Spacing::BetweenUnits`]: crate::fmt::friendly::Spacing::BetweenUnits
|
||||
[`Spacing::BetweenUnitsAndDesignators`]: crate::fmt::friendly::Spacing::BetweenUnitsAndDesignators
|
||||
[`Direction::Auto`]: crate::fmt::friendly::Direction::Auto
|
||||
[`Direction::Sign`]: crate::fmt::friendly::Direction::Sign
|
||||
[`Direction::ForceSign`]: crate::fmt::friendly::Direction::ForceSign
|
||||
[`Direction::Suffix`]: crate::fmt::friendly::Direction::Suffix
|
||||
[`FractionalUnit::Second`]: crate::fmt::friendly::FractionalUnit::Second
|
||||
[`SpanPrinter::designator`]: crate::fmt::friendly::SpanPrinter::designator
|
||||
[`SpanPrinter::spacing`]: crate::fmt::friendly::SpanPrinter::spacing
|
||||
[`SpanPrinter::direction`]: crate::fmt::friendly::SpanPrinter::direction
|
||||
[`SpanPrinter::fractional`]: crate::fmt::friendly::SpanPrinter::fractional
|
||||
[`SpanPrinter::comma_after_designator`]: crate::fmt::friendly::SpanPrinter::comma_after_designator
|
||||
[`SpanPrinter::hours_minutes_seconds`]: crate::fmt::friendly::SpanPrinter::hours_minutes_seconds
|
||||
|
||||
[`BrokenDownTime`]: crate::fmt::strtime::BrokenDownTime
|
||||
[`strtime::parse`]: crate::fmt::strtime::parse
|
||||
[`strtime::format`]: crate::fmt::strtime::format
|
||||
|
||||
[`rfc2822::parse`]: crate::fmt::rfc2822::parse
|
||||
[`rfc2822::to_string`]: crate::fmt::rfc2822::to_string
|
||||
[`DateTimePrinter::timestamp_to_rfc9110_string`]: crate::fmt::rfc2822::DateTimePrinter::timestamp_to_rfc9110_string
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
|
|
|
|||
51
src/span.rs
51
src/span.rs
|
|
@ -102,11 +102,6 @@ pub(crate) use span_eq;
|
|||
///
|
||||
/// # Negative spans
|
||||
///
|
||||
/// **WARNING:** As of nightly Rust 2024-07-26, negating spans like
|
||||
/// `-2.hours()` triggers a deny-by-default lint due to an ambiguous negative
|
||||
/// literal. However, in Jiff's case, this is a false positive. Feel free to
|
||||
/// `allow` the lint or write the span as `(-2).hours()` or `-(2.hours())`.
|
||||
///
|
||||
/// A span may be negative. All of these are equivalent:
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -548,10 +543,37 @@ pub(crate) use span_eq;
|
|||
/// # Ok::<(), Box<dyn std::error::Error>>(())
|
||||
/// ```
|
||||
///
|
||||
/// For simplicity, weeks are always considered non-uniform. And generally
|
||||
/// speaking, weeks only appear in a `Span` if they were explicitly put there
|
||||
/// by the caller or if they were explicitly requested by the caller in an API.
|
||||
/// For example:
|
||||
/// The APIs on `Span` will otherwise treat days as non-uniform unless a
|
||||
/// relative civil date is given, or there is an explicit opt-in to invariant
|
||||
/// 24-hour days. For example:
|
||||
///
|
||||
/// ```
|
||||
/// use jiff::{civil, SpanRelativeTo, ToSpan, Unit};
|
||||
///
|
||||
/// let span = 1.day();
|
||||
///
|
||||
/// // An error because days aren't always 24 hours:
|
||||
/// assert_eq!(
|
||||
/// span.total(Unit::Hour).unwrap_err().to_string(),
|
||||
/// "using unit 'day' in a span or configuration requires that either \
|
||||
/// a relative reference time be given or \
|
||||
/// `SpanRelativeTo::days_are_24_hours()` is used to indicate \
|
||||
/// invariant 24-hour days, but neither were provided",
|
||||
/// );
|
||||
/// // Opt into invariant 24 hour days without a relative date:
|
||||
/// let marker = SpanRelativeTo::days_are_24_hours();
|
||||
/// let hours = span.total((Unit::Hour, marker))?;
|
||||
/// // Or use a relative civil date, and all days are 24 hours:
|
||||
/// let date = civil::date(2020, 1, 1);
|
||||
/// let hours = span.total((Unit::Hour, date))?;
|
||||
/// assert_eq!(hours, 24.0);
|
||||
///
|
||||
/// # Ok::<(), Box<dyn std::error::Error>>(())
|
||||
/// ```
|
||||
///
|
||||
/// In Jiff, all weeks are 7 days. And generally speaking, weeks only appear in
|
||||
/// a `Span` if they were explicitly put there by the caller or if they were
|
||||
/// explicitly requested by the caller in an API. For example:
|
||||
///
|
||||
/// ```
|
||||
/// use jiff::{civil::date, ToSpan, Unit};
|
||||
|
|
@ -629,20 +651,21 @@ pub(crate) use span_eq;
|
|||
///
|
||||
/// Note that an error will occur when converting a `Span` to a
|
||||
/// `std::time::Duration` using the `TryFrom` trait implementation with units
|
||||
/// bigger than days:
|
||||
/// bigger than hours:
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// use jiff::ToSpan;
|
||||
///
|
||||
/// let span = 2.months().hours(10);
|
||||
/// let span = 2.days().hours(10);
|
||||
/// assert_eq!(
|
||||
/// Duration::try_from(span).unwrap_err().to_string(),
|
||||
/// "failed to convert span to duration without relative datetime \
|
||||
/// (must use `Span::to_duration` instead): using unit 'month' in a \
|
||||
/// span or configuration requires that a relative reference time \
|
||||
/// be given, but none was provided",
|
||||
/// (must use `Span::to_duration` instead): using unit 'day' in a \
|
||||
/// span or configuration requires that either a relative reference \
|
||||
/// time be given or `SpanRelativeTo::days_are_24_hours()` is used \
|
||||
/// to indicate invariant 24-hour days, but neither were provided",
|
||||
/// );
|
||||
///
|
||||
/// # Ok::<(), Box<dyn std::error::Error>>(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue