Congratulations, you've found a bug in time.h!
Calendars are a bunch of cycles (solar cycles, lunar cycles, arbitrary cycles) that we try to force into synchronization. Already hard. The arbitrary decisions of time zones and daylight savings time make that even harder. Not just because they're arbitrary, solvable with a database, but because they cause discontinuities in the calendar. You can write a perfectly valid seeming calendar time and time zone which never happened, or which happened twice. time.h doesn't always deal with them correctly or at all.
You're asking mktime
to convert calendar times which do not exist. I don't mean because you're asking for 25 o'clock, that's a different thing. I mean moving clocks forward an hour means there are combinations of time zones, dates, and times which do not map to a point in time_t. Moving clocks backwards means one combination of time zone, date, and time can map to two points in time. While time_t
is a steady beat of seconds since midnight January 1st, 1970 UTC.
For example, if we look at Africa/Algers...
After Sunday, 25 April, 1971 10:59:59 PM:
Clocks were moved forward to become Monday, 26 April, 1971 12:00:00 AM
That means in Africa/Algers on 1971/04/25 11 PM to 11:59:59 PM did not happen. There was no 1971/04/25 11:00 PM nor 1971/04/25 11:01 PM nor 1971/04/25 11:59 PM.
There is no time_t
you can put into localtime
to correctly get back a tm
of 1971/04/25 11 PM in Africa/Algers. mktime
and localtime
are supposed to round-trip, you should be able to put the time_t
returned by mktime
back into localtime
and get the same original tm
back. But these discontinuities mess that up. This can result in... creative results or crashes or infinite loops.
Similar for Africa/Tripoli
After Wednesday, 31 March, 1982 11:59:59 PM:
Clocks were moved forward to become Thursday, 01 April, 1982 01:00:00 AM
1982/04/01 0:00:00 did not happen in Africa/Tripoli.
And Africa/Windhoek.
After Sunday, 04 September, 1994 01:59:59 AM:
Clocks were moved forward to become Sunday, 04 September, 1994 03:00:00 AM
1994/09/04 2:00:00 did not happen in Africa/Windhoek.
C Standard 7.27.2.3.3 says...
The mktime function returns the specified calendar time encoded as a value of type time_t. If the calendar time cannot be represented, the function returns the value (time_t)(−1).
When you ask mktime
for a calendar time which did not happen you should get back -1, or its equivalent after the jump. For example, on MacOS...
// After Sunday, 25 April, 1971 10:59:59 PM// Clocks were moved forward to become Monday, 26 April, 1971 12:00:00 AMAfrica/Algiers Local 1971/04/25 23:00:00 dst:-1--> 41468400 1971/04/26 0:00:00 dst: 1 Local 1971/04/25 24:00:00 dst:-1--> 41468400 1971/04/26 0:00:00 dst: 1 Local 1971/04/25 25:00:00 dst:-1--> 41472000 1971/04/26 1:00:00 dst: 1 Local 1971/04/25 26:00:00 dst:-1--> 41475600 1971/04/26 2:00:00 dst: 1 Local 1971/04/25 27:00:00 dst:-1--> 41479200 1971/04/26 3:00:00 dst: 1
It has interpreted the non-existent 1971/04/25 23:00:00 to be the existent 1971/04/26 0:00:00. Presumably under the logic that 41468399 is 1971/04/25 22:59:59 and 1971/04/25 23:00:00 is one second later, so it should use localtime(41468400)
which is 1971/04/26 0:00:00.
However a particular mktime
deals with these discontinuities, it definitely shouldn't hang. If it hangs that indicates a bug in the library. mktime
generally has to do a lot of looping through progressively better guesses which indicates probably an infinite loop somewhere.
FWIW here's what MacOS returns.
// After Sunday, 25 April, 1971 10:59:59 PM// Clocks were moved forward to become Monday, 26 April, 1971 12:00:00 AMAfrica/Algiers Local 1971/04/25 23:00:00 dst:-1--> 41468400 1971/04/26 0:00:00 dst: 1 Local 1971/04/25 24:00:00 dst:-1--> 41468400 1971/04/26 0:00:00 dst: 1 Local 1971/04/25 25:00:00 dst:-1--> 41472000 1971/04/26 1:00:00 dst: 1 Local 1971/04/25 26:00:00 dst:-1--> 41475600 1971/04/26 2:00:00 dst: 1 Local 1971/04/25 27:00:00 dst:-1--> 41479200 1971/04/26 3:00:00 dst: 1// After Wednesday, 31 March, 1982 11:59:59 PM// Clocks were moved forward to become Thursday, 01 April, 1982 01:00:00 AMAfrica/Tripoli Local 1982/04/01 0:00:00 dst:-1--> 386463600 1982/04/01 1:00:00 dst: 1 Local 1982/04/01 1:00:00 dst:-1--> 386463600 1982/04/01 1:00:00 dst: 1 Local 1982/04/01 2:00:00 dst:-1--> 386467200 1982/04/01 2:00:00 dst: 1 Local 1982/04/01 3:00:00 dst:-1--> 386470800 1982/04/01 3:00:00 dst: 1 Local 1982/04/01 4:00:00 dst:-1--> 386474400 1982/04/01 4:00:00 dst: 1// After Sunday, 04 September, 1994 01:59:59 AM// Clocks were moved forward to become Sunday, 04 September, 1994 03:00:00 AMAfrica/Windhoek Local 1994/09/04 2:00:00 dst:-1--> 778640400 1994/09/04 3:00:00 dst: 1 Local 1994/09/04 3:00:00 dst:-1--> 778640400 1994/09/04 3:00:00 dst: 1 Local 1994/09/04 4:00:00 dst:-1--> 778644000 1994/09/04 4:00:00 dst: 1 Local 1994/09/04 5:00:00 dst:-1--> 778647600 1994/09/04 5:00:00 dst: 1 Local 1994/09/04 6:00:00 dst:-1--> 778651200 1994/09/04 6:00:00 dst: 1
It does cause the weird situation that two different calendar times map to the same time_t. This should only happen when clocks move backward not forward.
Maybe it's because you're right on the boundary? What if we ask for 1971/04/25 23:01:02? Clearly a time which does not exist...
// After Sunday, 25 April, 1971 10:59:59 PM// Clocks were moved forward to become Monday, 26 April, 1971 12:00:00 AMAfrica/Algiers Local 1971/04/25 23:01:02 dst:-1--> 41468462 1971/04/26 0:01:02 dst: 1 Local 1971/04/25 24:01:02 dst:-1--> 41468462 1971/04/26 0:01:02 dst: 1 Local 1971/04/25 25:01:02 dst:-1--> 41472062 1971/04/26 1:01:02 dst: 1 Local 1971/04/25 26:01:02 dst:-1--> 41475662 1971/04/26 2:01:02 dst: 1 Local 1971/04/25 27:01:02 dst:-1--> 41479262 1971/04/26 3:01:02 dst: 1// After Wednesday, 31 March, 1982 11:59:59 PM// Clocks were moved forward to become Thursday, 01 April, 1982 01:00:00 AMAfrica/Tripoli Local 1982/04/01 0:01:02 dst:-1--> 386463662 1982/04/01 1:01:02 dst: 1 Local 1982/04/01 1:01:02 dst:-1--> 386463662 1982/04/01 1:01:02 dst: 1 Local 1982/04/01 2:01:02 dst:-1--> 386467262 1982/04/01 2:01:02 dst: 1 Local 1982/04/01 3:01:02 dst:-1--> 386470862 1982/04/01 3:01:02 dst: 1 Local 1982/04/01 4:01:02 dst:-1--> 386474462 1982/04/01 4:01:02 dst: 1// After Sunday, 04 September, 1994 01:59:59 AM// Clocks were moved forward to become Sunday, 04 September, 1994 03:00:00 AMAfrica/Windhoek Local 1994/09/04 2:01:02 dst:-1--> 778640462 1994/09/04 3:01:02 dst: 1 Local 1994/09/04 3:01:02 dst:-1--> 778640462 1994/09/04 3:01:02 dst: 1 Local 1994/09/04 4:01:02 dst:-1--> 778644062 1994/09/04 4:01:02 dst: 1 Local 1994/09/04 5:01:02 dst:-1--> 778647662 1994/09/04 5:01:02 dst: 1 Local 1994/09/04 6:01:02 dst:-1--> 778651262 1994/09/04 6:01:02 dst: 1
Same thing, now even more questionable, but better than hanging.