Skip to content

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

Closed

Description

@sylvestre

Summary

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 lets the - flag propagate into the inner %m, producing 6/15/24 instead of 06/15/24.

Found by a fuzz_date run.

Reproduction

$ LC_ALL=C TZ=UTC /usr/bin/date -d '2024-06-15' '+%-D'
06/15/24

$ LC_ALL=C TZ=UTC target/debug/coreutils date -d '2024-06-15' '+%-D'
6/15/24

Test

#[test]
fn test_date_strftime_flag_on_composite() {
    new_ucmd!()
        .env("LC_ALL", "C")
        .env("TZ", "UTC")
        .arg("-d")
        .arg("2024-06-15")
        .arg("+%-D")
        .succeeds()
        .stdout_is("06/15/24\n");
}

Activity

  1. THEMIScale commented on Apr 10, 2026

    @THEMIScale
    Contributor

    I'll try to work on it.

  2. Abusalah0 commented on Apr 10, 2026

    @Abusalah0

    Hey @THEMIScale , can I try to do this one if you have not started working on it yet?

  3. THEMIScale commented on Apr 10, 2026

    @THEMIScale
    Contributor

    @Abusalah0 sure, go ahead.

  4. Abusalah0 commented on Apr 10, 2026

    @Abusalah0

    Thanks @THEMIScale .

    I can reproduce this on main as described by @sylvestre :

    • GNU date with LC_ALL=C TZ=UTC and +%-D outputs: 06/15/24
    • uutils outputs: 6/15/24

    This appears to stem from how composite strftime directives are handled during modifier processing. GNU treats composite specifiers as atomic, applying modifiers to the fully rendered result, while uutils currently allows modifier logic to propagate into inner sub-fields.

    I’ve prepared a fix that treats composite directives (%D, %F, %T, %r, %R, %c, %x, %X) as atomic for modifier handling, while preserving top-level width and padding behavior.

    Additionally:

    • Enabled the regression test provided in this issue
    • Added broader regression coverage for composite specifiers
    • Included a dedicated %+10D parity test

    I’ll open a PR shortly for review.

  5. added 5 commits that reference this issue on Aug 27, 2026
    c055443
    b4d5b00
    3e494fa
    a283bca
    5167654
  6. SichenLiang commented on Sep 2, 2026

    @SichenLiang
    Contributor

    Upstream fixed this on Monday, and the result isn't quite "composites are
    atomic" — there's one exception.

    Pádraig took it to bug-gnulib after #339 on the coreutils mirror, and the fix
    went in as gnulib 91c129e9dbd8 on Aug 31:

    * lib/strftime.c: Only propagate flags to %F as that has the
    year at the start so our current "yearish" based flag propagation
    produces consistent results.  Specifically this avoids glibc
    '_' and '-' flags propagating to year sub specifiers of %D,
    producing inconsistent year and month/date formatting.
    

    Coreutils bumped the submodule the same day (15b8d8ab8287). So date -d 2005-03-04 +%-D, which gives 03/04/5 on 9.7 and 9.11, becomes 03/04/05 in
    the next release — but %F keeps propagating:

    $ gnudate -d 0001-01-01 +'[%F]'     [0001-01-01]
    $ gnudate -d 0001-01-01 +'[%0F]'    [1-01-01]
    

    Worth having on the radar for the PRs here: handling every composite the same
    way matches GNU for %D %T %R %r %x %X, but %F goes the other way.

    One thing that may save someone time: #339's tests landed as 85857f1eaa70 with
    the year moved from 2024 to 2004 — "that better tests the padding propagation to
    %y" (Pádraig, on the mirror PR). The #[ignore]d composite test in
    tests/by-util/test_date.rs is on 2024-06-15, where %y is 24 — nothing to
    strip, so it passes under either behaviour. The new coreutils date.pl has dates
    and cases that do discriminate.

    Measured on b7519db against GNU 9.7 (Ubuntu 26.04) and the 9.11 reference
    build, TZ=UTC LC_ALL=C.

  7. xtqqczze commented on Oct 13, 2026

    @xtqqczze
    Collaborator

    Confirmed that the issue is resolved by #15111.

    The following comparison demonstrates the behavior before and after the fix:

    $ git checkout a84f05d0aa6bc311a6b267f371d44b8aaab7ce92~1 
    HEAD is now at 99e8fbe9bd uucore: fix doc_markdown lints in entries
    $ LC_ALL=C TZ=UTC cargo run -q date -d '2024-06-15' '+%-D'
    6/15/24
    $ git checkout a84f05d0aa6bc311a6b267f371d44b8aaab7ce92   
    Previous HEAD position was 99e8fbe9bd uucore: fix doc_markdown lints in entries
    HEAD is now at a84f05d0aa date: expand GNU-compatible parsing and formatting
    $ LC_ALL=C TZ=UTC cargo run -q date -d '2024-06-15' '+%-D'
    06/15/24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions