diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 93cae9db338..d3572ec72bb 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -583,19 +583,14 @@ pub fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result MAX_FORMAT_WIDTH { - return Err(field_width_too_large(width, specifier)); - } - if width < result.len() { - result.truncate(width); - } - } - return Ok(result); - } return Ok(strip_default_padding(&result)); } @@ -649,13 +644,7 @@ pub fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result) -> Result, + pad: Option, + specifier: &str, +) -> Result { + let Some(width) = width else { + if pad == Some(' ') { + return apply_nanosecond_modifiers(digits, Some(digits.len()), pad, specifier); + } + return Ok(digits.to_string()); + }; + if width > MAX_FORMAT_WIDTH { + return Err(field_width_too_large(width, specifier)); + } + if width == 0 { + return Ok(digits.to_string()); + } + let significant = significant_nanosecond_digits(&digits[..width.min(digits.len())]); + let Some(pad_char) = pad else { + return Ok(significant.to_string()); + }; + let padding = width - significant.len(); + let mut padded = try_alloc_padded(significant.len(), padding, width, specifier)?; + padded.push_str(significant); + padded.extend(std::iter::repeat_n(pad_char, padding)); + Ok(padded) +} + +/// Keep at least one digit of `digits` and drop its trailing zeros. +fn significant_nanosecond_digits(digits: &str) -> &str { + let trimmed = digits.trim_end_matches('0'); + if trimmed.is_empty() { + &digits[..1] + } else { + trimmed + } +} + /// Allocate a `String` with enough capacity for `current_len + padding`, /// returning `FieldWidthTooLarge` on arithmetic overflow or allocation failure. fn try_alloc_padded( diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index a6bb3fe82f1..a85b08f084f 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -2598,6 +2598,37 @@ fn test_date_strftime_n_width_and_flags() { .stdout_is("000000000\n"); } +// https://github.com/uutils/coreutils/issues/15697 +#[test] +fn test_date_strftime_n_width_keeps_leading_zeros() { + for (time, format, expected) in [ + ("@0.005123456", "+%3N", "005\n"), + ("@0.050123456", "+%3N", "050\n"), + ("@0.011123456", "+%3N", "011\n"), + ("@0.123456789", "+%3N", "123\n"), + ("@0.005123456", "+%6N", "005123\n"), + ("@0.005123456", "+%1N", "0\n"), + ("@0.005123456", "+%12N", "005123456000\n"), + ("@0.005123456", "+%_12N", "005123456 \n"), + ("@0.005123456", "+%_N", "005123456\n"), + ("@0.050000000", "+%_3N", "05 \n"), + ("@0.050000000", "+%-3N", "05\n"), + ("@0.050000000", "+%-12N", "05\n"), + ("@0.050000000", "+%-N", "050000000\n"), + ("@0.005123456", "+%+3N", "005\n"), + ("@1.005123456", "+%s%3N", "1005\n"), + ] { + new_ucmd!() + .env("LC_ALL", "C") + .env("TZ", "UTC") + .arg("-d") + .arg(time) + .arg(format) + .succeeds() + .stdout_is(expected); + } +} + #[test] fn test_date_strftime_flag_on_composite() { // GNU treats composite strftime specifiers atomically.