Search code examples
c++c++-chronoc++23

std::chrono::parse into zoned time


I need to parse a timestamp with timezone into a c++ timepoint.

My environment is linux and gcc 14.1 and -std=c++23.

My definitions are:

namespace MyTime {
    using clock_t = std::chrono::system_clock;
    using duration_t = clock_t::duration;
    using time_point_t = std::chrono::zoned_time<duration_t>;
    
    time_point_t now()
    {
        return std::chrono::zoned_time{std::chrono::current_zone(), clock_t::now()};
    }
    
    std::string to_string(time_point_t tp)
    {
        std::ostringstream oss;
        oss << std::format("{0:%Y-%m-%dT%T%Z}", tp);
    
        return oss.str();
    }
    
    time_point_t from_string(std::string_view str)
    {
        std::istringstream iss{std::string(str)};
        time_point_t tp{};
        iss >> std::chrono::parse("%Y-%m-%dT%T%Z",tp);
        return tp;
    }
}

MyTime::from_string(std::string_view str) gives me a compilation error.

How to compile this successfully?

See it on godbolt https://godbolt.org/z/Ysoeqe9zz

EDIT: The error message.

source>: In function 'MyTime::time_point_t MyTime::from_string(std::string_view)':
<source>:27:30: error: no matching function for call to 'parse(const char [14], MyTime::time_point_t&)'
   27 |     iss >> std::chrono::parse("%Y-%m-%dT%T%Z",tp);
      |            ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/chrono:3360,
                 from <source>:1:
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/chrono_io.h:2982:5: note: candidate: 'template<class _CharT, class _Parsable>  requires  __parsable<_Parsable, _CharT, std::char_traits<_CharT> > auto std::chrono::parse(const _CharT*, _Parsable&)'
 2982 |     parse(const _CharT* __fmt, _Parsable& __tp)
      |     ^~~~~

Solution

  • zoned_time is not parsable for the simple reason that the parse doesn't in general know what time_zone to put in the zoned_time. But this is fixable.

    The to_string function currently writes out the time_zone abbreviation. But time_zone abbreviations are not unique among time_zones. Only time_zone names are unique. So the to_string function must output the time_zone name instead (or in addition to the abbreviation):

    std::string to_string(time_point_t tp)
    {
        std::ostringstream oss;
        oss << std::format("{:%Y-%m-%dT%T}", tp) << tp.get_time_zone()->name();
    
        return oss.str();
    }
    

    Now the from_string function can parse the local time into a local_time, and parse the time_zone name into a string. Then a zoned_time can be formed from that local_time and time_zone name:

    time_point_t from_string(std::string_view str)
    {
        std::istringstream iss{std::string(str)};
        std::chrono::local_time<duration_t> tp{};
        std::string tz_name;
        iss >> std::chrono::parse("%Y-%m-%dT%T%Z",tp,tz_name);
        return time_point_t{tz_name, tp};
    }