diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index b77ed78dd65..e28b9dcd045 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -331,6 +331,67 @@ fn strip_default_padding(value: &str) -> String { value.to_string() } +/// Returns true if the specifier is `%N` (nanoseconds), which needs special +/// treatment: width controls precision (number of fractional digits) rather +/// than minimum field width, and the digit zeros are significant content, +/// not padding. +fn is_nanosecond_specifier(specifier: &str) -> bool { + specifier.ends_with('N') +} + +/// Apply modifiers specifically for the `%N` (nanoseconds) specifier. +/// +/// Unlike other numeric specifiers, `%N` treats width as precision +/// (number of fractional-second digits) and its zeros are significant +/// content, not padding. +/// +/// GNU behaviour: +/// - `%N` → all 9 digits (e.g. "000000000") +/// - `%-N` → all 9 digits unchanged (zeros are content, not padding) +/// - `%3N` → first 3 digits, zero-padded on the right (e.g. "000") +/// - `%_3N` → first 3 digits, trailing zeros replaced with spaces (e.g. "0 ") +/// - `%_N` → all 9 digits, trailing zeros replaced with spaces +fn apply_nanosecond_modifiers( + value: &str, + no_pad: bool, + underscore_flag: bool, + pad_char: char, + width: Option, +) -> String { + let default_width = 9; + let precision = width.unwrap_or(default_width); + + // Truncate or extend to the requested precision + let mut result: String = if precision <= value.len() { + value[..precision].to_string() + } else { + // Extend with trailing zeros to requested precision + let mut s = value.to_string(); + s.extend(std::iter::repeat_n('0', precision - value.len())); + s + }; + + if no_pad { + // `-` flag on %N: the zeros in nanoseconds are significant content, + // not padding, so return the digits unchanged. + } else if underscore_flag || pad_char == ' ' { + // `_` flag: replace trailing zeros with spaces + let trimmed = result.trim_end_matches('0'); + let content_len = if trimmed.is_empty() { 1 } else { trimmed.len() }; + let trailing_spaces = precision - content_len; + if trimmed.is_empty() { + result = "0".to_string(); + } else { + result = trimmed.to_string(); + } + result.extend(std::iter::repeat_n(' ', trailing_spaces)); + } + // Otherwise (default '0' padding or no flags): result already has the + // right number of zero-padded digits from the truncation/extension above. + + result +} + /// Apply width and flag modifiers to a formatted value. /// /// The specifier inside `parsed` (e.g., "d", "B", "Y") determines the default @@ -409,6 +470,19 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result