Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

schwern (1528)

schwern
  (email not shown publicly)
http://schwern.net/
AOL IM: MichaelSchwern (Add Buddy, Send Message)
Jabber: schwern@gmail.com

Schwern can destroy CPAN at his whim.

Journal of schwern (1528)

Thursday February 07, 2008
08:14 AM

Perl is now Y2038 safe

[ #35604 ]

They said it couldn't be done. They said it SHOULDN'T be done! But I have here a working 64 bit localtime_r() on a machine with just 32 bits of time_t. Time zones, daylight savings time... it all works.

$ ./miniperl -wle 'print scalar localtime(2**35)'
Mon Oct 25 20:46:08 3058

Perl will be Y2038 safe. And yes, I'm going to get it backported to 5.10.

The underlying C functions are solid, but I just sort of rammed them down perl's throat because it's 5am. Here's the patch for the intrepid.

The underlying C implementation is a heavily modified version of the code from 2038bug.com written by Paul Sheer. He came up with the same basic algorithm I did:

1) Write a 64 bit clean gmtime(), that's a SMOP and Paul did that.
2) Run your time through this new gmtime_64().
3) Change the year to a year between 2012 and 2037.
4) Run it through the 32 bit system localtime() to get time zone stuff.
5) Move the year back to the original.

The trick is using a 32 bit safe year that has the same properties as the real year. This means same leap year status and same calendar layout. Had to do some tricky Gregorian calendar math aided by Graham, Nick and Abigail who realized there's a 28 year cycle within the larger 400 year Gregorian cycle and Mark Mielke who provided a big table of 64 bit localtime() results to test against. There's also some edge cases around New Year's, but they're all taken care of.

This approach will break when daylight savings time changes, but I'd rather be off by an hour than 137 years. The full Perl patch will always prefer the system's 64 bit localtime() if one is available. As more machines upgrade time_t this hack will be used less.

The nice part is this code isn't specific to Perl and can be used to fix up any other C-based Unix program.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • He came up with the same basic algorithm I did: [snip]

    Ah, clever. Especially step 4.

    As for your patch, I think you can completely delete "if (!&tmbuf) RETPUSHUNDEF", and replace "else if (&tmbuf)" with "else", since now your tmbuf struct will always be there -- it's not a pointer returned from a function which can be NULL on error. In fact, those functions don't seem to have an error check (except for the asserts in _check_tm).
    --

    -- 
    Esli epei eto cumprenan, shris soa Sfaha.
    Aettot ibrec epesecoth, spakhea scrifeteis.

    • Yes, step four solved a large number of problems and made it much more efficient and accurate. The original 2038bug code wrote it's own 64 bit clean mktime() and was running it through that several times to get offsets and the dst information was wrong, all sorts of minor inaccuracies. But #3, picking the right safe year, was what consumed the most time.

      Don't look to hard at the perl patch, it was done in 5 minutes so that I'd have a working miniperl to point at. There's all sorts of configure and system