diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 453c90423..8f0c31489 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -20,7 +20,7 @@ use uucore::display::Quotable; use uucore::error::UResult; use uucore::format_usage; use uucore::translate; -use uucore::time::{FormatSystemTimeFallback, format_system_time}; +use uucore::time::{FormatSystemTimeFallback, format, format_system_time}; const TAB: char = '\t'; const LINES_PER_PAGE: usize = 66; @@ -33,7 +33,6 @@ const DEFAULT_COLUMN_WIDTH: usize = 72; const DEFAULT_COLUMN_WIDTH_WITH_S_OPTION: usize = 512; const DEFAULT_COLUMN_SEPARATOR: &char = &TAB; const FF: u8 = 0x0C_u8; -const DATE_TIME_FORMAT: &str = "%b %d %H:%M %Y"; mod options { pub const HEADER: &str = "header"; @@ -402,6 +401,20 @@ fn parse_usize(matches: &ArgMatches, opt: &str) -> Option .map(from_parse_error_to_pr_error) } +fn get_date_format() -> String { + // Replicate behavior from GNU manual. + if std::env::var("POSIXLY_CORRECT").is_ok() + // TODO: This needs to be moved to uucore and handled by icu? + && (std::env::var("LC_TIME").unwrap_or_default() == "POSIX" + || std::env::var("LC_ALL").unwrap_or_default() == "POSIX") + { + "%b %e %H:%M %Y" + } else { + format::LONG_ISO + } + .to_string() +} + #[allow(clippy::cognitive_complexity)] fn build_options( matches: &ArgMatches, @@ -501,7 +514,7 @@ fn build_options( format_system_time( &mut v, time, - DATE_TIME_FORMAT, + &get_date_format(), FormatSystemTimeFallback::Integer, ) .ok() diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index a8d8bd3c8..00a4fa1ad 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -9,9 +9,9 @@ use std::fs::metadata; use uutests::new_ucmd; use uutests::util::UCommand; -const DATE_TIME_FORMAT: &str = "%b %d %H:%M %Y"; +const DATE_TIME_FORMAT_DEFAULT: &str = "%Y-%m-%d %H:%M"; -fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String { +fn file_last_modified_time_format(ucmd: &UCommand, path: &str, format: &str) -> String { let tmp_dir_path = ucmd.get_full_fixture_path(path); let file_metadata = metadata(tmp_dir_path); file_metadata @@ -19,19 +19,23 @@ fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String { i.modified() .map(|x| { let date_time: DateTime = x.into(); - date_time.format(DATE_TIME_FORMAT).to_string() + date_time.format(format).to_string() }) .unwrap_or_default() }) .unwrap_or_default() } +fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String { + file_last_modified_time_format(ucmd, path, DATE_TIME_FORMAT_DEFAULT) +} + fn all_minutes(from: DateTime, to: DateTime) -> Vec { let to = to + Duration::try_minutes(1).unwrap(); let mut vec = vec![]; let mut current = from; while current < to { - vec.push(current.format(DATE_TIME_FORMAT).to_string()); + vec.push(current.format(DATE_TIME_FORMAT_DEFAULT).to_string()); current += Duration::try_minutes(1).unwrap(); } vec @@ -398,6 +402,50 @@ fn test_with_offset_space_option() { .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); } +#[test] +fn test_with_date_format() { + const POSIXLY_FORMAT: &str = "%b %e %H:%M %Y"; + + // POSIXLY_CORRECT + LC_ALL/TIME=POSIX uses "%b %e %H:%M %Y" date format + let test_file_path = "test_one_page.log"; + let expected_test_file_path = "test_one_page.log.expected"; + let mut scenario = new_ucmd!(); + let value = file_last_modified_time_format(&scenario, test_file_path, POSIXLY_FORMAT); + scenario + .env("POSIXLY_CORRECT", "1") + .env("LC_ALL", "POSIX") + .args(&[test_file_path]) + .succeeds() + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); + + let mut scenario = new_ucmd!(); + let value = file_last_modified_time_format(&scenario, test_file_path, POSIXLY_FORMAT); + scenario + .env("POSIXLY_CORRECT", "1") + .env("LC_TIME", "POSIX") + .args(&[test_file_path]) + .succeeds() + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); + + // But not if POSIXLY_CORRECT/LC_ALL is something else. + let mut scenario = new_ucmd!(); + let value = file_last_modified_time_format(&scenario, test_file_path, DATE_TIME_FORMAT_DEFAULT); + scenario + .env("LC_TIME", "POSIX") + .args(&[test_file_path]) + .succeeds() + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); + + let mut scenario = new_ucmd!(); + let value = file_last_modified_time_format(&scenario, test_file_path, DATE_TIME_FORMAT_DEFAULT); + scenario + .env("POSIXLY_CORRECT", "1") + .env("LC_TIME", "C") + .args(&[test_file_path]) + .succeeds() + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); +} + #[test] fn test_with_pr_core_utils_tests() { let test_cases = vec![