date: re-zone trailing TZ abbreviation input to local timezone (#12327)

The dedicated abbreviation-aware path `try_parse_with_abbreviation` in
`src/uu/date/src/date.rs` returned a `Zoned` in the input abbreviation
timezone (e.g. `-05` for `EST`). The generic `parse_datetime` fallback
already re-zones to `now.time_zone()` for display, so identical inputs
took two divergent code paths and only one matched GNU `date`.

GNU treats a trailing TZ abbreviation as describing the *input*
timezone; the output is always re-zoned into the local zone (or UTC
under `-u`). For example, under `TZ=UTC`:

    $ date -d "2024-01-01 EST"        # GNU
    Mon Jan  1 05:00:00 UTC 2024

uutils previously emitted `Mon Jan  1 00:00:00 -05 2024`.

Convert the parsed instant to `now.time_zone()` before returning,
matching the fallback path and GNU’s behavior. `-u` still works
because `now` is already zoned to UTC in that case (date.rs:362),
and the final output stage still applies `with_time_zone(TimeZone::UTC)`
when `settings.utc` is set.

Un-ignores the existing regression test for this issue.

Fixes uutils/parse_datetime#281.
This commit is contained in:
0xSoftBoi
2026-05-16 16:01:37 -04:00
committed by GitHub
parent 597a42ab0c
commit dfd62976de
2 changed files with 12 additions and 4 deletions
+8 -2
View File
@@ -853,11 +853,17 @@ fn try_parse_with_abbreviation<S: AsRef<str>>(date_str: S, now: &Zoned) -> Optio
if let Some(tz) = tz {
let date_part = s.trim_end_matches(last_word).trim();
// Parse in the target timezone so "10:30 EDT" means 10:30 in EDT
// Parse in the target timezone so "10:30 EDT" means 10:30 in EDT.
if let Ok(parsed) = parse_datetime::parse_datetime_at_date(now.clone(), date_part) {
let dt = parsed.datetime();
if let Ok(zoned) = dt.to_zoned(tz) {
return Some(zoned);
// The trailing abbreviation only describes the *input*
// timezone. For display, re-zone to the system timezone
// (i.e. `now`'s zone, which is UTC under `-u`). This
// matches GNU `date` and keeps this path consistent
// with the generic `parse_datetime` fallback below,
// which already re-zones via `to_zoned(now.time_zone())`.
return Some(zoned.with_time_zone(now.time_zone().clone()));
}
}
}
+4 -2
View File
@@ -1873,10 +1873,12 @@ fn test_date_input_hhmm_ampm() {
}
#[test]
#[ignore = "https://github.com/uutils/parse_datetime/issues/281 — GNU date re-zones input with trailing TZ abbreviation (e.g. `2024-01-01 EST`) into the local TZ; uutils keeps the input TZ on output."]
fn test_date_input_trailing_tz_abbrev_rezones() {
// `TZ=UTC+1 date -d '2024-01-01 EST'` should display the instant in UTC+1
// (GNU: 04:00:00 UTC), not leave it in EST (uutils: 00:00:00 -05).
// (GNU: 04:00:00 -01:00), not leave it in EST (the pre-fix uutils
// behavior was 00:00:00 -05). The trailing abbreviation only specifies
// the input timezone; output should be re-zoned to local.
// Regression test for https://github.com/uutils/parse_datetime/issues/281.
new_ucmd!()
.env("LC_ALL", "C")
.env("TZ", "UTC+1")