Alexey Starinsky
Alexey Starinsky

Reputation: 4339

Create std::chrono::time_point from string

A program like this

int
main()
{
    using namespace date;
    std::cout << std::chrono::system_clock::now() << '\n';
}

prints something like 2017-09-15 13:11:34.356648.

Assume I have a string literal "2017-09-15 13:11:34.356648" in my code.

What is the right way to create std::chrono::time_point from it in C++20?

Upvotes: 6

Views: 2554

Answers (2)

user3464207
user3464207

Reputation: 1

I couldn't find an answer for c++17, so I wrote it myself. Sorry, if I re-inventing the wheel, but it's really ugly 100 nanoseconds here.

The format "%Y-%m-%d %H:%M:%S.7sybmols_of_hundreds_ns"

And, yeah, you gonna look how to split the string

    std::string Utils::From(const std::chrono::system_clock::time_point& datetime) {
            auto t = std::chrono::system_clock::to_time_t(datetime);
            auto datetime_se = datetime.time_since_epoch();
            auto datetime_se_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(datetime_se);
            auto hundreds_ns = (datetime_se_ns % 1000000000)/100;  // 1000000000 - ns, std::chrono::system_clock 100 ns   
            std::tm tm = {};
            gmtime_s(&tm, &t);
            std::stringstream ss;
            ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
            ss << '.' << std::setfill('0') << std::setw(7) << hundreds_ns.count();
            return ss.str();
        }
        
           std::chrono::system_clock::time_point Utils::ToTimePoint(const std::string& datetime) {
            auto tokens = Utils::SplitString(datetime, ".");
            auto time_t = tokens[0];
            auto hundreds_ns_str = tokens[1];
        
            std::tm tm = {};
            std::istringstream ss(time_t);
            ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
            auto t = _mkgmtime(&tm);
            std::chrono::system_clock::time_point tp = std::chrono::system_clock::from_time_t(t);
        
            std::istringstream ss_h_ns(hundreds_ns_str);
            int hundreds_ns;
            ss_h_ns >> hundreds_ns;
            tp += std::chrono::duration_cast<std::chrono::system_clock::duration>(
                std::chrono::nanoseconds(hundreds_ns*100));
            return tp ;
        }

Upvotes: 0

Howard Hinnant
Howard Hinnant

Reputation: 219345

Just to be clear, there is no namespace date in C++20. So the code in the question should look like:

#include <chrono>
#include <iostream>

int
main()
{
    std::cout << std::chrono::system_clock::now() << '\n';
}

The inverse of this is std::chrono::parse which operates on streams. You can also use std::chrono::from_stream if desired. parse is a stream manipulator that makes the syntax a little nicer.

istringstream in{"2017-09-15 13:11:34.356648"};
system_clock::time_point tp;
in >> parse("%F %T", tp);

(I've dropped the namespaces just to keep the verbosity down)

The locale used is the global locale in effect at the time the istringstream is constructed. If you prefer another locale use the imbue member function to set the desired locale. The locale will only impact the decimal point character in this example.

The %T will read up to whatever precision the input time_point has (which varies with platform from microseconds to nanoseconds). If you want to be sure you can parse nanoseconds even if system_clock::time_point is coarser than that, then you can parse into a sys_time<nanoseconds> which is a type alias for time_point<system_clock, nanoseconds>.

sys_time<nanoseconds> tp;
in >> parse("%F %T", tp);

If the input stream has precision less than the input time_point, there is no problem. What the stream has will be read, and no more. If the input stream has precision finer than the input time_point, then the parse stops at the precision of the time_point, and the remaining digits are left unparsed in the stream.

Other strptime-like parsing flags are supported.

Upvotes: 5

Related Questions