Skip to content

Commit 3e494fa

Browse files
Navaneeth YadamreddyNavaneeth Yadamreddy
authored andcommitted
date: skip '-' flag for composite strftime specifiers
GNU date treats composite strftime specifiers (%D, %F, %T, %r, %R, %c, %x, %X) as atomic: flags like '-' apply to the whole expansion, not to inner sub-fields. uutils let the '-' flag propagate into inner specifiers, producing e.g. '6/15/24' for %-D instead of '06/15/24'. Strip the '-' flag from composite specifiers before applying modifiers, so inner fields keep their default padding while width and other flags still work. Fixes #11657
1 parent 7d10e5b commit 3e494fa

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

src/uu/date/src/format_modifiers.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,25 @@ fn format_with_modifiers(
236236
let formatted = broken_down.to_string_with_config(config, &base_format)?;
237237

238238
if !parsed.flags.is_empty() || parsed.width.is_some() {
239-
let modified = apply_modifiers(&formatted, &parsed)?;
239+
// Strip `-` from composite specifiers (D, F, T, etc.) so
240+
// apply_modifiers does not remove inner leading zeros.
241+
let effective =
242+
if is_composite_specifier(parsed.spec) && parsed.flags.contains('-') {
243+
ParsedSpec {
244+
flags: &parsed.flags.replace('-', ""),
245+
width: parsed.width,
246+
spec: parsed.spec,
247+
len: parsed.len,
248+
}
249+
} else {
250+
ParsedSpec {
251+
flags: parsed.flags,
252+
width: parsed.width,
253+
spec: parsed.spec,
254+
len: parsed.len,
255+
}
256+
};
257+
let modified = apply_modifiers(&formatted, &effective)?;
240258
result.push_str(&modified);
241259
} else {
242260
result.push_str(&formatted);
@@ -256,6 +274,13 @@ fn format_with_modifiers(
256274
Ok(result)
257275
}
258276

277+
/// Returns true if the specifier is composite (multi-field, e.g. %D = %m/%d/%y).
278+
fn is_composite_specifier(spec: &str) -> bool {
279+
// strip leading colons (e.g. ":z" → "z")
280+
let s = spec.trim_start_matches(':');
281+
matches!(s, "D" | "F" | "T" | "r" | "R" | "c" | "x" | "X")
282+
}
283+
259284
/// Returns true if the specifier produces text output (default pad is space)
260285
/// rather than numeric output (default pad is zero).
261286
fn is_text_specifier(specifier: &str) -> bool {

tests/by-util/test_date.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3148,3 +3148,27 @@ fn test_nanoseconds_width_prefix_ignored_issue12001() {
31483148
// compare to 4 because of \n
31493149
assert_eq!(result.stdout().len(), 4);
31503150
}
3151+
3152+
// Regression test for https://github.com/uutils/coreutils/issues/11657
3153+
// strftime flags like `-` should not propagate into composite specifiers like %D
3154+
#[test]
3155+
fn test_date_format_composite_specifier_flags_issue11657() {
3156+
// GNU date treats %D as atomic — the `-` flag should NOT strip leading
3157+
// zeros from the month/day within the expansion of %D.
3158+
new_ucmd!()
3159+
.env("TZ", "UTC")
3160+
.arg("-d")
3161+
.arg("2024-06-15")
3162+
.arg("+%-D")
3163+
.succeeds()
3164+
.stdout_is("06/15/24\n");
3165+
3166+
// Same for %F (ISO date)
3167+
new_ucmd!()
3168+
.env("TZ", "UTC")
3169+
.arg("-d")
3170+
.arg("2024-01-05")
3171+
.arg("+%-F")
3172+
.succeeds()
3173+
.stdout_is("2024-01-05\n");
3174+
}

0 commit comments

Comments
 (0)