HowardHinnant / date

A date and time library based on the C++11/14/17 <chrono> header
Other
3.07k stars 669 forks source link

How can I convert between std::chrono::time_point and string (2023-12-06 00:46:45.801479+08)? #805

Closed jyshaobing closed 6 months ago

jyshaobing commented 6 months ago

How can I convert between std::chrono::time_point and string (2023-12-06 00:46:45.801479+08)? I'm trying to convert time to string but failed, can someone help me? %.f does not work... I just need time with milliseconds.

#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include <iomanip>
#include "date/date.h"
#include "date/tz.h"

using TimePoint = std::chrono::time_point<std::chrono::system_clock>;

std::string to_string(const TimePoint& time_point)
{
    date::zoned_time zt{date::current_zone(), time_point};
    return date::format("%Y-%m-%d %H:%M:%S%.f %z", zt); //%.f does not work
}

TimePoint from_string(const std::string& time_str)
{
    std::istringstream ss(time_str);
    date::sys_time<std::chrono::milliseconds> tp;
    ss >> date::parse("%Y-%m-%d %H:%M:%S%.f %z", tp);

    return tp;
}
HowardHinnant commented 6 months ago

Do you get the behavior you want if you remove the %.f from the format string?

jyshaobing commented 6 months ago

Do you get the behavior you want if you remove the %.f from the format string?

no, the string will be like 2023-12-06 00:46:45.801479000+0800,and the from_string is wrong

HowardHinnant commented 6 months ago

Ok. For your use case %z won't work as it always outputs the offset with a precision of minutes. But that can be worked around by getting the offset and formatting it manually. The second issue is that your sys_time has precision nanoseconds and you want microseconds. The following code addresses both of these issues:

std::string to_string(const TimePoint& time_point)
{
    auto tp_us = date::floor<std::chrono::microseconds>(time_point);
    date::zoned_time zt{date::current_zone(), tp_us};
    auto offset = zt.get_info().offset;

    auto s = date::format("%F %T", zt);
    if (offset >= std::chrono::seconds{0})
        s += '+';
    return s + date::format("%H", offset);
}

I've also shortened the format string to "%F %T". This is nothing but a shortcut for "%Y-%m-%d %H:%M:%S" and either will do.

For from_string simply change milliseconds to microseconds (and remove the %.f). You also have the option here of using the shortcut "%F %T".

In summary, the precision of the time_point controls the precision of the formatting/parsing, as opposed to the %.f flag.

jyshaobing commented 6 months ago

Ok. For your use case %z won't work as it always outputs the offset with a precision of minutes. But that can be worked around by getting the offset and formatting it manually. The second issue is that your sys_time has precision nanoseconds and you want microseconds. The following code addresses both of these issues:

std::string to_string(const TimePoint& time_point)
{
    auto tp_us = date::floor<std::chrono::microseconds>(time_point);
    date::zoned_time zt{date::current_zone(), tp_us};
    auto offset = zt.get_info().offset;

    auto s = date::format("%F %T", zt);
    if (offset >= std::chrono::seconds{0})
        s += '+';
    return s + date::format("%H", offset);
}

I've also shortened the format string to "%F %T". This is nothing but a shortcut for "%Y-%m-%d %H:%M:%S" and either will do.

For from_string simply change milliseconds to microseconds (and remove the %.f). You also have the option here of using the shortcut "%F %T".

In summary, the precision of the time_point controls the precision of the formatting/parsing, as opposed to the %.f flag.

Thank you so much, the above code solved my problem! 1.use date::floor to remove extra nanoseconds 2.create date::zoned_time 3.format it without time offset and add time offset later