Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
7 changes: 3 additions & 4 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 32 additions & 22 deletions src/uu/date/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"),
Expand All @@ -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.
Expand Down Expand Up @@ -547,18 +556,15 @@ fn resolve_tz_abbreviation<S: AsRef<str>>(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}");
}
}
}
Expand All @@ -571,7 +577,13 @@ fn resolve_tz_abbreviation<S: AsRef<str>>(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<S: AsRef<str> + Clone>(
s: S,
) -> Result<Zoned, (String, parse_datetime::ParseDateTimeError)> {
Expand All @@ -580,12 +592,10 @@ fn parse_date<S: AsRef<str> + 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)),
}
Expand Down
1 change: 1 addition & 0 deletions src/uu/touch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
37 changes: 36 additions & 1 deletion src/uu/touch/src/touch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -637,7 +638,41 @@ fn parse_date(ref_time: DateTime<Local>, s: &str) -> Result<FileTime, TouchError
}
}

if let Ok(dt) = parse_datetime::parse_datetime_at_date(ref_time, s) {
// **parse_datetime 0.13 API change:**
// The parse_datetime crate was updated from 0.11 to 0.13 in commit 2a69918ca to fix
// issue #8754 (large second values like "12345.123456789 seconds ago" failing).
// This introduced a breaking API change in parse_datetime_at_date:
//
// Previously (0.11): parse_datetime_at_date(chrono::DateTime) → chrono::DateTime
// Now (0.13): parse_datetime_at_date(jiff::Zoned) → jiff::Zoned
//
// Commit 4340913c4 initially adapted to this by switching from parse_datetime_at_date
// to parse_datetime, which broke deterministic relative date parsing (the ref_time
// parameter was no longer used, causing tests/touch/relative to fail in CI).
//
// This implementation restores parse_datetime_at_date usage with proper conversions:
// chrono::DateTime → jiff::Zoned → parse_datetime_at_date → jiff::Zoned → chrono::DateTime
//
// The use of parse_datetime_at_date (not parse_datetime) is critical for deterministic
// behavior with relative dates like "yesterday" or "2 days ago", which must be
// calculated relative to ref_time, not the current system time.

// Convert chrono DateTime to jiff Zoned for parse_datetime_at_date
let ref_zoned = {
let ts = Timestamp::new(
ref_time.timestamp(),
ref_time.timestamp_subsec_nanos() as i32,
)
.map_err(|_| TouchError::InvalidDateFormat(s.to_owned()))?;
Zoned::new(ts, jiff::tz::TimeZone::system())
};

if let Ok(zoned) = parse_datetime::parse_datetime_at_date(ref_zoned, s) {
let timestamp = zoned.timestamp();
let dt =
DateTime::from_timestamp(timestamp.as_second(), timestamp.subsec_nanosecond() as u32)
.map(|dt| dt.with_timezone(&Local))
.ok_or_else(|| TouchError::InvalidDateFormat(s.to_owned()))?;
return Ok(datetime_to_filetime(&dt));
}

Expand Down
Loading