Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

temporal: use uppercase unit designator labels by default #191

Merged
merged 1 commit into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ Bug fixes:

* [#155](https://github.com/BurntSushi/jiff/issues/155):
Relax `strftime` format strings from ASCII-only to all of UTF-8.
* [#188](https://github.com/BurntSushi/jiff/issues/188):
`Span` and `SignedDuration` now use uppercase unit designator labels in their
default ISO 8601 `Display` implementation.


0.1.18 (2024-12-31)
Expand Down
2 changes: 1 addition & 1 deletion COMPARE.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ use jiff::{Span, ToSpan};
fn main() -> anyhow::Result<()> {
let span = 5.years().months(2).days(1).hours(20);
let json = serde_json::to_string_pretty(&span)?;
assert_eq!(json, "\"P5y2m1dT20h\"");
assert_eq!(json, "\"P5Y2M1DT20H\"");

let got: Span = serde_json::from_str(&json)?;
assert_eq!(got, span);
Expand Down
4 changes: 2 additions & 2 deletions src/civil/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1635,11 +1635,11 @@ impl Date {
///
/// // The default limits durations to using "days" as the biggest unit.
/// let span = d1.until(d2)?;
/// assert_eq!(span.to_string(), "P8456d");
/// assert_eq!(span.to_string(), "P8456D");
///
/// // But we can ask for units all the way up to years.
/// let span = d1.until((Unit::Year, d2))?;
/// assert_eq!(span.to_string(), "P23y1m24d");
/// assert_eq!(span.to_string(), "P23Y1M24D");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
Expand Down
4 changes: 2 additions & 2 deletions src/civil/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1775,11 +1775,11 @@ impl DateTime {
///
/// // The default limits durations to using "days" as the biggest unit.
/// let span = dt1.until(dt2)?;
/// assert_eq!(span.to_string(), "P8456dT12h5m29.9999965s");
/// assert_eq!(span.to_string(), "P8456DT12H5M29.9999965S");
///
/// // But we can ask for units all the way up to years.
/// let span = dt1.until((Unit::Year, dt2))?;
/// assert_eq!(span.to_string(), "P23y1m24dT12h5m29.9999965s");
/// assert_eq!(span.to_string(), "P23Y1M24DT12H5M29.9999965S");
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
Expand Down
4 changes: 2 additions & 2 deletions src/civil/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,12 +1198,12 @@ impl Time {
///
/// // The default limits spans to using "hours" as the biggest unit.
/// let span = t1.until(t2)?;
/// assert_eq!(span.to_string(), "PT12h5m29.9999965s");
/// assert_eq!(span.to_string(), "PT12H5M29.9999965S");
///
/// // But we can ask for smaller units, like capping the biggest unit
/// // to minutes instead of hours.
/// let span = t1.until((Unit::Minute, t2))?;
/// assert_eq!(span.to_string(), "PT725m29.9999965s");
/// assert_eq!(span.to_string(), "PT725M29.9999965S");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
Expand Down
13 changes: 7 additions & 6 deletions src/fmt/friendly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ format when using the `std::fmt::Display` trait implementation:
use jiff::{SignedDuration, ToSpan};

let span = 2.months().days(35).hours(2).minutes(30);
assert_eq!(format!("{span}"), "P2m35dT2h30m"); // ISO 8601
assert_eq!(format!("{span}"), "P2M35DT2H30M"); // ISO 8601
assert_eq!(format!("{span:#}"), "2mo 35d 2h 30m"); // "friendly"

let sdur = SignedDuration::new(2 * 60 * 60 + 30 * 60, 123_456_789);
assert_eq!(format!("{sdur}"), "PT2h30m0.123456789s"); // ISO 8601
assert_eq!(format!("{sdur}"), "PT2H30M0.123456789S"); // ISO 8601
assert_eq!(format!("{sdur:#}"), "2h 30m 123ms 456µs 789ns"); // "friendly"
```

Expand Down Expand Up @@ -467,10 +467,11 @@ P1Y2M3DT4H59M1.1S
P1y2m3dT4h59m1.1S
```

When all of the unit designators are capital letters in particular, everything
runs together and it's hard for the eye to distinguish where digits stop and
letters begin. Using lowercase letters for unit designators helps somewhat,
but this is an extension to ISO 8601 that isn't broadly supported.
When all of the unit designators are capital letters in particular (which
is the default), everything runs together and it's hard for the eye to
distinguish where digits stop and letters begin. Using lowercase letters for
unit designators helps somewhat, but this is an extension to ISO 8601 that
isn't broadly supported.

The "friendly" format resolves both of these problems by permitting sub-second
components and allowing the use of whitespace and longer unit designator labels
Expand Down
214 changes: 107 additions & 107 deletions src/fmt/friendly/parser.rs

Large diffs are not rendered by default.

45 changes: 36 additions & 9 deletions src/fmt/temporal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ 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 the
`P` and `T` designators, but lowercase the unit designators.
* 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
Expand Down Expand Up @@ -1566,7 +1569,7 @@ impl SpanParser {
/// let mut buf = vec![];
/// // Printing to a `Vec<u8>` can never fail.
/// PRINTER.print_span(&span, &mut buf).unwrap();
/// assert_eq!(buf, "PT48m".as_bytes());
/// assert_eq!(buf, "PT48M".as_bytes());
/// ```
///
/// # Example: using adapters with `std::io::Write` and `std::fmt::Write`
Expand Down Expand Up @@ -1605,6 +1608,30 @@ impl SpanPrinter {
SpanPrinter { p: printer::SpanPrinter::new() }
}

/// Use lowercase for unit designator labels.
///
/// By default, unit designator labels are written in uppercase.
///
/// # Example
///
/// This shows the difference between the default (uppercase) and enabling
/// lowercase. Lowercase unit designator labels tend to be easier to read
/// (in this author's opinion), but they aren't as broadly supported since
/// they are an extension to ISO 8601.
///
/// ```
/// use jiff::{fmt::temporal::SpanPrinter, ToSpan};
///
/// let span = 5.years().days(10).hours(1);
/// let printer = SpanPrinter::new();
/// assert_eq!(printer.span_to_string(&span), "P5Y10DT1H");
/// assert_eq!(printer.lowercase(true).span_to_string(&span), "P5y10dT1h");
/// ```
#[inline]
pub const fn lowercase(self, yes: bool) -> SpanPrinter {
SpanPrinter { p: self.p.lowercase(yes) }
}

/// Format a `Span` into a string.
///
/// This is a convenience routine for [`SpanPrinter::print_span`] with
Expand All @@ -1618,7 +1645,7 @@ impl SpanPrinter {
/// const PRINTER: SpanPrinter = SpanPrinter::new();
///
/// let span = 3.years().months(5);
/// assert_eq!(PRINTER.span_to_string(&span), "P3y5m");
/// assert_eq!(PRINTER.span_to_string(&span), "P3Y5M");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
Expand Down Expand Up @@ -1646,8 +1673,8 @@ impl SpanPrinter {
/// const PRINTER: SpanPrinter = SpanPrinter::new();
///
/// let dur = SignedDuration::new(86_525, 123_000_789);
/// assert_eq!(PRINTER.duration_to_string(&dur), "PT24h2m5.123000789s");
/// assert_eq!(PRINTER.duration_to_string(&-dur), "-PT24h2m5.123000789s");
/// assert_eq!(PRINTER.duration_to_string(&dur), "PT24H2M5.123000789S");
/// assert_eq!(PRINTER.duration_to_string(&-dur), "-PT24H2M5.123000789S");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
Expand Down Expand Up @@ -1683,7 +1710,7 @@ impl SpanPrinter {
/// let mut buf = String::new();
/// // Printing to a `String` can never fail.
/// PRINTER.print_span(&span, &mut buf).unwrap();
/// assert_eq!(buf, "P3y5m");
/// assert_eq!(buf, "P3Y5M");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
Expand Down Expand Up @@ -1719,12 +1746,12 @@ impl SpanPrinter {
/// let mut buf = String::new();
/// // Printing to a `String` can never fail.
/// PRINTER.print_duration(&dur, &mut buf).unwrap();
/// assert_eq!(buf, "PT24h2m5.123000789s");
/// assert_eq!(buf, "PT24H2M5.123000789S");
///
/// // Negative durations are supported.
/// buf.clear();
/// PRINTER.print_duration(&-dur, &mut buf).unwrap();
/// assert_eq!(buf, "-PT24h2m5.123000789s");
/// assert_eq!(buf, "-PT24H2M5.123000789S");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
Expand Down
Loading
Loading