Skip to content

Conversation

@jfinkels
Copy link
Collaborator

Support precision when rendering the time of last data modification with stat. For example, after this commit

$ stat --printf='%.1Y\n' f
1668645806.7

Previously, the precision in the format specification was ignored. This is implemented with a custom renderer because GNU stat seems to truncate the number as opposed to rounding the number as would happen when using format! with a specified number of digits of precision.

Fixes #3233

Support precision when rendering the time of last data modification
with `stat`. For example, after this commit

    $ stat --printf='%.1Y\n' f
    1668645806.7

Previously, the precision in the format specification was
ignored. This is implemented with a custom renderer because GNU `stat`
seems to truncate the number as opposed to rounding the number as
would happen when using `format!` with a specified number of digits of
precision.

Fixes uutils#3233
@sylvestre
Copy link
Contributor

Sweet, i was working on it but your implementation is better than mine :)

Could you please add this test? It covers more cases

#[cfg(feature = "touch")]
#[test]
fn test_timestamp_format() {
    let ts = TestScenario::new(util_name!());

    // Create a file with a specific timestamp for testing
    ts.ccmd("touch")
        .args(&["-d", "1970-01-01 18:43:33.023456789", "k"])
        .succeeds()
        .no_stderr();

    let test_cases = vec![
        // Basic timestamp formats
        ("%Y", "67413"),
        ("%.Y", "67413.023456789"),
        ("%.1Y", "67413.0"),
        ("%.3Y", "67413.023"),
        ("%.6Y", "67413.023456"),
        ("%.9Y", "67413.023456789"),
        // Width and padding tests
        ("%13.6Y", " 67413.023456"),
        ("%013.6Y", "067413.023456"),
        ("%-13.6Y", "67413.023456 "),
        // Longer width/precision combinations
        ("%18.10Y", " 67413.0234567890"),
        ("%I18.10Y", " 67413.0234567890"),
        ("%018.10Y", "0067413.0234567890"),
        ("%-18.10Y", "67413.0234567890 "),
    ];

    for (format_str, expected) in test_cases {
        let result = ts
            .ucmd()
            .args(&["-c", format_str, "k"])
            .succeeds()
            .stdout_move_str();

        assert_eq!(
            result.trim(),
            expected,
            "Format '{}' failed.\nExpected: '{}'\nGot: '{}'",
            format_str,
            expected,
            result.trim()
        );
    }
}

@jfinkels
Copy link
Collaborator Author

jfinkels commented Jan 11, 2025 via email

@jfinkels
Copy link
Collaborator Author

In order to pass the new test cases, I needed to update the code a bit. There is now a Precision enum that distinguishes between NotSpecified (as in %Y), NoNumber (as in %.Y), and Number(usize) (as in %.3Y). I also made some slight adjustments to the proposed tests: (1) the number of spaces was off by one in the padding test cases, and (2) the result.trim() was trimming the spaces.

There is still a few aspects of the code that are not quite right, but I think they can be fixed in separate pull requests. First, there is some numerical inaccuracy between getting the Metadata::mtime_nsec() and the formatting code, so what should be .7639 is actually .7640. Second, the handling of %.Y does not quite match the documentation, which specifies 9 digits of precision.

@sylvestre
Copy link
Contributor

Yeah, it is surprisingly complex :)

@github-actions
Copy link

GNU testsuite comparison:

GNU test failed: tests/misc/stdbuf. tests/misc/stdbuf is passing on 'main'. Maybe you have to rebase?
Skip an intermittent issue tests/tail/inotify-dir-recreate (fails in this run but passes in the 'main' branch)
Congrats! The gnu test tests/chmod/symlinks is no longer failing!

@sylvestre sylvestre merged commit 988cc4e into uutils:main Jan 12, 2025
64 of 65 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

stat: --printf option does not support precision in certain format strings

2 participants