Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

formatting chrono with precision #3219

Closed
brevzin opened this issue Dec 8, 2022 · 9 comments
Closed

formatting chrono with precision #3219

brevzin opened this issue Dec 8, 2022 · 9 comments

Comments

@brevzin
Copy link
Contributor

brevzin commented Dec 8, 2022

#include <chrono>
#include <fmt/chrono.h>

int main() {
    using namespace std::chrono;

    // this one prints 1.00s, as expected
    fmt::print("{:.2}\n", duration<double>(1.0));

    // this one prints 1s but shouldn't compile
    fmt::print("{:.2}\n", duration<int>(1));

    // these both print .2
    fmt::print("{:.2}\n", time_point<system_clock, duration<double>>(duration<double>(1.0)));
    fmt::print("{:.2}\n", time_point<system_clock, duration<int>>(duration<int>(1)));
}

If we look at the standard spec, it's clear that using precision with a duration is only valid for floating point (https://eel.is/c++draft/time.format#1.sentence-4), so the second call there should be rejected.

There's some ambiguity in the standard spec right now about what formatting a time_point<system_clock, duration<int>> with {:.2} actually means (valid or not? probably not?) but either way, printing .2 in either case is definitely incorrect.

@vitaut
Copy link
Contributor

vitaut commented Dec 9, 2022

Good catch(es), thanks. As usual, PRs are welcome =).

@phprus
Copy link
Contributor

phprus commented Dec 10, 2022

Why is printing .2 incorrect?
I think, in chrono-specs, the string .2 is a valid literal-char.

@jwakely
Copy link
Contributor

jwakely commented Dec 10, 2022

No, the first character of a chrono-spec must be %

@jwakely
Copy link
Contributor

jwakely commented Dec 10, 2022

If you want to write literal characters before the first % put them before the replacement field: ".2{}\n"

@vitaut
Copy link
Contributor

vitaut commented Dec 21, 2022

The time point issue is fixed in #3232 (thanks @ShawnZhong) but precision of durations with integral representation is useful and will continue to be supported.

@vitaut vitaut closed this as completed Dec 21, 2022
@ShawnZhong
Copy link
Contributor

Thanks for clarifying. I'm good with precision for integral durations.

I have some follow-up comments/questions (which I can open a new issue if needed):

  • I agree that precision with %S is useful and should continue to be supported:

    // %S writes the second as a decimal number
    fmt::print("{:.8%S}\n", 1234ns); // prints 00.000001234
  • I'm not sure what should be the expected behavior of just specifying the precision:

    fmt::print("{:.8}\n", 1234ns); // prints 1234ns
  • Precision with %Q throws format_error, which is reasonable

    // %Q writes the count of ticks of the duration
    fmt::print("{:.8%Q}", 1234ns); // precision not allowed for this argument type
  • Whether or not %M and %H are valid for formatting durations?

    // %M writes the minute as a decimal number.
    fmt::print("{:.8%M}\n", 123.4min); // prints 03
    // %H writes the hour (24-hour clock) as a decimal number. 
    fmt::print("{:.8%H}\n", 12.34h);   // prints 12

@vitaut
Copy link
Contributor

vitaut commented Dec 26, 2022

I'm not sure what should be the expected behavior of just specifying the precision

This seems pretty harmless and we can continue ignoring precision here (maybe even ignore in other cases).

Whether or not %M and %H are valid for formatting durations?

I think %M and %H make sense for time points but not necessarily for durations. The same probably applies to %S, not sure why we allowed them in the first place. Unless, of course, they are supported by the standard and the date library in which case we should check with Howard about the rationale.

@jwakely
Copy link
Contributor

jwakely commented Jan 12, 2023

The standard seems clear:

However, if a flag refers to a “time of day” (e.g. %H, %I, %p, etc.), then a specialization of duration is
interpreted as the time of day elapsed since midnight.

So %H uses the value of chrono::floor<chrono::hours>(dur).count() and %M uses the value of chrono::floor<chrono::minutes>(dur).count().

@jwakely
Copy link
Contributor

jwakely commented Jan 12, 2023

Also see the example in [time.format] p4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants