Skip to content

Commit da6ede1

Browse files
committed
touch: fix parse_datetime 0.13 API usage for deterministic relative dates
The previous implementation incorrectly used parse_datetime() instead of parse_datetime_at_date(), causing relative date strings like 'yesterday' or '2 days ago' to be calculated from the current system time instead of the caller-specified reference time. This fix: - Uses parse_datetime_at_date() with proper chrono->jiff->chrono conversions - Restores deterministic behavior for relative date parsing - Adds jiff dependency to uu_touch for the required type conversions - Ensures tests/touch/relative passes in CI The parse_datetime_at_date() function still exists in v0.13, but now takes jiff::Zoned instead of chrono::DateTime as the reference time parameter.
1 parent 4340913 commit da6ede1

File tree

4 files changed

+32
-8
lines changed

4 files changed

+32
-8
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ icu_locale = "2.0.0"
334334
icu_provider = "2.0.0"
335335
indicatif = "0.18.0"
336336
itertools = "0.14.0"
337-
jiff = { version = "0.2.10", default-features = false, features = [
337+
jiff = { version = "0.2.15", default-features = false, features = [
338338
"std",
339339
"alloc",
340340
"tz-system",

src/uu/touch/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ path = "src/touch.rs"
2222
filetime = { workspace = true }
2323
clap = { workspace = true }
2424
chrono = { workspace = true }
25+
jiff = { workspace = true }
2526
parse_datetime = { workspace = true }
2627
thiserror = { workspace = true }
2728
uucore = { workspace = true, features = ["libc", "parser"] }

src/uu/touch/src/touch.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use chrono::{
1515
use clap::builder::{PossibleValue, ValueParser};
1616
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command};
1717
use filetime::{FileTime, set_file_times, set_symlink_file_times};
18+
use jiff::{Timestamp, Zoned};
1819
use std::borrow::Cow;
1920
use std::ffi::OsString;
2021
use std::fs::{self, File};
@@ -588,7 +589,7 @@ fn stat(path: &Path, follow: bool) -> std::io::Result<(FileTime, FileTime)> {
588589
))
589590
}
590591

591-
fn parse_date(_ref_time: DateTime<Local>, s: &str) -> Result<FileTime, TouchError> {
592+
fn parse_date(ref_time: DateTime<Local>, s: &str) -> Result<FileTime, TouchError> {
592593
// This isn't actually compatible with GNU touch, but there doesn't seem to
593594
// be any simple specification for what format this parameter allows and I'm
594595
// not about to implement GNU parse_datetime.
@@ -638,14 +639,35 @@ fn parse_date(_ref_time: DateTime<Local>, s: &str) -> Result<FileTime, TouchErro
638639
}
639640

640641
// **parse_datetime 0.13 API change:**
641-
// Previously (0.11): parse_datetime_at_date(chrono) → chrono::DateTime
642-
// Now (0.13): parse_datetime() → jiff::Zoned
642+
// The parse_datetime crate was updated from 0.11 to 0.13 in commit 2a69918ca to fix
643+
// issue #8754 (large second values like "12345.123456789 seconds ago" failing).
644+
// This introduced a breaking API change in parse_datetime_at_date:
643645
//
644-
// Since touch still uses chrono types internally, we convert:
645-
// jiff::Zoned → Unix timestamp → chrono::DateTime
646+
// Previously (0.11): parse_datetime_at_date(chrono::DateTime) → chrono::DateTime
647+
// Now (0.13): parse_datetime_at_date(jiff::Zoned)jiff::Zoned
646648
//
647-
// TODO: Consider migrating touch to jiff to eliminate this conversion
648-
if let Ok(zoned) = parse_datetime::parse_datetime(s) {
649+
// Commit 4340913c4 initially adapted to this by switching from parse_datetime_at_date
650+
// to parse_datetime, which broke deterministic relative date parsing (the ref_time
651+
// parameter was no longer used, causing tests/touch/relative to fail in CI).
652+
//
653+
// This implementation restores parse_datetime_at_date usage with proper conversions:
654+
// chrono::DateTime → jiff::Zoned → parse_datetime_at_date → jiff::Zoned → chrono::DateTime
655+
//
656+
// The use of parse_datetime_at_date (not parse_datetime) is critical for deterministic
657+
// behavior with relative dates like "yesterday" or "2 days ago", which must be
658+
// calculated relative to ref_time, not the current system time.
659+
660+
// Convert chrono DateTime to jiff Zoned for parse_datetime_at_date
661+
let ref_zoned = {
662+
let ts = Timestamp::new(
663+
ref_time.timestamp(),
664+
ref_time.timestamp_subsec_nanos() as i32,
665+
)
666+
.map_err(|_| TouchError::InvalidDateFormat(s.to_owned()))?;
667+
Zoned::new(ts, jiff::tz::TimeZone::system())
668+
};
669+
670+
if let Ok(zoned) = parse_datetime::parse_datetime_at_date(ref_zoned, s) {
649671
let timestamp = zoned.timestamp();
650672
let dt =
651673
DateTime::from_timestamp(timestamp.as_second(), timestamp.subsec_nanosecond() as u32)

0 commit comments

Comments
 (0)