3262. Formatting of negative durations is not specified

Section: 30.12 [time.format] Status: C++20 Submitter: Tomasz Kamiński Opened: 2019-08-18 Last modified: 2021-02-25

Priority: 2

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with C++20 status.

Discussion:

The current specification of the formatting for std::chrono::duration and std::hh_mm_ss types is unclear in regards the the handling of negative values. To illustrate:

std::cout << std::format("%H:%M:%S", -10'000s); // prints either -02:46:40 or -02:-46:-40

The indented behavior (and currently implemented, see here) is to apply the sign once, before the leftmost converted field.

[2019-09-14 Priority set to 2 based on reflector discussion]

Previous resolution [SUPERSEDED]:

This wording is relative to N4830.

[Drafting note: With the above clarification, the specification of the operator<< for hh_mm_ss may be simplified to format("{:%T}", hms).]

  1. Modify 30.12 [time.format] as indicated:

    -2- Each conversion specifier conversion-spec is replaced by appropriate characters as described in Table [tab:time.format.spec]. Some of the conversion specifiers depend on the locale that is passed to the formatting function if the latter takes one, or the global locale otherwise. If the formatted object does not contain the information the conversion specifier refers to, an exception of type format_error is thrown.

    -?- The result of formatting a std::chrono::duration instance holding a negative value, or of an hh_mm_ss object h for which h.is_negative() is true, is equivalent to the output of the corresponding positive value, with a - character placed before the replacement of the leftmost conversion specifier.

    [Example:

    cout << format("%T", -10'000s); // prints: -02:46:40
    cout << format("%H:%M:%S", -10'000s); // prints: -02:46:40
    cout << format("minutes %M, hours %H, seconds %S", -10'000s); // prints: minutes -46, hours 02, seconds 40
    

    end example]

    -3- Unless explicitly requested, […]

  2. Modify 30.9.3 [time.hms.nonmembers] as indicated:

    template<class charT, class traits, class Duration>
    basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os, const hh_mm_ss<Duration>& hms);
    

    -1- Effects: Equivalent to:

    return os << format(os.getloc(),
                 hms.is_negative() ? STATICALLY-WIDEN<charT>("-{:%T}")
                                   : STATICALLY-WIDEN<charT>("{:%T}"),
                 abs(hms.to_duration()));
    

[2019-09-14; Howard improves wording]

[2020-02; Status set to Immediate after LWG discussion Thursday in Prague. (Minor example wording cleanup)]

Proposed resolution:

This wording is relative to N4830.

[Drafting note: With the above clarification, the specification of the operator<< for hh_mm_ss may be simplified to format("{:%T}", hms).]

  1. Modify 30.12 [time.format] as indicated:

    -2- Each conversion specifier conversion-spec is replaced by appropriate characters as described in Table [tab:time.format.spec]. Some of the conversion specifiers depend on the locale that is passed to the formatting function if the latter takes one, or the global locale otherwise. If the formatted object does not contain the information the conversion specifier refers to, an exception of type format_error is thrown.

    -?- The result of formatting a std::chrono::duration instance holding a negative value, or of an hh_mm_ss object h for which h.is_negative() is true, is equivalent to the output of the corresponding positive value, with a STATICALLY-WIDEN<charT>("-") character sequence placed before the replacement of the leftmost conversion specifier.

    [Example:

    cout << format("{%:T}", -10'000s); // prints: -02:46:40
    cout << format("{:%H:%M:%S}", -10'000s); // prints: -02:46:40
    cout << format("{:minutes %M, hours %H, seconds %S}", -10'000s); // prints: minutes -46, hours 02, seconds 40
    

    end example]

    -3- Unless explicitly requested, […]

  2. Modify 30.9.3 [time.hms.nonmembers] as indicated:

    template<class charT, class traits, class Duration>
    basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os, const hh_mm_ss<Duration>& hms);
    

    -1- Effects: Equivalent to:

    return os << format(os.getloc(),
                 hms.is_negative() ? STATICALLY-WIDEN<charT>("-{:%T}")
                                   : STATICALLY-WIDEN<charT>("{:%T}"),
                 abs(hms.to_duration()));