Section: 30.12 [time.format] Status: New Submitter: Tam S. B. Opened: 2023-01-14 Last modified: 2023-05-30
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
30.12 [time.format]/3:
If the formatted object does not contain the information the conversion
specifier refers to, an exception of type format_error
is thrown.
30.12 [time.format]/6:
If the type being formatted does not contain the information that the format
flag needs, an exception of type format_error
is thrown.
It's unclear how to determine if a type contain the needed information, and implementations diverge.
For example, consider
#include <chrono> #include <format> auto f(std::chrono::month_day_last mdl) { return std::format("{:%j}", mdl); }
Both libstdc++ and libc++ produce a compile-time error, claiming that the
argument does not contain the information, while MSVC STL throws
format_error
at run time unless mdl
is January/last
,
in which case the function returns "031".
Another interesting case is format("{:%d}", mdl)
where the value
can be printed for all months except February, which requires a year
to know how many days it has.
A related example from Jonathan Wakely:
std::chrono::weekday_indexed wdi(Monday, 7); // 7th Monday in the month assert( ! wdi.ok() ); assert( wdi.weekday().ok() ); std::format("{:%a}", wdi);
For %a
the required information is "a valid weekday",
and arguably this does contain a valid weekday.
On the other hand, there's no 7th Monday, so this isn't valid.
Should this throw or not?
This was discussed by LWG and Howard Hinnant summarized the intended behaviour as:
"The intention of 30.12 [time.format]/6 is to address things like formatting aduration
with%F
. Aduration
doesn't contain the calendrical information that%F
requires (year, month, day). Ditto for using%a
(weekday name) with ayear
. It is meant to address mismatching types and flags, and not meant to address values."
The type chrono::weekday
does contain the information needed to print
a weekday. A specific invalid value doesn't change that.
The type chrono::month_day_last
does not contain the information
needed to print the day of the year.
A specific value where the day can be known doesn't change that.
The day of month is more interesting, and might need more discussion.
Jonathan proposed adding more examples to clarify the intention that only the
type matters, and not the value. There is some redundancy between p3 and p6.
Referring to "the formatted object" in p3 seems unclear.
Saying "type" as in p6 is better.
But p6 refers to "format flag" which is not defined,
whereas p3 uses "conversion specifier" (defined at the start of that paragraph).
The two uses of "flag" in p6 look like remnants from the earlier chrono::format
feature that was replaced by integration with std::format
.
[2023-02-01; Reflector poll]
Set priority to 3 after reflector poll.
[2023-05-30; Jonathan adds wording]
Proposed resolution:
This wording is relative to N4950.
Modify 30.12 [time.format] as indicated:
-3- Each conversion specifier conversion-spec is replaced by appropriate characters as described in Table 101 ([tab:time.format.spec]); the formats specified in ISO 8601:2004 shall be used where so described. Some of the conversion specifiers depend on the formatting locale. If the string literal encoding is a Unicode encoding form and the locale is among an implementation-defined set of locales, each replacement that depends on the locale is performed as if the replacement character sequence is converted to the string literal encoding. If the
formatted objecttype being formatted does not contain the information the conversion specifier refers to, an exception of typeformat_error
is thrown.[Example ?: A
duration
does not contain enough information to format as a weekday using%w
. Aweekday_indexed
does contain enough information to format using%w
andMonday[7]
can be formatted as"1"
even thoughMonday[7].ok()
isfalse
. Amonth_day
does not contain enough information to format as the day of the year using%j
, even when themonth()
part isJanuary
. — end example]However, if a flag refers to a "time of day" (e.g.,
%H
,%I
,%p
, etc.), then a specialization ofduration
is interpreted as the time of day elapsed since midnight.-4- The result of formatting a
std::chrono::duration
instance holding a negative value, or anhh_mm_ss
objecth
for whichh.is_negative()
istrue
, is equivalent to the output of the corresponding positive value, with aSTATICALLY-WIDEN<charT>("-")
character sequence placed before the replacement of the initial conversion specifier.[Example 1:
— end 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
-5- Unless explicitly requested, the result of formatting a chrono type does not contain time zone abbreviation and time zone offset information. If the information is available, the conversion specifiers
%Z
and%z
will format this information (respectively).[Note 1: If the information is not available and a
%Z
or%z
conversion specifier appears in the chrono-format-spec, an exception of typeformat_error
is thrown, as described above. — end note]
-6- If the type being formatted does not contain the information that the format flag needs, an exception of typeformat_error
is thrown, as described above.
[Example 2: Aduration
does not contain enough information to format as aweekday
. — end example]
However, if a flag refers to a "time of day" (e.g.,%H
,%I
,%p
, etc.), then a specialization ofduration
is interpreted as the time of day elapsed since midnight.