Closed pawnissimo closed 2 years ago
I'm going to make a couple of assumptions:
timeZone.getTimeZone()
returns a string
or string_view
.t_utc
is.I don't know what your inputs are, or if you are wanting the current time in your current time zone.
It would also be good to know which C++ standard you're using. Later versions allow more convenient syntax.
I would like to know the date and time of day (factoring in DST if applicable) corresponding to "t_utc" in the timezone defined by tzone.
#include "date/tz.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
std::string tz_name = "America/New_York";
date::utc_clock::time_point t_utc{1633448911665608us};
auto leap_second_info = date::get_leap_second_info(t_utc);
auto t_sys = date::clock_cast<std::chrono::system_clock>(t_utc);
date::zoned_time t_zoned{tz_name, t_sys};
auto t_local = t_zoned.get_local_time();
auto today_local = std::chrono::floor<date::days>(t_local);
date::hh_mm_ss time_local{t_local - today_local};
date::year_month_day date_local{today_local};
date::year y = date_local.year();
date::month m = date_local.month();
date::day d = date_local.day();
std::chrono::hours h = time_local.hours();
std::chrono::minutes M = time_local.minutes();
std::chrono::seconds s = time_local.seconds()
+ std::chrono::seconds{leap_second_info.is_leap_second};
}
get_leap_second_info
returns a struct called date::leap_second_info
containing two items: 1) a bool
that is true if t_utc
points within a leap second, and 2) a count of leap seconds since 1970. The second part isn't needed for this computation.{y, m, d, h, M, s}
structure, one first needs to convert to a system_clock
-based time_point
. date::clock_cast
does this. If t_utc
points within a leap second, this conversion maps to the last instant prior to the leap second (system_clock
does not represent leap seconds).zoned_time
with the time zone name and the system_clock::time_point
..get_local_time()
returns the local time from a zoned_time
. This does take DST into account if applicable.floor
the local time using days
you get a count of local days since 1970-01-01. This is a date in a {count}
data structure.std::chrono::duration
is stored in a hh_mm_ss
which has the job of converting the duration
into a {hours, minutes, seconds, subseconds}
data structure.{count}
data structure can be converted to a {year, month, day}
data structure named date::year_month_day
.year_month_day
and hh_mm_ss
. If the original t_utc
was pointing to a leap second, that fact is added back into the result for seconds
.If you would like integral representations of these fields then:
year
has an explicit conversion to int
.month
and day
have explicit conversions to unsigned
.duration
s have a member function .count()
.@HowardHinnant Thank you very much for the detailed walkthrough. So I was able to compile this code on Linux with C++17 but I get compile time errors in tz.h with VisualStudio2019/C++17 (also tried C++20).
The errors occur while compiling these two lines: date::zoned_time t_zoned{tz_name, t_sys}; auto t_local = t_zoned.get_local_time();
_1>\date\include\date\tz.h(1651,1): error C2819: type 'std::basic_string<char,std::char_traits
This would be the corresponding code in tz.h:
template <class Duration, class TimeZonePtr> inline local_time<typename zoned_time<Duration, TimeZonePtr>::duration> zoned_time<Duration, TimeZonePtr>::get_localtime() const { return zone->tolocal(tp); }
zone_ does not seem to be of the correct type.
I have a workaround using locate_zone and make_zoned in place of date::zoned_time t_zoned{tz_name, t_sys}; but I read in an older post that make_zoned will be phased out.
Glad you found a workaround. I don't have Visual to experiment with, but here is my guess at another workaround: Change:
date::zoned_time t_zoned{tz_name, t_sys};
To:
date::zoned_time<std::chrono::system_clock::duration> t_zoned{tz_name, t_sys};
My theory is that Visual is not correctly implementing the C++17 CTAD language feature. CTAD is a feature that deduces the correct zoned_time
template arguments from the constructor argument list. Indeed, pre-CTAD, deducing this template parameter was the only reason the make_zoned
factory function was introduced in the first place. And CTAD is the reason that the make_zoned
factory function is now no longer needed (modulo broken compilers).
You may or may not have a similar issue with hh_mm_ss
, which also is templated on a duration
type.
One more thought: zoned_time
has deduction guides which help CTAD to figure things out. But these are guarded in tz.h by HAS_DEDUCTION_GUIDES=1
. This guard is triggered by cplusplus >= 201703. If Visual is not correctly setting cplusplus, this could also cause these symptoms. So another workaround is to define the configuration macro HAS_DEDUCTION_GUIDES=1
in the Visual Studio IDE (https://docs.microsoft.com/en-us/cpp/build/working-with-project-properties?view=msvc-160#to-create-a-user-defined-macro).
Thanks, you are right about HAS_DEDUCTION_GUIDES. __cplusplus remains set to 1997 in VS regardless of the configured C++ standard. However there's a compiler option to override this behavior; documented here: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-160
Tried it and seems to work. Thanks for all the help.
That's good to know, thanks.
const date::tzdb& tzdb = date::get_tzdb(); const date::time_zone* tzone = tzdb.locate_zone(timeZone.getTimeZone()); std::chrono::_V2::system_clock::time_point t_sys = date::to_sys_time(t_utc); auto t_zoned = make_zoned(tzone, t_sys);
What is the correct way of retrieving the corresponding year, month, day, hour, minute, second (in variables) for t_zoned ?