diff --git a/Cargo.lock b/Cargo.lock index 3cd7a493961..8a1579edc45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2135,13 +2135,12 @@ dependencies = [ [[package]] name = "parse_datetime" -version = "0.11.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b77d27257a460cefd73a54448e5f3fd4db224150baf6ca3e02eedf4eb2b3e9" +checksum = "77d45119ed61100f40b2389d8ed12e51ec869046d4279afbb5a7c73a4733be36" dependencies = [ - "chrono", + "jiff", "num-traits", - "regex", "winnow", ] @@ -4128,6 +4127,7 @@ dependencies = [ "clap", "filetime", "fluent", + "jiff", "parse_datetime", "thiserror 2.0.17", "uucore", diff --git a/Cargo.toml b/Cargo.toml index 56b7c742fde..311ad95c6cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -352,7 +352,7 @@ num-prime = "0.4.4" num-traits = "0.2.19" number_prefix = "0.4" onig = { version = "~6.5.1", default-features = false } -parse_datetime = "0.11.0" +parse_datetime = "0.13.0" phf = "0.13.1" phf_codegen = "0.13.1" platform-info = "2.0.3" diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index b7c7e85cf2e..1f8eeab9fbf 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1071,13 +1071,12 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "parse_datetime" -version = "0.11.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b77d27257a460cefd73a54448e5f3fd4db224150baf6ca3e02eedf4eb2b3e9" +checksum = "77d45119ed61100f40b2389d8ed12e51ec869046d4279afbb5a7c73a4733be36" dependencies = [ - "chrono", + "jiff", "num-traits", - "regex", "winnow", ] diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index e8a0af266bc..c3381dfbd2a 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore strtime ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes getres +// spell-checker:ignore strtime ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes getres AWST ACST AEST use clap::{Arg, ArgAction, Command}; use jiff::fmt::strtime; @@ -459,8 +459,9 @@ fn make_format_string(settings: &Settings) -> &str { /// - MST: Mountain Standard Time (US) preferred over Malaysia Standard Time /// - PST: Pacific Standard Time (US) - widely used abbreviation /// - GMT: Alias for UTC (universal) +/// - Australian timezones: AWST, ACST, AEST (cannot be dynamically discovered) /// -/// All other timezones (AWST, JST, CET, etc.) are dynamically resolved from IANA database. // spell-checker:disable-line +/// All other timezones (JST, CET, etc.) are dynamically resolved from IANA database. // spell-checker:disable-line static PREFERRED_TZ_MAPPINGS: &[(&str, &str)] = &[ // Universal (no ambiguity, but commonly used) ("UTC", "UTC"), @@ -475,7 +476,15 @@ static PREFERRED_TZ_MAPPINGS: &[(&str, &str)] = &[ ("EST", "America/New_York"), // Ambiguous: US vs Australia ("EDT", "America/New_York"), // Other highly ambiguous cases - ("IST", "Asia/Kolkata"), // Ambiguous: India vs Israel vs Ireland // spell-checker:disable-line + /* spell-checker: disable */ + ("IST", "Asia/Kolkata"), // Ambiguous: India vs Israel vs Ireland + // Australian timezones (cannot be discovered from IANA location names) + ("AWST", "Australia/Perth"), // Australian Western Standard Time + ("ACST", "Australia/Adelaide"), // Australian Central Standard Time + ("ACDT", "Australia/Adelaide"), // Australian Central Daylight Time + ("AEST", "Australia/Sydney"), // Australian Eastern Standard Time + ("AEDT", "Australia/Sydney"), // Australian Eastern Daylight Time + /* spell-checker: enable */ ]; /// Lazy-loaded timezone abbreviation lookup map built from IANA database. @@ -547,18 +556,15 @@ fn resolve_tz_abbreviation>(date_str: S) -> String { // Try to parse the date with UTC first to get timestamp let date_with_utc = format!("{date_part} +00:00"); if let Ok(parsed) = parse_datetime::parse_datetime(&date_with_utc) { - // Create timestamp from parsed date - if let Ok(ts) = Timestamp::new( - parsed.timestamp(), - parsed.timestamp_subsec_nanos() as i32, - ) { - // Get the offset for this specific timestamp in the target timezone - let zoned = ts.to_zoned(tz); - let offset_str = format!("{}", zoned.offset()); - - // Replace abbreviation with offset - return format!("{date_part} {offset_str}"); - } + // Get timestamp from parsed date (which is already a Zoned) + let ts = parsed.timestamp(); + + // Get the offset for this specific timestamp in the target timezone + let zoned = ts.to_zoned(tz); + let offset_str = format!("{}", zoned.offset()); + + // Replace abbreviation with offset + return format!("{date_part} {offset_str}"); } } } @@ -571,7 +577,13 @@ fn resolve_tz_abbreviation>(date_str: S) -> String { /// Parse a `String` into a `DateTime`. /// If it fails, return a tuple of the `String` along with its `ParseError`. -// TODO: Convert `parse_datetime` to jiff and remove wrapper from chrono to jiff structures. +/// +/// **Update for parse_datetime 0.13:** +/// - parse_datetime 0.11: returned `chrono::DateTime` → required conversion to `jiff::Zoned` +/// - parse_datetime 0.13: returns `jiff::Zoned` directly → no conversion needed +/// +/// This change was necessary to fix issue #8754 (parsing large second values like +/// "12345.123456789 seconds ago" which failed in 0.11 but works in 0.13). fn parse_date + Clone>( s: S, ) -> Result { @@ -580,12 +592,10 @@ fn parse_date + Clone>( match parse_datetime::parse_datetime(&resolved) { Ok(date) => { - let timestamp = - Timestamp::new(date.timestamp(), date.timestamp_subsec_nanos() as i32).unwrap(); - Ok(Zoned::new( - timestamp, - TimeZone::try_system().unwrap_or(TimeZone::UTC), - )) + // Convert to system timezone for display + // (parse_datetime 0.13 returns Zoned in the input's timezone) + let timestamp = date.timestamp(); + Ok(timestamp.to_zoned(TimeZone::try_system().unwrap_or(TimeZone::UTC))) } Err(e) => Err((s.as_ref().into(), e)), } diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index 55ac39bd89b..f5409ec7a5a 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -22,6 +22,7 @@ path = "src/touch.rs" filetime = { workspace = true } clap = { workspace = true } chrono = { workspace = true } +jiff = "0.2.15" parse_datetime = { workspace = true } thiserror = { workspace = true } uucore = { workspace = true, features = ["libc", "parser"] } diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 98d87e3367b..b1581f8f073 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -15,6 +15,7 @@ use chrono::{ use clap::builder::{PossibleValue, ValueParser}; use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command}; use filetime::{FileTime, set_file_times, set_symlink_file_times}; +use jiff::{Timestamp, Zoned}; use std::borrow::Cow; use std::ffi::OsString; use std::fs::{self, File}; @@ -637,7 +638,41 @@ fn parse_date(ref_time: DateTime, s: &str) -> Result