Quantcast
Channel: User Schwern - Stack Overflow
Viewing all articles
Browse latest Browse all 581

Answer by Schwern for mktime() call never returns. Library bug?

$
0
0

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.


Viewing all articles
Browse latest Browse all 581

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>