Merge pull request #8907 from naoNao89/fix/date-numeric-d-parsing
Some checks failed
CICD / Style/cargo-deny (push) Has been cancelled
CICD / Style/deps (push) Has been cancelled
CICD / Documentation/warnings (push) Has been cancelled
CICD / MinRustV (push) Has been cancelled
CICD / Separate Builds (push) Has been cancelled
CICD / Dependencies (push) Has been cancelled
CICD / Code Coverage (push) Has been cancelled
Android / Test builds (push) Has been cancelled
GnuTests / Run GNU tests (native) (push) Has been cancelled
GnuTests / Run GNU tests (SELinux) (push) Has been cancelled
Benchmarks / Run benchmarks (CodSpeed) (push) Has been cancelled
Code Quality / Style/format (push) Has been cancelled
Code Quality / Style/lint (push) Has been cancelled
Code Quality / Style/spelling (push) Has been cancelled
Code Quality / Style/toml (push) Has been cancelled
Code Quality / Style/Python (push) Has been cancelled
Code Quality / Pre-commit hooks (push) Has been cancelled
Devcontainer / Verify devcontainer (push) Has been cancelled
FreeBSD / Style and Lint (push) Has been cancelled
FreeBSD / Tests (push) Has been cancelled
WSL2 / Test (push) Has been cancelled
CICD / Binary sizes (push) Has been cancelled
CICD / Build (push) Has been cancelled
CICD / Test all features separately (push) Has been cancelled
CICD / Build/Makefile (push) Has been cancelled
CICD / Build/stable (push) Has been cancelled
CICD / Build/nightly (push) Has been cancelled
CICD / Tests/BusyBox test suite (push) Has been cancelled
CICD / Tests/Toybox test suite (push) Has been cancelled
CICD / Safe Traversal Security Check (push) Has been cancelled
CICD / Build/SELinux (push) Has been cancelled
CICD / Build/SELinux-Stubs (Non-Linux) (push) Has been cancelled
GnuTests / Aggregate GNU test results (push) Has been cancelled

date: follow GNU pure-number -d semantics
This commit is contained in:
Sylvestre Ledru 2025-10-14 12:29:29 +02:00 committed by GitHub
commit e2e5c76a7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 1 deletions

View file

@ -203,7 +203,48 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Iterate over all dates - whether it's a single date or a file.
let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
DateSource::Human(ref input) => {
let date = parse_date(input);
// GNU compatibility (Pure numbers in date strings):
// - Manual: https://www.gnu.org/software/coreutils/manual/html_node/Pure-numbers-in-date-strings.html
// - Semantics: a pure decimal number denotes todays time-of-day (HH or HHMM).
// Examples: "0"/"00" => 00:00 today; "7"/"07" => 07:00 today; "0700" => 07:00 today.
// For all other forms, fall back to the general parser.
let is_pure_digits =
!input.is_empty() && input.len() <= 4 && input.chars().all(|c| c.is_ascii_digit());
let date = if is_pure_digits {
// Derive HH and MM from the input
let (hh_opt, mm_opt) = if input.len() <= 2 {
(input.parse::<u32>().ok(), Some(0u32))
} else {
let (h, m) = input.split_at(input.len() - 2);
(h.parse::<u32>().ok(), m.parse::<u32>().ok())
};
if let (Some(hh), Some(mm)) = (hh_opt, mm_opt) {
// Compose a concrete datetime string for today with zone offset.
// Use the already-determined 'now' and settings.utc to select offset.
let date_part =
strtime::format("%F", &now).unwrap_or_else(|_| String::from("1970-01-01"));
// If -u, force +00:00; otherwise use the local offset of 'now'.
let offset = if settings.utc {
String::from("+00:00")
} else {
strtime::format("%:z", &now).unwrap_or_default()
};
let composed = if offset.is_empty() {
format!("{date_part} {hh:02}:{mm:02}")
} else {
format!("{date_part} {hh:02}:{mm:02} {offset}")
};
parse_date(composed)
} else {
// Fallback on parse failure of digits
parse_date(input)
}
} else {
parse_date(input)
};
let iter = std::iter::once(date);
Box::new(iter)
}

View file

@ -778,3 +778,60 @@ fn test_date_resolution_no_combine() {
.arg("2025-01-01")
.fails();
}
#[test]
fn test_date_numeric_d_basic_utc() {
// Verify GNU-compatible pure-digit parsing for -d STRING under UTC
// 0/00 -> today at 00:00; 7/07 -> today at 07:00; 0700 -> today at 07:00
let today = Utc::now().date_naive();
let yyyy = today.year();
let mm = today.month();
let dd = today.day();
let mk =
|h: u32, m: u32| -> String { format!("{yyyy:04}-{mm:02}-{dd:02} {h:02}:{m:02}:00 UTC\n") };
new_ucmd!()
.env("TZ", "UTC0")
.arg("-d")
.arg("0")
.arg("+%F %T %Z")
.succeeds()
.stdout_only(mk(0, 0));
new_ucmd!()
.env("TZ", "UTC0")
.arg("-d")
.arg("7")
.arg("+%F %T %Z")
.succeeds()
.stdout_only(mk(7, 0));
new_ucmd!()
.env("TZ", "UTC0")
.arg("-d")
.arg("0700")
.arg("+%F %T %Z")
.succeeds()
.stdout_only(mk(7, 0));
}
#[test]
fn test_date_numeric_d_invalid_numbers() {
// Ensure invalid HHMM values are rejected (GNU-compatible)
new_ucmd!()
.env("TZ", "UTC0")
.arg("-d")
.arg("2400")
.arg("+%F %T %Z")
.fails()
.stderr_contains("invalid date");
new_ucmd!()
.env("TZ", "UTC0")
.arg("-d")
.arg("2360")
.arg("+%F %T %Z")
.fails()
.stderr_contains("invalid date");
}