Skip to content

fix(date): treat composite strftime specifiers as atomic#11752

Open
Abusalah0 wants to merge 3 commits intouutils:mainfrom
Abusalah0:date-composite-strftime-atomic-modifiers
Open

fix(date): treat composite strftime specifiers as atomic#11752
Abusalah0 wants to merge 3 commits intouutils:mainfrom
Abusalah0:date-composite-strftime-atomic-modifiers

Conversation

@Abusalah0
Copy link
Copy Markdown

Fixes #11657

Summary

GNU date treats composite strftime directives (such as %D, %F, %T, %r, %R, %c, %x, %X) as atomic units. Formatting modifiers should apply to the full rendered result, not propagate into inner sub-fields.

Previously, uutils expanded composite directives (e.g. %D → %m/%d/%y) and applied modifiers to each inner specifier, leading to incorrect output such as:
+%-D → 6/15/24

where GNU correctly produces:
+%-D → 06/15/24

Fix

The fix is implemented in src/date/format/format_modifiers.rs:

  • Added composite directive detection to identify %D, %F, %T, %r, %R, %c, %x, %X
  • Updated modifier handling logic to treat composite outputs as atomic values
  • Prevented modifier propagation (e.g. -, 0, width adjustments) into expanded sub-fields
  • Preserved width and padding behavior at the top level

Tests

Changes in tests/by-util/test_date.rs:

  • Enabled the regression test provided in this issue
  • Added additional coverage for composite directives
  • Added a dedicated %+10D test to verify width and padding behavior

@github-actions
Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/tail/retry. tests/tail/retry is passing on 'main'. Maybe you have to rebase?
Skip an intermittent issue tests/date/resolution (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tty/tty-eof (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/tail/tail-n0f (passes in this run but fails in the 'main' branch)

matches!(
specifier.chars().last(),
Some('A' | 'a' | 'B' | 'b' | 'h' | 'Z' | 'p' | 'P' | 'e' | 'k' | 'l')
Some(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new matches! arm now lists 18 letters across many lines. Since every composite is also space-padded by default, you can express
that relationship directly:


  fn is_space_padded_specifier(specifier: &str) -> bool {
      is_atomic_composite_specifier(specifier)
          || matches!(
              specifier.chars().last(),
              Some('A' | 'a' | 'B' | 'b' | 'h' | 'Z' | 'p' | 'P' | 'e' | 'k' | 'l')
          )
  }

no ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes Thank you,, I Updated it as suggested.


#[test]
fn test_date_strftime_composite_modifiers_are_atomic() {
let test_cases = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no tests for, e.g.:

  • %^c → SAT JUN 15 03:04:05 2024
  • %#c → swap-cased composite
  • %^D → no-op on 06/15/24 (still 06/15/24)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added tests for these Cases.

@github-actions
Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/cut/bounded-memory (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tty/tty-eof (fails in this run but passes in the 'main' branch)
Congrats! The gnu test tests/tail/tail-n0f is now passing!

@Abusalah0 Abusalah0 requested a review from sylvestre April 13, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

date: strftime flags propagate into composite specifiers (%-D, %-F, …)

2 participants