date: use PosixCustom formatting for GNU-compatible output (#10245)

This commit is contained in:
Chris Dryden
2026-01-14 23:39:16 +01:00
committed by GitHub
parent f7b5f8f101
commit 450e7cfee9
2 changed files with 41 additions and 16 deletions
+15 -13
View File
@@ -8,7 +8,7 @@
mod locale;
use clap::{Arg, ArgAction, Command};
use jiff::fmt::strtime;
use jiff::fmt::strtime::{self, BrokenDownTime, Config, PosixCustom};
use jiff::tz::{TimeZone, TimeZoneDatabase};
use jiff::{Timestamp, Zoned};
use std::collections::HashMap;
@@ -431,21 +431,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut stdout = BufWriter::new(std::io::stdout().lock());
// Format all the dates
let config = Config::new().custom(PosixCustom::new()).lenient(true);
for date in dates {
match date {
// TODO: Switch to lenient formatting.
Ok(date) => match strtime::format(format_string, &date) {
Ok(s) => writeln!(stdout, "{s}").map_err(|e| {
USimpleError::new(1, translate!("date-error-write", "error" => e))
})?,
Err(e) => {
let _ = stdout.flush();
return Err(USimpleError::new(
1,
translate!("date-error-invalid-format", "format" => format_string, "error" => e),
));
Ok(date) => {
match BrokenDownTime::from(&date).to_string_with_config(&config, format_string) {
Ok(s) => writeln!(stdout, "{s}").map_err(|e| {
USimpleError::new(1, translate!("date-error-write", "error" => e))
})?,
Err(e) => {
let _ = stdout.flush();
return Err(USimpleError::new(
1,
translate!("date-error-invalid-format", "format" => format_string, "error" => e),
));
}
}
},
}
Err((input, _err)) => {
let _ = stdout.flush();
show!(USimpleError::new(
+26 -3
View File
@@ -480,9 +480,8 @@ fn test_date_set_valid_4() {
#[test]
fn test_invalid_format_string() {
let result = new_ucmd!().arg("+%!").fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: invalid format "));
// With lenient mode, invalid format sequences are output literally (like GNU date)
new_ucmd!().arg("+%!").succeeds().stdout_is("%!\n");
}
#[test]
@@ -1446,3 +1445,27 @@ fn test_date_locale_fr_french() {
"Output should include timezone information, got: {stdout}"
);
}
#[test]
fn test_date_posix_format_specifiers() {
let cases = [
// %r: 12-hour time with zero-padded hour (08:17:48 AM, not 8:17:48 AM)
("%r", "08:17:48 AM"),
// %x: locale date in MM/DD/YY format
("%x", "01/19/97"),
// %X: locale time in HH:MM:SS format
("%X", "08:17:48"),
// %:8z: invalid format (width between : and z) should output literally (lenient mode)
("%:8z", "%:8z"),
];
for (format, expected) in cases {
new_ucmd!()
.env("TZ", "UTC")
.arg("-d")
.arg("1997-01-19 08:17:48")
.arg(format!("+{format}"))
.succeeds()
.stdout_is(format!("{expected}\n"));
}
}