mirror of
https://github.com/uutils/coreutils.git
synced 2025-12-23 08:47:37 +00:00
fix(date): align pure-digit -d parsing with GNU semantics
Implement GNU 'Pure numbers in date strings' for time-of-day tokens: - 1–2 digits => HH:00 today - 3–4 digits => HHMM today - Validate ranges; reject invalid times (e.g., 2400, 2360) Also: - Add tests for -d0, -d7, -d0700 under TZ=UTC0 and invalid numeric inputs - Reference GNU manual section for pure numbers
This commit is contained in:
parent
8d59e08097
commit
0047c7e66f
2 changed files with 99 additions and 1 deletions
|
|
@ -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 today’s 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue