diff options
-rw-r--r-- | lib/Time/gmtime.t | 3 | ||||
-rw-r--r-- | lib/Time/localtime.t | 5 | ||||
-rw-r--r-- | pod/perlport.pod | 5 | ||||
-rwxr-xr-x | t/op/time.t | 29 | ||||
-rw-r--r-- | time64.c | 41 | ||||
-rw-r--r-- | time64_config.h | 7 |
6 files changed, 58 insertions, 32 deletions
diff --git a/lib/Time/gmtime.t b/lib/Time/gmtime.t index 1ccd7fb826..9c77f81570 100644 --- a/lib/Time/gmtime.t +++ b/lib/Time/gmtime.t @@ -9,7 +9,7 @@ BEGIN { my(@times, @methods); BEGIN { - @times = (-2**33, -2**31-1, 0, 2**31-1, 2**33, time); + @times = (-2**62, -2**50, -2**33, -2**31-1, -1, 0, 1, 2**31-1, 2**33, 2**50, 2**62, time); @methods = qw(sec min hour mday mon year wday yday isdst); plan tests => (@times * @methods) + 1; @@ -17,7 +17,6 @@ BEGIN { use_ok Time::gmtime; } -# Perl has its own gmtime() so it's safe to do negative times. for my $time (@times) { my $gmtime = gmtime $time; # This is the OO gmtime. my @gmtime = CORE::gmtime $time; # This is the gmtime function diff --git a/lib/Time/localtime.t b/lib/Time/localtime.t index 8600eff3e7..f300343ff8 100644 --- a/lib/Time/localtime.t +++ b/lib/Time/localtime.t @@ -7,8 +7,9 @@ BEGIN { require "./test.pl"; } +my(@times, @methods); BEGIN { - @times = (-2**33, -2**31-1, 0, 2**31-1, 2**33, time); + @times = (-2**62, -2**50, -2**33, -2**31-1, -1, 0, 1, 2**31-1, 2**33, 2**50, 2**62, time); @methods = qw(sec min hour mday mon year wday yday isdst); plan tests => (@times * @methods) + 1; @@ -16,8 +17,6 @@ BEGIN { use_ok Time::localtime; } -# Since Perl's localtime() still uses the system localtime, don't try -# to do negative times. The system might not support it. for my $time (@times) { my $localtime = localtime $time; # This is the OO localtime. my @localtime = CORE::localtime $time; # This is the localtime function diff --git a/pod/perlport.pod b/pod/perlport.pod index 8a72de246f..ff1ba39675 100644 --- a/pod/perlport.pod +++ b/pod/perlport.pod @@ -1860,7 +1860,10 @@ platforms. See L<File::Glob> for portability information. =item gmtime -gmtime() has a range of about 2 billion years before and after 1970. +In theory, gmtime() is reliable from -2**63 to 2**63-1. However, +because work arounds in the implementation use floating point numbers, +it will become inaccurate as the time gets larger. This is a bug and +will be fixed in the future. =item ioctl FILEHANDLE,FUNCTION,SCALAR diff --git a/t/op/time.t b/t/op/time.t index 2e61eea77f..00c5b0501c 100755 --- a/t/op/time.t +++ b/t/op/time.t @@ -6,7 +6,7 @@ BEGIN { require './test.pl'; } -plan tests => 34; +plan tests => 42; ($beguser,$begsys) = times; @@ -82,16 +82,19 @@ ok(gmtime() =~ /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat)[ ] # Test gmtime over a range of times. { # gm/localtime should go all the way from -2**63 to 2**63-1 + # but floating point hacks mean it gets unreliable for large numbers. my %tests = ( # time_t gmtime list scalar - -2**35 => [52, 13, 20, 7, 2, -1019, 5, 65, 0, "Fri Mar 7 20:13:52 881"], - -2**32 => [44, 31, 17, 24, 10, -67, 0, 327, 0, "Sun Nov 24 17:31:44 1833"], - -2**31 => [52, 45, 20, 13, 11, 1, 5, 346, 0, "Fri Dec 13 20:45:52 1901"], - 0 => [0, 0, 0, 1, 0, 70, 4, 0, 0, "Thu Jan 1 00:00:00 1970"], - 2**30 => [4, 37, 13, 10, 0, 104, 6, 9, 0, "Sat Jan 10 13:37:04 2004"], - 2**31 => [8, 14, 3, 19, 0, 138, 2, 18, 0, "Tue Jan 19 03:14:08 2038"], - 2**32 => [16, 28, 6, 7, 1, 206, 0, 37, 0, "Sun Feb 7 06:28:16 2106"], - 2**39 => [8, 18, 12, 25, 0, 17491, 2, 24, 0, "Tue Jan 25 12:18:08 19391"], + -2**35 => [52, 13, 20, 7, 2, -1019, 5, 65, 0, "Fri Mar 7 20:13:52 881"], + -2**32 => [44, 31, 17, 24, 10, -67, 0, 327, 0, "Sun Nov 24 17:31:44 1833"], + -2**31 => [52, 45, 20, 13, 11, 1, 5, 346, 0, "Fri Dec 13 20:45:52 1901"], + -1 => [59, 59, 23, 31, 11, 69, 3, 364, 0, "Wed Dec 31 23:59:59 1969"], + 0 => [0, 0, 0, 1, 0, 70, 4, 0, 0, "Thu Jan 1 00:00:00 1970"], + 1 => [1, 0, 0, 1, 0, 70, 4, 0, 0, "Thu Jan 1 00:00:01 1970"], + 2**30 => [4, 37, 13, 10, 0, 104, 6, 9, 0, "Sat Jan 10 13:37:04 2004"], + 2**31 => [8, 14, 3, 19, 0, 138, 2, 18, 0, "Tue Jan 19 03:14:08 2038"], + 2**32 => [16, 28, 6, 7, 1, 206, 0, 37, 0, "Sun Feb 7 06:28:16 2106"], + 2**39 => [8, 18, 12, 25, 0, 17491, 2, 24, 0, "Tue Jan 25 12:18:08 19391"], ); for my $time (keys %tests) { @@ -110,9 +113,11 @@ ok(gmtime() =~ /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat)[ ] # the same regardless of the time zone. my %tests = ( # time_t month, year, scalar - -8589934592 => [9, -203, qr/Oct \d+ .* 1697$/], - 5000000000 => [5, 228, qr/Jun \d+ .* 2128$/], - 1163500000 => [10, 106, qr/Nov \d+ .* 2006$/], + -8589934592 => [9, -203, qr/Oct \d+ .* 1697$/], + -1296000 => [11, 69, qr/Dec \d+ .* 1969$/], + 1296000 => [0, 70, qr/Jan \d+ .* 1970$/], + 5000000000 => [5, 228, qr/Jun \d+ .* 2128$/], + 1163500000 => [10, 106, qr/Nov \d+ .* 2006$/], ); for my $time (keys %tests) { @@ -108,11 +108,18 @@ static const int dow_year_start[SOLAR_CYCLE_LENGTH] = { # define SHOULD_USE_SYSTEM_GMTIME(a) (0) #endif +#ifdef TIME_64_DEBUG +# define TRACE(format, ...) (fprintf(stderr, format, __VA_ARGS__)) +# define TRACE_NO_VARS(format) (fprintf(stderr, format)) +#else +# define TRACE(format, ...) ((void)0) +# define TRACE_NO_VARS(format) ((void)0) +#endif static int is_exception_century(Year year) { int is_exception = ((year % 100 == 0) && !(year % 400 == 0)); - /* printf("is_exception_century: %s\n", is_exception ? "yes" : "no"); */ + TRACE("# is_exception_century: %s\n", is_exception ? "yes" : "no"); return(is_exception); } @@ -201,10 +208,8 @@ static Year cycle_offset(Year year) exceptions = year_diff / 100; exceptions -= year_diff / 400; - /* - fprintf(stderr, "# year: %lld, exceptions: %lld, year_diff: %lld\n", - year, exceptions, year_diff); - */ + TRACE("# year: %lld, exceptions: %lld, year_diff: %lld\n", + year, exceptions, year_diff); return exceptions * 16; } @@ -249,10 +254,8 @@ static int safe_year(Year year) assert(safe_year <= 2037 && safe_year >= 2010); - /* - printf("year: %d, year_cycle: %d, safe_year: %d\n", - year, year_cycle, safe_year); - */ + TRACE("# year: %lld, year_cycle: %lld, safe_year: %d\n", + year, year_cycle, safe_year); return safe_year; } @@ -413,7 +416,7 @@ struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p) if (m >= 0) { /* Gregorian cycles, this is huge optimization for distant times */ - cycles = m / (Time64_T) days_in_gregorian_cycle; + cycles = (int)(m / (Time64_T) days_in_gregorian_cycle); if( cycles ) { m -= (cycles * (Time64_T) days_in_gregorian_cycle); year += (cycles * years_in_gregorian_cycle); @@ -437,7 +440,7 @@ struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p) year--; /* Gregorian cycles */ - cycles = (m / (Time64_T) days_in_gregorian_cycle) + 1; + cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1); if( cycles ) { m -= (cycles * (Time64_T) days_in_gregorian_cycle); year += (cycles * years_in_gregorian_cycle); @@ -497,6 +500,8 @@ struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm) if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) { safe_time = *time; + TRACE("Using system localtime for %lld\n", *time); + LOCALTIME_R(&safe_time, &safe_date); copy_tm_to_TM(&safe_date, local_tm); @@ -505,26 +510,34 @@ struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm) return local_tm; } - if( gmtime64_r(time, &gm_tm) == NULL ) + if( gmtime64_r(time, &gm_tm) == NULL ) { + TRACE("gmtime64_r returned null for %lld\n", *time); return NULL; + } orig_year = gm_tm.tm_year; if (gm_tm.tm_year > (2037 - 1900) || - gm_tm.tm_year < (1902 - 1900) + gm_tm.tm_year < (1970 - 1900) ) { + TRACE("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; } safe_time = timegm64(&gm_tm); - if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) + if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) { + TRACE("localtime_r(%d) returned NULL\n", (int)safe_time); return NULL; + } copy_tm_to_TM(&safe_date, local_tm); local_tm->tm_year = orig_year; if( local_tm->tm_year != orig_year ) { + TRACE("tm_year overflow: tm_year %lld, orig_year %lld\n", + (Year)local_tm->tm_year, (Year)orig_year); + #ifdef EOVERFLOW errno = EOVERFLOW; #endif diff --git a/time64_config.h b/time64_config.h index c22a1153e2..6b54534cbb 100644 --- a/time64_config.h +++ b/time64_config.h @@ -7,6 +7,13 @@ Sensible defaults provided. */ +/* Debugging + TIME_64_DEBUG + Define if you want debugging messages +*/ +/* #define TIME_64_DEBUG */ + + /* INT_64_T A 64 bit integer type to use to store time and others. Must be defined. |