|
HowItWorks
For most functions, it's a simple matter of defining a 64 bit time type (Time_T), a 64 bit integer type to store the year (Int64_T) and rewriting the time.t functions to make use of them. Simple Matter Of Programming. localtimelocaltime() is a bit harder. It involves time zones and daylight savings. DEEP VOODOO! One option would be to ship a complete time zone database, like the DateTime Perl module does. But this is not something I want to maintain, and it means an independent time zone database for users to keep up to date independent of their system's. Instead, the trick is to have localtime64() make use of the system localtime() to do all the time zone calculations. How? First, observe that there is a repeatable cycle of years. Every 28 years the Julian calendar repeats itself. If we were using the Julian calendar then we could map any year back to an equivalent earlier one simply by moding it 28. For example, 2002 and 2058 start on the same day (Tuesday) and have the same number of days. Once you realize that, the basic algorithm is simple:
There's some edges around New Year's, but that's about it. But we don't use the Julian calendar, we use the Gregorian calendar. The difference being in the Gregorian calendar years divisible by 100 aren't leap years unless they're divisible by 400. The upshot is the Gregorian calendar cycle is 400 years long. A 32 bit localtime() can only be trusted for about 138. What to do? If you stare at a table showing what day the year starts on long enough, you can observe patterns. These patterns can be exploited to come up with an algorithm that can map any year to a point in the 28 year cycle with the same starting day of week and leap year status. #define SOLAR_CYCLE_LENGTH 28
static const int dow_year_start[SOLAR_CYCLE_LENGTH] = {
5, 0, 1, 2, /* 0 2016 - 2019 */
3, 5, 6, 0, /* 4 */
1, 3, 4, 5, /* 8 */
6, 1, 2, 3, /* 12 */
4, 6, 0, 1, /* 16 */
2, 4, 5, 6, /* 20 2036, 2037, 2010, 2011 */
0, 2, 3, 4 /* 24 2012, 2013, 2014, 2015 */
};_cycle_offset() adjusts the differences between the Gregorian and Julian cycles by adjusting the result for each year between that doesn't follow the Julian "divides by 4" leap year rule. For example, between 2008 and 2108 there is 1 exception (2100). Between 2008 and 2408 there are 3 (2100, 2200 and 2300). _safe_year() uses the offset to calculate the appropriate point in the cycle, but also makes sure the surrounding years have the same starting day-of-week and leap year status. This is important to ensure the New Years exceptions mentioned above work out. And that's about it. |
This is brilliant!
Eventhough, imo, by 2038 most likely all will already be replaced with a 64 bits machine that algorithm is still fascinating.
Cheers,
@eddy.net
Consider a 30 year mortgage.
You're not the only person to think that this is all a little premature, so I wrote down the rationale for fixing 2038 now.
PS This library does nothing to fix the internal system clock, your 32 bit computer will still fail in 2038.