Title
Stream insertion for `chrono::local_time` should be constrained
Status
new
Section
[time.clock.local]
Submitter
Jonathan Wakely

Created on 2025-05-16.00:00:00 last changed 3 weeks ago

Messages

Date: 2025-05-16.11:16:52

Proposed resolution:

This wording is relative to N5008.

  1. Modify [time.clock.local] as indicated:

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

    -?- Constraints: os << sys_time<Duration>{lt.time_since_epoch()} is a valid expression.

    -2- Effects:

     os << sys_time<Duration>{lt.time_since_epoch()};
    

    -3- Returns: `os`.

Date: 2025-05-16.00:00:00

Stream insertion for `chrono::local_time` is defined in terms of conversion to `chrono::sys_time`, but not all `chrono::sys_time` specializations can be inserted into an ostream, because one of the overloads is constrained and the other requires convertibility to `chrono::sys_days` (see [time.clock.system.nonmembers]).

This means the following code fails to compile:


#include <iostream>
#include <chrono>

template<typename T>
concept ostream_insertable = requires (std::ostream& o, const T& t) { o << t; };

using D = std::chrono::duration<double>;

int main() {
  if constexpr (ostream_insertable<std::chrono::sys_time<D>>)
    std::cout << std::chrono::sys_time<D>{};
  if constexpr (ostream_insertable<std::chrono::local_time<D>>)
    std::cout << std::chrono::local_time<D>{}; // FAIL
}
The first condition is false, because there's no overload that's suitable. The second is true, because the operator<< overload for `chrono::local_time` isn't constrained and so insertion appears to be valid. But actually trying to use it is ill-formed, because it tries to convert the local_time<D> to a sys_time<D> and then insert that, which isn't valid.

History
Date User Action Args
2025-05-16 11:16:52adminsetmessages: + msg14752
2025-05-16 00:00:00admincreate