Skip to content
Open
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
18 changes: 12 additions & 6 deletions src/uu/date/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// - 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());
//
// GNU compatibility (Military timezone 'J' after a time):
// 'J' is local time, so "<digits>j"/"<digits>J" is the same time-of-day form
// as the bare "<digits>" input ("9j" == "9"). Strip it before the digit check.
let time_digits = input.strip_suffix(['j', 'J']).unwrap_or(input);
let is_pure_digits = !time_digits.is_empty()
&& time_digits.len() <= 4
&& time_digits.chars().all(|c| c.is_ascii_digit());

let date = if is_empty_or_whitespace || is_military_j {
// Treat empty string or 'J' as midnight today (00:00:00) in local time
Expand Down Expand Up @@ -463,11 +469,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let composed = format!("{date_part} {total_hours:02}:00:00 +00:00");
parse_date(composed, &now, DebugOptions::new(settings.debug, false))
} else 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))
// Derive HH and MM from the digits
let (hh_opt, mm_opt) = if time_digits.len() <= 2 {
(time_digits.parse::<u32>().ok(), Some(0u32))
} else {
let (h, m) = input.split_at(input.len() - 2);
let (h, m) = time_digits.split_at(time_digits.len() - 2);
(h.parse::<u32>().ok(), m.parse::<u32>().ok())
};

Expand Down
58 changes: 46 additions & 12 deletions tests/by-util/test_date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,41 @@ fn test_date_military_timezone_j_variations() {
.stdout_contains("UTC");
}

#[test]
fn test_date_military_timezone_j_with_time() {
// 'J' is local time, so "<digits>j" is the time-of-day form (HH or HHMM)
// in the local zone, same as the bare "<digits>" input. GNU accepts it.
let test_cases = vec![
("8j", "08:00:00"),
("9j", "09:00:00"),
("9J", "09:00:00"),
("12j", "12:00:00"),
("1230j", "12:30:00"),
("0j", "00:00:00"),
("00j", "00:00:00"),
];

for (input, expected) in test_cases {
new_ucmd!()
.env("TZ", "UTC")
.arg("-d")
.arg(input)
.arg("+%T")
.succeeds()
.stdout_is(format!("{expected}\n"));
}

// Out-of-range times are rejected, same as the bare digit form
for input in ["2400j", "2360j", "12345j"] {
new_ucmd!()
.env("TZ", "UTC")
.arg("-d")
.arg(input)
.fails()
.stderr_contains("invalid date");
}
}

#[test]
fn test_date_empty_string() {
// Empty string should be treated as midnight today
Expand Down Expand Up @@ -1973,20 +2008,19 @@ fn test_date_bare_timezone_abbreviation() {
}

#[test]
#[ignore = "https://github.com/uutils/parse_datetime/issues/279GNU date silently ignores unrecognized trailing tokens (e.g. `8j`), but parse_datetime rejects them."]
#[ignore = "https://github.com/uutils/parse_datetime/issues/279. GNU date silently ignores unrecognized trailing tokens (e.g. `8 j`), but parse_datetime rejects them."]
fn test_date_ignores_unrecognized_trailing_tokens() {
// GNU compatibility: trailing unknown word-tokens after a valid number are ignored.
// GNU parses `8j`, `8 j`, etc. as hour 8; our parse_datetime crate errors out.
for input in ["8j", "8 j"] {
new_ucmd!()
.env("TZ", "UTC")
.arg("-u")
.arg("-d")
.arg(input)
.arg("+%H:%M:%S")
.succeeds()
.stdout_only("08:00:00\n");
}
// GNU parses `8 j` (number, space, token) as hour 8; our parse_datetime crate errors out.
// The no-space `8j` form is handled directly (see test_date_military_timezone_j_with_time).
new_ucmd!()
.env("TZ", "UTC")
.arg("-u")
.arg("-d")
.arg("8 j")
.arg("+%H:%M:%S")
.succeeds()
.stdout_only("08:00:00\n");
}

#[test]
Expand Down
Loading