I'm trying to use the date (https://howardhinnant.github.io/date/date.html) library to convert a data/time string into date/time belonging to a specific time zone.
I'm using this:
std::string t = "2024-01-02T14:42:27";
std::stringstream str(t);
std::chrono::time_point< std::chrono::system_clock, std::chrono::seconds > src_timestamp;
date::from_stream( str, "%Y-%m-%dT%H:%M:%S", result );
const auto src_tz = locate_zone(std::string("CET"));
auto zoned_src_timestamp = make_zoned(src_tz, src_timestamp);
std::cout << zoned_src_timestamp.get_local_time() << " TZ\n";
When I print zoned_src_timestamp.get_local_time() I expect to see my local time, but I get "2024-01-02 15:42:27" instead. This is not the local time that I'm expecting.
I'm trying to parse a date/time string into a time_point and use it with a timezone to have a local time that I can convert into a different timezone.
I'm trying to parse a date/time string into a time_point and use it with a timezone to have a local time that I can convert into a different timezone.
std::string t = "2024-01-02T14:42:27";
std::stringstream str(t);
So far so good. But I'm interpreting "use it with a timezone" to mean, "parse the time as local time in a timezone", namely "CET". To do this the type of src_timestamp
must be a local_time
instantiation as opposed to a sys_time
instantiation.
sys_time
is that family of time_point
based on system_clock
and always means Unix Time. local_time
represents a local time that has yet to be paired with a specific time_zone
. There is a specific type alias for seconds-precision called local_seconds
that can be handy to use:
date::local_seconds src_timestamp;
If you prefer another precision, say milliseconds
, then that is:
date::local_time<std::chrono::millliseconds> src_timestamp;
Then do the parse with the local_time
:
date::from_stream( str, "%Y-%m-%dT%H:%M:%S", src_timestamp );
If you print this back out, you'll get exactly the same time. Now use it to create a zoned_time
with the desired time_zone
. You can use either a time_zone const*
or a string to specify the time_zone
:
auto zoned_src_timestamp = date::zoned_time("CET", src_timestamp);
This assumes C++17 or higher. make_zoned
is convenient with C++11/14 as it works around the lack of CTAD.
The construction of zoned_src_timestamp
creates a pair data structure of {time_zone const*, sys_time}
. The sys_time
is computed at construction by using the time_zone const*
to convert the local_time
to sys_time
. If you ask this object for the local time: zoned_src_timestamp.get_local_time()
then it will just convert the sys_time
back to the local_time
you used to construct it with.
One can also construct a zoned_time
with a sys_time
, in which case the conversion from local is avoided at construction time, and the sys_time
is simply stored.
One can also construct a zoned_time
with another zoned_time
. I believe this is what you're looking for:
auto zoned_local_timestamp = date::zoned_time{date::current_zone(),
zoned_src_timestamp};
This uses current_zone()
as the time_zone
, and zoned_src_timestamp
as the "time stamp".
current_zone()
simply returns a time_zone const*
to your computer's currently set local time zone.
Construction with zoned_src_timestamp
uses its sys_time
to construct the new zoned_time
, so that both zoned_time
represent the same instant in time, though in (potentially) different timezones. I.e. they have the same UTC time, but different local times.
Then you can get the local time out of zoned_local_timestamp
:
std::cout << zoned_local_timestamp.get_local_time() << " TZ\n";
zoned_time
is meant to simply be a convenience wrapper. You can also work directly with time_zone
to accomplish the same thing:
std::string t = "2024-01-02T14:42:27";
std::stringstream str(t);
date::local_seconds src_timestamp;
date::from_stream( str, "%Y-%m-%dT%H:%M:%S", src_timestamp );
auto utc_timestamp = date::locate_zone("CET")->to_sys(src_timestamp);
auto local_timestamp = date::current_zone()->to_local(utc_timestamp);
std::cout << local_timestamp << " TZ\n";
zoned_time
becomes much more convenient when you want to format with UTC offsets or time zone abbreviations as the zoned_time
collects everything there is to know about a time_zone
/local_time
combination into a single object.
zoned_time
would also be more convenient in generic code where the "time stamp" could be either sys_time
or local_time
.
Finally, all of this is now part of C++20. Implementations are still coming on line. MSVC has it. gcc-14 has it, with partial implementations in earlier versions. LLVM has partial implementations. If you are lucky enough to be working with a C++20 implementation, prefer that over date
. Everything will be in namespace std::chrono
instead of namespace date
.