I am trying to determine whether a given time_t
value is in DST for the given timezone. So I wrote something like the following:
inline bool is_DST(time_t t) {
struct tm* ti = localtime(&t);
return ti->tm_isdst > 0;
}
This works great for current-ish times. For example, on a current time_t value it returns true
. For the J2000 epoch, it returns false
. Nice.
Unfortunately, the MSVC variant of localtime(...)
returns nullptr
on negative input. Reading the docs linked above tells me it also will fail after year 3000. So, for example (time_t)(-1)
(corresponding to 1969-12-31 23:59:59) will cause a segfault.
This is bad. 1969 wasn't that long ago. Standard practice is to extrapolate UTC with the assumption that there are no leap seconds either before 1972 or anywhere in the future (until announced).*
What are my options to implement is_DST(...)
that will work for the range of dates given by a 64-bit time_t
(±2.9*1011 years or so) based on above extrapolation?
*There's some question as to whether this is the right thing. Leap seconds were introduced in 1972, and extrapolating UTC backward means you have to assume there weren't leap seconds (there would have been). And extrapolating UTC forward is hard too, because Earth's rotation makes leap seconds unpredictable.
Here is the library ildjarn refers to in the comments:
https://github.com/HowardHinnant/date
It is open source and works on VS-2013 and later. Here is how you would use it to implement is_DST
:
#include "tz.h"
#include <ctime>
bool
is_DST(std::time_t t)
{
using namespace std::chrono;
using namespace date;
return current_zone()->get_info(system_clock::from_time_t(t)).save != 0min;
}
This will work for negative time_t
, but not for the full range of a 64bit time_t
(which would be +/- 292 billion years). But it will easily go back to the mid 1800's when uniform time was first introduced.
On Windows you will either have to install libcurl, or manually download the IANA timezone database.
If you would like to answer this question for some time zone other than the computer's current one, that is easy enough:
return locate_zone("Europe/Monaco")
->get_info(system_clock::from_time_t(t)).save != 0min;
The range on Windows is [-27258-04-19 21:11:54.5224192, 31197-09-14 02:48:05.4775807]
. The restriction on this range is that this is the range of the VS system_clock::time_point
(which has a precision of 100ns, and is stored as a int64_t
).