diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 4011599121..70e8b99b43 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -1157,7 +1157,17 @@ fn parse_dates_from_reader( bytes.pop(); } match String::from_utf8(bytes) { - Ok(s) => parse_date(s, now, dbg_opts, allow_extended), + Ok(s) => { + let trimmed = s.trim(); + if trimmed.is_empty() || trimmed == "-" { + // GNU treats empty lines (and a lone "-", like `-d`) in + // `--file`/stdin input as midnight today, not the + // current time (issue #14498). + Ok(ParsedDateTime::InRange(midnight_today(now))) + } else { + parse_date(s, now, dbg_opts, allow_extended) + } + } // Report lines with invalid UTF-8 (with non-printable bytes // octal-escaped like GNU) instead of silently stopping the input Err(e) => Err(( @@ -1168,6 +1178,18 @@ fn parse_dates_from_reader( })) } +/// Midnight at the start of `now`'s civil date, in its time zone. +/// +/// Empty (whitespace-only or lone `-`) lines in `--file`/stdin input mean +/// midnight today for GNU `date`, mirroring the `-d` handling above. Falls +/// back to `now` itself on zones whose midnight does not exist. +fn midnight_today(now: &Zoned) -> Zoned { + now.date() + .at(0, 0, 0, 0) + .to_zoned(now.time_zone().clone()) + .unwrap_or_else(|_| now.clone()) +} + /// Parse a string into either an in-range [`Zoned`] value or an extended date. fn parse_date>( s: S, diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index 51ce9ed579..bab68bbc8c 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -656,6 +656,19 @@ fn test_date_for_file_with_non_utf8_path() { ucmd.arg("--file").arg(file).succeeds().no_output(); } +#[test] +fn test_date_file_empty_lines_are_midnight() { + // Empty, whitespace-only, and lone `-` lines mean midnight today, + // like GNU, not the current time (#14498). + let (at, mut ucmd) = at_and_ucmd!(); + at.write("dates", "\n \n-\n"); + ucmd.arg("--file") + .arg("dates") + .arg("+%H:%M") + .succeeds() + .stdout_is("00:00\n00:00\n00:00\n"); +} + #[test] fn test_date_file_invalid_utf8_line() { let (at, mut ucmd) = at_and_ucmd!();