From dfd62976de6b7d52828973635484fcf426b99372 Mon Sep 17 00:00:00 2001 From: 0xSoftBoi Date: Sat, 16 May 2026 16:01:37 -0400 Subject: [PATCH] date: re-zone trailing TZ abbreviation input to local timezone (#12327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/uu/date/src/date.rs | 10 ++++++++-- tests/by-util/test_date.rs | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index ab7cbf680..9d25dbd58 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -853,11 +853,17 @@ fn try_parse_with_abbreviation>(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())); } } } diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index 05ba50696..f61c99cad 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -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")