10

Fun fact that I'm sure most of us who get to play in the time realms know - there are date/times that can appear valid but actually do not exist, e.g. 2:30 AM on a daylight savings switching time.

Is there a way in C++ (standard or Windows) to figure out if a given date/time is valid in a given time zone specification?

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
dlanod
  • 8,664
  • 8
  • 54
  • 96
  • An intriguing question, certainly. Do you also need to support leap second days, like 30-June-2015, 23.59.60? (If so that rules out Google's time API) I *think* boost date-time libraries would be the best starting point. I use those libraries but not for this particular thing. – Bathsheba Apr 08 '16 at 07:13
  • I've tagged "boost-date-time" and, perhaps naughtily, "boost" since I imagine a solution might be either available out of the box for this, or not particularly difficult to implement in that framework. – Bathsheba Apr 08 '16 at 07:15
  • The standard C++ library is inept at dealing with time. If you target Windows then you could use SystemTimeToFileTime() or SystemTimeToTzSpecificLocalTime(). Passing a bad date makes the function fail with ERROR_INVALID_PARAMETER. If it has to be cross-platform then you have to go shopping for a library. – Hans Passant Apr 08 '16 at 07:25
  • @HansPassant If that's the case it's MS' implementation that is inept in this case. The standard C++ library has `time_t`, `struct tm`, `mktime` and `localtime` that should be enough to perform the task. If it doesn't work as the standard prescribe don't blame the standard... – skyking Apr 08 '16 at 08:04
  • 1
    The C++ Standard library is *intentionally* inept. There are too many edge cases that mean that a general all-purpose date time library *cannot* be adequately standardised. The closest one I've come across (boost) relies upon a configuration file. Look at the mess that Java has got itself into with that simply dreadful `java.util.Date` and `java.util.Calendar` stuff. – Bathsheba Apr 08 '16 at 08:07
  • 1
    Maybe Howard Hinnant's [library](https://github.com/HowardHinnant/date) is an alternative? [(cppcon talk)](https://www.youtube.com/watch?v=tzyGjOm8AKo) – nonsensation Apr 08 '16 at 09:28
  • @HansPassant given we're talking TZ-specific times, I suspect you're thinking of `TzSpecificLocalTimeToSystemTime` rather than `SystemTimeToTzSpecificLocalTime`. I can confirm that it's what prompted the question - it converted a non-existent time to a "best guess" of an hour prior. Very unexpected. – dlanod Apr 11 '16 at 00:32

3 Answers3

1

Using this free, open-source library:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono_literals;
    try
    {
        auto zone = locate_zone("America/New_York");
        zone->to_sys(local_days{mar/13/2016} + 2h + 30min);
    }
    catch (const std::exception& e)
    {
        std::cout << e.what() << '\n';
    }
}

The output of this program is:

2016-03-13 02:30 is in a gap between
2016-03-13 02:00:00 EST and
2016-03-13 03:00:00 EDT which are both equivalent to
2016-03-13 07:00:00 UTC

In short, this program attempts to translate a locale date/time of 2016-03-13 02:30:00 to UTC using the timezone "America/New_York". The translation throws an exception because the local time doesn't exist. An exception is also thrown (with a different error message), if the local time is ambiguous, such as when setting the local clock back from daylight saving.

The library also provides syntax for avoiding these exceptions if that is the desire.

This works on VS-2013, VS-2015, clang, and gcc. It requires C++11, C++14 or C++1z.

The link above points to the documentation. Here is the github repository:

https://github.com/HowardHinnant/date

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
1

Using Windows-specific functions, you can make a call to TzSpecificLocalTimeToSystemTime() followed by a call to SystemTimeToTzSpecificLocalTime() with the relevant time zone.

Upon completion, if the two differ you have an invalid time as TzSpecificLocalTimeToSystemTime converts the invalid time to a "real" time.

bool IsValidTime(TIME_ZONE_INFORMATION tz, SYSTEMTIME st)
{
    SYSTEMTIME utcSystemTime, st2;
    TzSpecificLocalTimeToSystemTime(&tz, &st, &utcSystemTime);
    SystemTimeToTzSpecificLocalTime(&tz, &utcSystemTime, &st2);

    return (st != st2);
}
dlanod
  • 8,664
  • 8
  • 54
  • 96
0

The time_t format is an integer value (seconds since 1/1/1970 00:00 UTC) so every possible value of time_t exists (within the limits of the integer type used of course).

On the other hand, not all possible values of "struct tm" (where you have members for day, month, year, hour, minute and second) exist (and this is probably what you mean). To check whether a "struct tm" exists, you have to do the following:

  • Set the member tm_isdst to -1. This indicates that you don't know whether its DST or not.
    • Convert the "struct tm" to a time_t using mktime().
    • Convert the time_t back to "struct tm" using localtime().
    • Compare the resulting "struct tm" with the initial "struct tm", ignore the tm_isdst field (or use it to see whether you are in DST or not).
Patrick
  • 23,217
  • 12
  • 67
  • 130
  • What does `mktime` do if you supply a value at 02:30 when going back to normal time? The problem here is that the calendar time would be repeating the 02:xx hour twice (once with dst and once without). If you don't know which you mean the computer probably can't tell you either... – skyking Apr 08 '16 at 07:56
  • The standard doesn't require `time_t` to be an integer, but otherwise it seems that the standard would imply that your solution would work. If it will actually work on the implementation is quite another question... – skyking Apr 08 '16 at 07:59
  • 1
    @Skyking: you're right (partially). When the clock is set back for one hour (DST --> non-DST), and you are not specifying whether you are in DST or not, the CRT will guess, but at least I would hope that it would return either 2:30 DST, or 2:30 non-DST. Anyway, best way to test it is to try it. The result might indeed depend on the implementation of the CRT. – Patrick Apr 08 '16 at 16:56