Skip to content

date: reject a trailing timezone when the input already has one - #14168

Open
ARMeeru wants to merge 1 commit into
uutils:mainfrom
ARMeeru:fix/date-reject-conflicting-timezones
Open

date: reject a trailing timezone when the input already has one#14168
ARMeeru wants to merge 1 commit into
uutils:mainfrom
ARMeeru:fix/date-reject-conflicting-timezones

Conversation

@ARMeeru

@ARMeeru ARMeeru commented Aug 27, 2026

Copy link
Copy Markdown

Fixes #13865.

date accepted two inputs that GNU rejects:

$ date -d 'Jan 23 6:00PM GMT-1 EST'
Fri Jan 23 23:00:00 UTC 2026
$ date -d '023-060 MEST'
Wed Aug 26 21:00:00 UTC 2026     # today's date

Root cause

It is not parse_datetime. The crate rejects both strings, both at the pinned 0.15.0 and on its main branch. I checked by calling it directly rather than through date.

try_parse_with_abbreviation strips a trailing timezone abbreviation and re-parses what is left, so that the abbreviation can be applied as the input timezone. It already guarded against a second timezone, but only by checking whether the last remaining word was itself a bare abbreviation. That catches EST PST and nothing else. GMT-1 fails the shape check because of the hyphen and 023-060 fails it because of the digits, so both were rescued after the standard parser had correctly rejected them. In the second case 023-060 parses on its own as 23:00 with a -01:00 offset, which is where the surprising today's-date output came from.

The same hole let @0 EST through, and a timestamp cannot take a timezone at all.

The change

Ask parse_datetime whether what is left can still take a timezone, by appending one it already understands. If it cannot, the remainder either carries timezone information of its own or cannot accept one, so the rescue is declined and the whole string falls through to the standard parser, which rejects it.

This keeps the offset grammar in one place. The alternative was to recognise offset forms textually in date.rs, which means maintaining a second copy of that grammar, and a rule broad enough to catch 023-060 also matches the -15 in 2024-01-15.

Deleting this rescue path was the other option, and it does not work: parse_datetime 0.15.0 rejects MEZ, MESZ, MEST and KST, which GNU accepts, and it rejects the Australian abbreviations that tests/by-util/test_date.rs deliberately covers. All of those still parse.

Tests

test_date_rejects_input_that_cannot_take_a_timezone covers the two inputs from the issue plus 2024-01-15 12:00 EST EST and @0 EST. test_date_accepts_gnu_timezone_abbreviations pins the abbreviations that must keep working, including the four that only this path handles. The first one fails before the change. The second passes before and after, and is there to catch a regression.

Verification

Differential testing against GNU coreutils 9.1 over 66 inputs, comparing this branch, its base commit, and GNU. There is no input where this branch disagrees with GNU while the base agreed. Everything that changed moved from accepted to rejected, matching GNU each time, and that includes 2024-01-15T12:00Z EST, which is not one of the reported cases.

The date suite goes from 142 to 144 passing with the same five failures before and after. Those five are test_date_set_valid* and test_date_for_no_permission_file, which fail in my container because setting the clock needs a capability it does not have. cargo fmt --check and cargo clippy --all-targets -- -D warnings are clean.

Every GNU behaviour referenced here was measured by running GNU date, not by reading its source.

Left alone

Three things I noticed while working on this, all separate from the reported bug and none of them touched here:

  • IANA location names are accepted as abbreviations, so date -d '2024-01-15 12:00 OSLO' works. build_tz_abbrev_map derives them from zone-name suffixes. GNU rejects them.
  • parse_datetime returns offsets that differ from GNU for ADT, AST, BST, GST and SST. Wrong times rather than errors, and the ambiguity looks like a decision for maintainers.
  • POSIX TZ strings are rejected: TZ="EST5EDT" 2024-06-01 12:00 gives 16:00 under GNU and fails here. TZ="America/New_York" works, so it is specific to that form.

Happy to file these separately if they are worth tracking.

@codspeed-hq

codspeed-hq Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 10 untouched benchmarks
⏩ 398 skipped benchmarks1


Comparing ARMeeru:fix/date-reject-conflicting-timezones (9693b5d) with main (1a6fb19)2

Open in CodSpeed

Footnotes

  1. 398 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (7d10e5b) during the generation of this report, so 1a6fb19 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skipping an intermittent issue tests/tail/retry (passes in this run but fails in the 'main' branch)

@ARMeeru

ARMeeru commented Aug 27, 2026

Copy link
Copy Markdown
Author

Reviewing the failures.

`try_parse_with_abbreviation` strips a trailing timezone abbreviation and
re-parses what is left, which let it accept strings the parser had already
rejected. Its guard only caught a remaining bare abbreviation such as
"EST PST", so anything else carrying zone information slipped past it:

    date -d "Jan 23 6:00PM GMT-1 EST"    # an offset and a zone
    date -d "023-060 MEST"               # a time with an offset, and a zone
    date -d "@0 EST"                     # a timestamp cannot take a zone

GNU date rejects all of these. We accepted them, and the "023-060" case
quietly answered with today's date.

Rather than reimplement the offset grammar here, read the answer off the
parse this path already performs. parse_datetime hands back the zone the
input named, or the current zone when it named none, so a mismatch says the
remainder carries its own. A leading "@" is tested directly, since a
timestamp cannot take a zone at all. The parser is consulted a second time
only when the remainder does name a zone whose offset coincides with the
current one, which the comparison cannot see on its own. The common case
stays at a single parse, and that matters because -f runs this once per line.

Input that carries zone information of its own, or cannot take one at all,
now falls through to the standard parser and is rejected.

The abbreviations this path exists for are unaffected, including the
Australian ones that GNU does not support. Checked against GNU coreutils
9.11 over the conflicting forms in several timezones, and over 300 inputs
GNU accepts, none of which this change rejects.

Closes uutils#13865
@ARMeeru
ARMeeru force-pushed the fix/date-reject-conflicting-timezones branch from 04781ae to 9693b5d Compare August 27, 2026 07:32
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.

bug(date): Invalid dates accepted, while gnu reject them (Part 2)

1 participant