Make Time::Local run on perls older than 5.12 again
authorFlorian Ragwitz <rafl@debian.org>
Sun, 2 Jan 2011 15:25:52 +0000 (16:25 +0100)
committerFlorian Ragwitz <rafl@debian.org>
Sun, 2 Jan 2011 15:34:49 +0000 (16:34 +0100)
With 5.12, this module was made y2038-safe, even on systems with a 32-bit
time_t. Unfortunately, that broke things on older perl versions and blead became
this module's upstream as the y2038-safety couldn't easily be backported.

This change also doesn't attempt to backport y2038 support. It merely restores
the old behaviour including the y2038 limitations on older versions of perl on
platforms without a 64-bit time_t.

With this, we can at least move Time::Locale's upstream back to CPAN. Making it
y2038-safe for old perls on 32-bit time_t platforms probably shouldn't be a
priority anyway. There's Time::y2038, which does everything this module does,
and then some, while being y2038-safe.

At some point, this module should probably be deprecated.

Porting/Maintainers.pl
ext/Time-Local/lib/Time/Local.pm
ext/Time-Local/t/Local.t
pod/perldelta.pod

index 89deb84..a0f3e2c 100755 (executable)
@@ -1520,7 +1520,7 @@ use File::Glob qw(:case);
     'Time::Local' =>
        {
        'MAINTAINER'    => 'drolsky',
-       'DISTRIBUTION'  => 'DROLSKY/Time-Local-1.1901.tar.gz',
+       'DISTRIBUTION'  => 'FLORA/Time-Local-1.2000.tar.gz',
        'FILES'         => q[ext/Time-Local],
        'EXCLUDED'      => [ qw(t/pod-coverage.t t/pod.t) ],
        'UPSTREAM'      => 'blead',
index 96a688d..0e9c4ac 100644 (file)
@@ -6,7 +6,7 @@ use Config;
 use strict;
 
 use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
-$VERSION   = '1.1901_01';
+$VERSION   = '1.2000';
 
 @ISA       = qw( Exporter );
 @EXPORT    = qw( timegm timelocal );
@@ -28,8 +28,23 @@ use constant SECS_PER_MINUTE => 60;
 use constant SECS_PER_HOUR   => 3600;
 use constant SECS_PER_DAY    => 86400;
 
-# localtime()'s limit is the year 2**31
-my $MaxDay = 365 * (2**31);
+my $MaxDay;
+if ($] < 5.012000) {
+    my $MaxInt;
+    if ( $^O eq 'MacOS' ) {
+        # time_t is unsigned...
+        $MaxInt = ( 1 << ( 8 * $Config{ivsize} ) ) - 1;
+    }
+    else {
+        $MaxInt = ( ( 1 << ( 8 * $Config{ivsize} - 2 ) ) - 1 ) * 2 + 1;
+    }
+
+    $MaxDay = int( ( $MaxInt - ( SECS_PER_DAY / 2 ) ) / SECS_PER_DAY ) - 1;
+}
+else {
+    # recent localtime()'s limit is the year 2**31
+    $MaxDay = 365 * (2**31);
+}
 
 # Determine the EPOC day for this machine
 my $Epoc = 0;
@@ -266,6 +281,21 @@ absolute four digit year instead.
 The scheme above allows interpretation of a wide range of dates,
 particularly if 4-digit years are used.
 
+=head2 Limits of time_t
+
+On perl versions older than 5.12.0, the range of dates that can be
+actually be handled depends on the size of C<time_t> (usually a signed
+integer) on the given platform. Currently, this is 32 bits for most
+systems, yielding an approximate range from Dec 1901 to Jan 2038.
+
+Both C<timelocal()> and C<timegm()> croak if given dates outside the
+supported range.
+
+As of version 5.12.0, perl has stopped using the underlying time
+library of the operating system it's running on and has its own
+implementation of those routines with a safe range of at least
++/ 2**52 (about 142 million years).
+
 =head2 Ambiguous Local Times (DST)
 
 Because of DST changes, there are many time zones where the same local
@@ -288,6 +318,19 @@ for the "Europe/Paris" time zone, the local clock jumped from
 If the C<timelocal()> function is given a non-existent local time, it
 will simply return an epoch value for the time one hour later.
 
+=head2 Negative Epoch Values
+
+On perl version 5.12.0 and newer, negative epoch values are fully
+supported.
+
+On older versions of perl, negative epoch (C<time_t>) values, which
+are not officially supported by the POSIX standards, are known not to
+work on some systems. These include MacOS (pre-OSX) and Win32.
+
+On systems which do support negative epoch values, this module should
+be able to cope with dates before the start of the epoch, down the
+minimum value of time_t for the system.
+
 =head1 IMPLEMENTATION
 
 These routines are quite efficient and yet are always guaranteed to
index 61a15a8..521cac0 100644 (file)
@@ -19,12 +19,17 @@ my @time =
    # leap day
    [2020,  2, 29, 12, 59, 59],
    [2030,  7,  4, 17, 07, 06],
-   [2038,  1, 17, 23, 59, 59],     # last full day in any tz
 
-   # more than 2**31 time_t
-   [2258,  8, 11,  1, 49, 17],
+# The following test fails on a surprising number of systems
+# so it is commented out. The end of the Epoch for a 32-bit signed
+# implementation of time_t should be Jan 19, 2038  03:14:07 UTC.
+#  [2038,  1, 17, 23, 59, 59],     # last full day in any tz
   );
 
+# more than 2**31 time_t - requires a 64bit safe localtime/gmtime
+push @time, [2258,  8, 11,  1, 49, 17]
+    if $] >= 5.012000;
+
 my @bad_time =
     (
      # month too large
@@ -82,35 +87,42 @@ for (@time, @neg_time) {
     $year -= 1900;
     $mon--;
 
-    # Test timelocal()
-    {
-        my $year_in = $year < 70 ? $year + 1900 : $year;
-        my $time = timelocal($sec,$min,$hour,$mday,$mon,$year_in);
-
-        my($s,$m,$h,$D,$M,$Y) = localtime($time);
-
-        is($s, $sec, "timelocal second for @$_");
-        is($m, $min, "timelocal minute for @$_");
-        is($h, $hour, "timelocal hour for @$_");
-        is($D, $mday, "timelocal day for @$_");
-        is($M, $mon, "timelocal month for @$_");
-        is($Y, $year, "timelocal year for @$_");
-    }
-
-
-    # Test timegm()
-    {
-        my $year_in = $year < 70 ? $year + 1900 : $year;
-        my $time = timegm($sec,$min,$hour,$mday,$mon,$year_in);
-
-        my($s,$m,$h,$D,$M,$Y) = gmtime($time);
-
-        is($s, $sec, "timegm second for @$_");
-        is($m, $min, "timegm minute for @$_");
-        is($h, $hour, "timegm hour for @$_");
-        is($D, $mday, "timegm day for @$_");
-        is($M, $mon, "timegm month for @$_");
-        is($Y, $year, "timegm year for @$_");
+    SKIP: {
+        skip '1970 test on VOS fails.', 12
+            if $^O eq 'vos' && $year == 70;
+        skip 'this platform does not support negative epochs.', 12
+            if $year < 70 && ! $neg_epoch_ok;
+
+        # Test timelocal()
+        {
+            my $year_in = $year < 70 ? $year + 1900 : $year;
+            my $time = timelocal($sec,$min,$hour,$mday,$mon,$year_in);
+
+            my($s,$m,$h,$D,$M,$Y) = localtime($time);
+
+            is($s, $sec, "timelocal second for @$_");
+            is($m, $min, "timelocal minute for @$_");
+            is($h, $hour, "timelocal hour for @$_");
+            is($D, $mday, "timelocal day for @$_");
+            is($M, $mon, "timelocal month for @$_");
+            is($Y, $year, "timelocal year for @$_");
+        }
+
+
+        # Test timegm()
+        {
+            my $year_in = $year < 70 ? $year + 1900 : $year;
+            my $time = timegm($sec,$min,$hour,$mday,$mon,$year_in);
+
+            my($s,$m,$h,$D,$M,$Y) = gmtime($time);
+
+            is($s, $sec, "timegm second for @$_");
+            is($m, $min, "timegm minute for @$_");
+            is($h, $hour, "timegm hour for @$_");
+            is($D, $mday, "timegm day for @$_");
+            is($M, $mon, "timegm month for @$_");
+            is($Y, $year, "timegm year for @$_");
+        }
     }
 }
 
@@ -157,7 +169,11 @@ for my $p (@years) {
         "$year $string a leap year" );
 }
 
+SKIP:
 {
+    skip 'this platform does not support negative epochs.', 6
+        unless $neg_epoch_ok;
+
     eval { timegm(0,0,0,29,1,1900) };
     like($@, qr/Day '29' out of range 1\.\.28/,
          'does not accept leap day in 1900');
index 42c4c42..3a846a9 100644 (file)
@@ -112,7 +112,7 @@ C<threads::shared> has been upgraded from version 1.35 to 1.36
 
 =item *
 
-XXX
+C<Time::Local> has been upgraded from version 1.1901_01 to 1.2000.
 
 =back