From dac35129d2f600c30372360695bece6b7fe013b5 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 12 Jan 2025 20:02:01 -0500 Subject: [PATCH 1/2] date: add dependencies for managing time zones Add dependencies on third-party packages `chrono-tz` and `iana-time-zone` to our `date` package. Together, these two packages allow us to produce time zone abbreviations (like `UTC`) from numeric timezone offsets. --- Cargo.lock | 33 +++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/uu/date/Cargo.toml | 2 ++ 3 files changed, 37 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c3f84e90be4..b564af6c407 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -302,6 +302,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "chrono-tz" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -1582,6 +1604,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + [[package]] name = "parse_datetime" version = "0.6.0" @@ -2590,7 +2621,9 @@ name = "uu_date" version = "0.0.29" dependencies = [ "chrono", + "chrono-tz", "clap", + "iana-time-zone", "libc", "parse_datetime", "uucore", diff --git a/Cargo.toml b/Cargo.toml index 8fdb15d5d6f..2fb991d5705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -280,6 +280,8 @@ chrono = { version = "0.4.38", default-features = false, features = [ ] } clap = { version = "4.5", features = ["wrap_help", "cargo"] } clap_complete = "4.4" +chrono-tz = "0.8.3" +iana-time-zone = "0.1.57" clap_mangen = "0.2" compare = "0.1.0" coz = { version = "0.1.3" } diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index 75766507655..87e8d383a75 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -22,6 +22,8 @@ chrono = { workspace = true } clap = { workspace = true } uucore = { workspace = true } parse_datetime = { workspace = true } +chrono-tz = { workspace = true } +iana-time-zone = { workspace = true } [target.'cfg(unix)'.dependencies] libc = { workspace = true } From d82d038b5475a14d2210925bac9d949774a4531c Mon Sep 17 00:00:00 2001 From: Krishna Nagam <40730166+KrishnaNagam@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:02:31 -0500 Subject: [PATCH 2/2] date: display %Z alphabetic time zone abbreviation Improve the display of dates formatted with the `%Z` specifier so that the timezone abbreviation is displayed, not just its numeric offset. Fixes #3756 Co-authored-by: Jeffrey Finkelstein --- src/uu/date/src/date.rs | 21 ++++++++++++++++++--- tests/by-util/test_date.rs | 16 ++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 766e79bd497..d91f8f82c71 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -6,10 +6,12 @@ // spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes use chrono::format::{Item, StrftimeItems}; -use chrono::{DateTime, FixedOffset, Local, Offset, TimeDelta, Utc}; +use chrono::{DateTime, FixedOffset, Local, Offset, TimeDelta, TimeZone, Utc}; #[cfg(windows)] use chrono::{Datelike, Timelike}; +use chrono_tz::{OffsetName, Tz}; use clap::{crate_version, Arg, ArgAction, Command}; +use iana_time_zone::get_timezone; #[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))] use libc::{clock_settime, timespec, CLOCK_REALTIME}; use std::fs::File; @@ -272,8 +274,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { for date in dates { match date { Ok(date) => { + // TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970 + let tz = match std::env::var("TZ") { + // TODO Support other time zones... + Ok(s) if s == "UTC0" => Tz::Etc__UTC, + _ => match get_timezone() { + Ok(tz_str) => tz_str.parse().unwrap(), + Err(_) => Tz::Etc__UTC, + }, + }; + let offset = tz.offset_from_utc_date(&Utc::now().date_naive()); + let tz_abbreviation = offset.abbreviation(); // GNU `date` uses `%N` for nano seconds, however crate::chrono uses `%f` - let format_string = &format_string.replace("%N", "%f"); + let format_string = &format_string + .replace("%N", "%f") + .replace("%Z", tz_abbreviation); // Refuse to pass this string to chrono as it is crashing in this crate if format_string.contains("%#z") { return Err(USimpleError::new( @@ -403,7 +418,7 @@ fn make_format_string(settings: &Settings) -> &str { Rfc3339Format::Ns => "%F %T.%f%:z", }, Format::Custom(ref fmt) => fmt, - Format::Default => "%c", + Format::Default => "%a %b %e %X %Z %Y", } } diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index 553414af853..d0a9c09a201 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -144,11 +144,12 @@ fn test_date_utc() { #[test] fn test_date_utc_issue_6495() { new_ucmd!() + .env("TZ", "UTC0") .arg("-u") .arg("-d") .arg("@0") .succeeds() - .stdout_is("Thu Jan 1 00:00:00 1970\n"); + .stdout_is("Thu Jan 1 00:00:00 UTC 1970\n"); } #[test] @@ -423,16 +424,18 @@ fn test_invalid_date_string() { #[test] fn test_date_one_digit_date() { new_ucmd!() + .env("TZ", "UTC0") .arg("-d") .arg("2000-1-1") .succeeds() - .stdout_contains("Sat Jan 1 00:00:00 2000"); + .stdout_only("Sat Jan 1 00:00:00 UTC 2000\n"); new_ucmd!() + .env("TZ", "UTC0") .arg("-d") .arg("2000-1-4") .succeeds() - .stdout_contains("Tue Jan 4 00:00:00 2000"); + .stdout_only("Tue Jan 4 00:00:00 UTC 2000\n"); } #[test] @@ -464,6 +467,7 @@ fn test_date_parse_from_format() { #[test] fn test_date_from_stdin() { new_ucmd!() + .env("TZ", "UTC0") .arg("-f") .arg("-") .pipe_in( @@ -473,8 +477,8 @@ fn test_date_from_stdin() { ) .succeeds() .stdout_is( - "Mon Mar 27 08:30:00 2023\n\ - Sat Apr 1 12:00:00 2023\n\ - Sat Apr 15 18:30:00 2023\n", + "Mon Mar 27 08:30:00 UTC 2023\n\ + Sat Apr 1 12:00:00 UTC 2023\n\ + Sat Apr 15 18:30:00 UTC 2023\n", ); }