date: add support for the 'J' military timezone

This commit is contained in:
Sylvestre Ledru
2025-10-27 10:30:43 +01:00
parent b17129d8ee
commit 284554658d
2 changed files with 42 additions and 9 deletions
+22 -2
View File
@@ -205,15 +205,35 @@ 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) => {
// GNU compatibility (Military timezone 'J'):
// 'J' is reserved for local time in military timezones.
// GNU date accepts it and treats it as midnight today (00:00:00).
let is_military_j = input.eq_ignore_ascii_case("j");
// 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).
// - 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 {
let date = if is_military_j {
// Treat 'J' as midnight today (00:00:00) in local time
let date_part =
strtime::format("%F", &now).unwrap_or_else(|_| String::from("1970-01-01"));
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} 00:00")
} else {
format!("{date_part} 00:00 {offset}")
};
parse_date(composed)
} 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))
+20 -7
View File
@@ -946,17 +946,30 @@ fn test_date_tz_abbreviation_unknown() {
}
#[test]
#[ignore = "we reject 'J', GNU treats as midnight"]
fn test_date_fuzz_military_timezone_j() {
// J is reserved for local time in military timezones
// GNU date treats it as midnight, we reject it
fn test_date_military_timezone_j_variations() {
// Test multiple variations of 'J' input (case insensitive, with whitespace)
// All should produce midnight (00:00:00)
let test_cases = vec!["J", "j", " J ", " j ", "\tJ\t"];
for input in test_cases {
new_ucmd!()
.env("TZ", "UTC")
.arg("-d")
.arg(input)
.arg("+%T")
.succeeds()
.stdout_is("00:00:00\n");
}
// Test with -u flag to verify UTC behavior
new_ucmd!()
.env("TZ", "UTC+1")
.arg("-u")
.arg("-d")
.arg("J")
.arg("+%F %T %Z")
.arg("+%T %Z")
.succeeds()
.stdout_contains("00:00:00");
.stdout_contains("00:00:00")
.stdout_contains("UTC");
}
#[test]