Date
2023-01-14.00:00:00
Message id
13208

Content

[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.
[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 [time.format]/6 is to address things like formatting a duration with %F. A duration doesn't contain the calendrical information that %F requires (year, month, day). Ditto for using %a (weekday name) with a year. 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.