Title
Cannot format `zoned_time` with resolution coarser than seconds
Status
voting
Section
[time.format]
Submitter
Jonathan Wakely

Created on 2024-07-26.00:00:00 last changed yesterday

Messages

Date: 2024-08-02.21:14:41

Proposed resolution:

This wording is relative to N4986.

  1. Modify [time.format] as indicated:

    template<classDuration, class charT>
      struct formatter<chrono::local-time-format-t<Duration>, charT>;
    

    -17- Let `f` be a locale-time-format-t<Duration> object passed to `formatter::format`.

    -18- Remarks: If the chrono-specs is omitted, the result is equivalent to using `%F %T %Z` as the chrono-specs. If `%Z` is used, it is replaced with `*f.abbrev` if `f.abbrev` is not a null pointer value. If `%Z` is used and `f.abbrev` is a null pointer value, an exception of type `format_error` is thrown. If `%z` (or a modified variant of `%z`) is used, it is formatted with the value of `*f.offset_sec` if `f.offset_sec` is not a null pointer value. If `%z` (or a modified variant of `%z`) is used and `f.offset_sec` is a null pointer value, then an exception of type `format_error` is thrown.

      template<class Duration, class TimeZonePtr, class charT>
      struct formatter<chrono::zoned_time<Duration, TimeZonePtr>, charT>
          : formatter<chrono::local-time-format-t<common_type_t<Duration, seconds>>, charT> {
        template<class FormatContext>
          typename FormatContext::iterator
          format(const chrono::zoned_time<Duration, TimeZonePtr>& tp, FormatContext& ctx) const;
      };
    
    template<class FormatContext>
      typename FormatContext::iterator
        format(const chrono::zoned_time<Duration, TimeZonePtr>& tp, FormatContext& ctx) const;
    

    -19- Effects: Equivalent to:

    sys_info info = tp.get_info();
    return formatter<chrono::local-time-format-t<common_type_t<Duration, seconds>>, charT>::
             format({tp.get_local_time(), &info.abbrev, &info.offset}, ctx);
    

Date: 2024-08-15.00:00:00

[ 2024-08-02; Reflector poll ]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

Date: 2024-07-26.21:49:06

The std::formatter<std::chrono::zoned_time<Duration, TimeZonePtr>> specialization calls `tp.get_local_time()` for the object it passes to its base class' `format` function. But `get_local_time()` does not return a local_time<Duration>, it returns local_time<common_type_t<Duration, seconds>>. The base class' `format` function is only defined for local_time<Duration>. That means this is ill-formed, even though the static assert passes:

using namespace std::chrono;
  static_assert( std::formattable<zoned_time<minutes>, char> );
  zoned_time<minutes> zt;
  (void) std::format("{}", zt); // error: cannot convert local_time<seconds> to local_time<minutes>

Additionally, it's not specified what output you should get for:

`std::format("{}", local_time_format(zt.get_local_time()));`
[time.format] p7 says it's formatted as if by streaming to an `ostringstream`, but there is no operator<< for `local-time-format-t`. Presumably it should give the same result as operator<< for a `zoned_time`, i.e. `"{:L%F %T %Z}"` with padding adjustments etc.

The proposed resolution below has been implemented in libstdc++.

History
Date User Action Args
2024-11-19 16:09:07adminsetstatus: ready -> voting
2024-08-02 21:14:41adminsetmessages: + msg14278
2024-08-02 21:14:41adminsetstatus: new -> ready
2024-07-26 21:47:51adminsetmessages: + msg14268
2024-07-26 00:00:00admincreate