From 6df88d3c73528adda9ece1d290bec352c86efa64 Mon Sep 17 00:00:00 2001 From: Aaryan Dogra Date: Mon, 27 Apr 2026 21:55:59 +0530 Subject: [PATCH 1/7] Implement no_pad logic for 'N' specifier Add no_pad handling for specifier ending with 'N'. --- src/uu/date/src/format_modifiers.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index b77ed78dd65..8022c552f54 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -411,6 +411,18 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result Date: Wed, 29 Apr 2026 18:21:43 +0530 Subject: [PATCH 2/7] Fix negative width prefix handling for %N format specifier When the dash flag (-) is used with a width on the %N (nanoseconds) format specifier, the width should be applied to truncate the output without any additional processing like trimming trailing zeros. Changes: - Changed width field from usize to isize to support signed widths - Updated parsing logic to parse width as isize - Fixed %N handling in no_pad branch to truncate correctly - Removed incorrect trailing zero trimming - Updated effective_width calculation to handle isize widths Behavior: %-2N now correctly returns first 2 digits of nanoseconds, matching GNU coreutils behavior. Fixes: Issue #12001 --- src/uu/date/src/format_modifiers.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 8022c552f54..6198d9167cc 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -75,9 +75,10 @@ struct ParsedSpec<'a> { /// Flag characters from `[_0^#+-]`. flags: &'a str, /// Explicit width, if present. `None` means no width was specified. - /// A value that overflows `usize` is represented as `Some(usize::MAX)` so + /// Negative widths (isize) are used when `-` flag precedes the width. + /// A value that overflows `isize` is represented as `Some(isize::MAX)` so /// the downstream allocation check surfaces it as `FieldWidthTooLarge`. - width: Option, + width: Option, /// The specifier itself, including any leading colons (e.g. `Y`, `:z`, `::z`). spec: &'a str, /// Total byte length of the parsed sequence including the leading `%`. @@ -110,7 +111,7 @@ fn parse_format_spec(s: &str) -> Option> { pos += 1; } let width = if pos > width_start { - Some(s[width_start..pos].parse::().unwrap_or(usize::MAX)) + Some(s[width_start..pos].parse::().unwrap_or(isize::MAX)) } else { None }; @@ -413,14 +414,13 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result 0 { + let end = w_usize.min(result.len()); + let truncated = &result[..end]; + // Return truncated nanoseconds without trimming trailing zeros (GNU behavior) + return Ok(truncated.to_string()); + } } } return Ok(strip_default_padding(&result)); @@ -430,7 +430,10 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result w, + Some(w) => { + let abs_w = w.abs() as usize; + abs_w + } None if underscore_flag || pad_char != default_pad => get_default_width(specifier), None => 0, }; From 88f389b7c5328c53dd803f3fdb41b29ae148dcba Mon Sep 17 00:00:00 2001 From: Aaryan Date: Thu, 30 Apr 2026 21:02:31 +0530 Subject: [PATCH 3/7] my test --- src/uu/date/src/format_modifiers.rs | 39 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 6198d9167cc..dcd7b12f5dd 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -75,7 +75,7 @@ struct ParsedSpec<'a> { /// Flag characters from `[_0^#+-]`. flags: &'a str, /// Explicit width, if present. `None` means no width was specified. - /// Negative widths (isize) are used when `-` flag precedes the width. + /// Negative widths are used when `-` flag precedes the width. /// A value that overflows `isize` is represented as `Some(isize::MAX)` so /// the downstream allocation check surfaces it as `FieldWidthTooLarge`. width: Option, @@ -105,13 +105,18 @@ fn parse_format_spec(s: &str) -> Option> { } let flags = &s[flags_start..pos]; + // Check if '-' flag is present + let has_minus_flag = flags.contains('-'); + // Width: zero or more ASCII digits. let width_start = pos; while pos < bytes.len() && bytes[pos].is_ascii_digit() { pos += 1; } let width = if pos > width_start { - Some(s[width_start..pos].parse::().unwrap_or(isize::MAX)) + let parsed: isize = s[width_start..pos].parse::().unwrap_or(isize::MAX); + // Make width negative if '-' flag is present + Some(if has_minus_flag { -parsed } else { parsed }) } else { None }; @@ -344,6 +349,11 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result9}", result); + // } + // Determine default pad character based on specifier type // Determine default pad character based on specifier type. // Text specifiers (month names, etc.) and numeric specifiers like %e, %k, %l @@ -412,17 +422,13 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result 0 { - let end = w_usize.min(result.len()); - let truncated = &result[..end]; - // Return truncated nanoseconds without trimming trailing zeros (GNU behavior) - return Ok(truncated.to_string()); - } + let abs_w = w.abs() as usize; + let end = abs_w.min(result.len()); + let truncated = &result[..end]; + // Return truncated nanoseconds without trimming trailing zeros (GNU behavior) + return Ok(truncated.to_string()); } - } return Ok(strip_default_padding(&result)); } @@ -540,7 +546,7 @@ mod tests { /// Build a `ParsedSpec` for unit-testing `apply_modifiers` without a real /// format string. `len` is set to 0 because these tests never use it. - fn spec<'a>(flags: &'a str, width: Option, spec: &'a str) -> ParsedSpec<'a> { + fn spec<'a>(flags: &'a str, width: Option, spec: &'a str) -> ParsedSpec<'a> { ParsedSpec { flags, width, @@ -859,11 +865,11 @@ mod tests { #[test] fn test_apply_modifiers_width_too_large() { - let err = apply_modifiers("x", &spec("", Some(usize::MAX), "c")).unwrap_err(); + let err = apply_modifiers("x", &spec("", Some(isize::MAX), "c")).unwrap_err(); assert!(matches!( err, FormatError::FieldWidthTooLarge { width, specifier } - if width == usize::MAX && specifier == "c" + if width == isize::MAX && specifier == "c" )); } @@ -880,7 +886,7 @@ mod tests { assert!(matches!( err, FormatError::FieldWidthTooLarge { width, specifier } - if width == usize::MAX && specifier == "Y" + if width == isize::MAX && specifier == "Y" )); } @@ -954,7 +960,7 @@ mod tests { #[test] fn test_parse_format_spec() { // (input, expected: Some((flags, width, spec, len)) or None) - type ParsedTuple = (&'static str, Option, &'static str, usize); + type ParsedTuple = (&'static str, Option, &'static str, usize); let cases: &[(&str, Option)] = &[ // ---- plain single-letter specifiers ---- ("%Y", Some(("", None, "Y", 2))), @@ -1003,6 +1009,7 @@ mod tests { for (input, expected) in cases { let actual = parse_format_spec(input).map(|p| (p.flags, p.width, p.spec, p.len)); + assert_eq!(actual, *expected, "input = {input:?}"); } } From 864853b17ac5a7c52bd3dd29ce92a2a9664e8df8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 15:50:39 +0000 Subject: [PATCH 4/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/uu/date/src/format_modifiers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index dcd7b12f5dd..66c396716f1 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -1009,7 +1009,7 @@ mod tests { for (input, expected) in cases { let actual = parse_format_spec(input).map(|p| (p.flags, p.width, p.spec, p.len)); - + assert_eq!(actual, *expected, "input = {input:?}"); } } From 86c66d74fff1bafea885c359aec8674f9dd34757 Mon Sep 17 00:00:00 2001 From: Aaryan Date: Fri, 1 May 2026 17:26:50 +0530 Subject: [PATCH 5/7] fix-two --- src/uu/date/src/format_modifiers.rs | 33 ++++++++++++----------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 66c396716f1..23cdeb42df0 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -44,7 +44,7 @@ pub enum FormatError { /// Error from the underlying jiff library JiffError(jiff::Error), /// Field width calculation overflowed or required allocation failed - FieldWidthTooLarge { width: usize, specifier: String }, + FieldWidthTooLarge { width: isize, specifier: String }, } impl fmt::Display for FormatError { @@ -75,7 +75,6 @@ struct ParsedSpec<'a> { /// Flag characters from `[_0^#+-]`. flags: &'a str, /// Explicit width, if present. `None` means no width was specified. - /// Negative widths are used when `-` flag precedes the width. /// A value that overflows `isize` is represented as `Some(isize::MAX)` so /// the downstream allocation check surfaces it as `FieldWidthTooLarge`. width: Option, @@ -105,9 +104,6 @@ fn parse_format_spec(s: &str) -> Option> { } let flags = &s[flags_start..pos]; - // Check if '-' flag is present - let has_minus_flag = flags.contains('-'); - // Width: zero or more ASCII digits. let width_start = pos; while pos < bytes.len() && bytes[pos].is_ascii_digit() { @@ -115,8 +111,7 @@ fn parse_format_spec(s: &str) -> Option> { } let width = if pos > width_start { let parsed: isize = s[width_start..pos].parse::().unwrap_or(isize::MAX); - // Make width negative if '-' flag is present - Some(if has_minus_flag { -parsed } else { parsed }) + Some(parsed) } else { None }; @@ -348,12 +343,7 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result9}", result); - // } - + // Determine default pad character based on specifier type // Determine default pad character based on specifier type. // Text specifiers (month names, etc.) and numeric specifiers like %e, %k, %l @@ -422,13 +412,16 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result) -> Result) -> Result Result { let target_len = From a6b25aca9abfc7ec67530fe7d35e1fbc071bb4ff Mon Sep 17 00:00:00 2001 From: Aaryan Date: Fri, 1 May 2026 19:03:30 +0530 Subject: [PATCH 6/7] fix three --- src/uu/date/src/format_modifiers.rs | 39 ++++++++++++----------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 23cdeb42df0..30a1d01518d 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -44,7 +44,7 @@ pub enum FormatError { /// Error from the underlying jiff library JiffError(jiff::Error), /// Field width calculation overflowed or required allocation failed - FieldWidthTooLarge { width: isize, specifier: String }, + FieldWidthTooLarge { width: usize, specifier: String }, } impl fmt::Display for FormatError { @@ -75,9 +75,8 @@ struct ParsedSpec<'a> { /// Flag characters from `[_0^#+-]`. flags: &'a str, /// Explicit width, if present. `None` means no width was specified. - /// A value that overflows `isize` is represented as `Some(isize::MAX)` so - /// the downstream allocation check surfaces it as `FieldWidthTooLarge`. - width: Option, + /// Width overflows are handled naturally by usize parsing. + width: Option, /// The specifier itself, including any leading colons (e.g. `Y`, `:z`, `::z`). spec: &'a str, /// Total byte length of the parsed sequence including the leading `%`. @@ -110,8 +109,7 @@ fn parse_format_spec(s: &str) -> Option> { pos += 1; } let width = if pos > width_start { - let parsed: isize = s[width_start..pos].parse::().unwrap_or(isize::MAX); - Some(parsed) + Some(s[width_start..pos].parse::().unwrap_or(usize::MAX)) } else { None }; @@ -412,11 +410,11 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result) -> Result { - let abs_w = w.abs() as usize; - abs_w - } + Some(w) => w, None if underscore_flag || pad_char != default_pad => get_default_width(specifier), None => 0, }; @@ -476,16 +471,14 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result) -> Result Result { let target_len = @@ -541,7 +534,7 @@ mod tests { /// Build a `ParsedSpec` for unit-testing `apply_modifiers` without a real /// format string. `len` is set to 0 because these tests never use it. - fn spec<'a>(flags: &'a str, width: Option, spec: &'a str) -> ParsedSpec<'a> { + fn spec<'a>(flags: &'a str, width: Option, spec: &'a str) -> ParsedSpec<'a> { ParsedSpec { flags, width, @@ -860,11 +853,11 @@ mod tests { #[test] fn test_apply_modifiers_width_too_large() { - let err = apply_modifiers("x", &spec("", Some(isize::MAX), "c")).unwrap_err(); + let err = apply_modifiers("x", &spec("", Some(usize::MAX), "c")).unwrap_err(); assert!(matches!( err, FormatError::FieldWidthTooLarge { width, specifier } - if width == isize::MAX && specifier == "c" + if width == usize::MAX && specifier == "c" )); } @@ -881,7 +874,7 @@ mod tests { assert!(matches!( err, FormatError::FieldWidthTooLarge { width, specifier } - if width == isize::MAX && specifier == "Y" + if width == usize::MAX && specifier == "Y" )); } @@ -955,7 +948,7 @@ mod tests { #[test] fn test_parse_format_spec() { // (input, expected: Some((flags, width, spec, len)) or None) - type ParsedTuple = (&'static str, Option, &'static str, usize); + type ParsedTuple = (&'static str, Option, &'static str, usize); let cases: &[(&str, Option)] = &[ // ---- plain single-letter specifiers ---- ("%Y", Some(("", None, "Y", 2))), From b241b1c380eda235f022411d584dbe688a4fb714 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 13:34:44 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/uu/date/src/format_modifiers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 30a1d01518d..3e1071ef636 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -341,7 +341,7 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result