# $Id: Head.U 6 2006-08-25 22:21:46Z rmanfredi $
#
-# Generated on Fri Oct 3 17:54:11 CEST 2008 [metaconfig 3.5 PL0]
+# Generated on Sat Oct 18 17:13:43 CEST 2008 [metaconfig 3.5 PL0]
# (with additional metaconfig patches by perlbug@perl.org)
cat >c1$$ <<EOF
struct tm *tmp;
time_t pt;
-void gm_check (time_t t)
+void gm_check (time_t t, int min_year, int max_year)
{
tmp = gmtime (&t);
- if (tmp == NULL || tmp->tm_year < -1900)
+ if ( tmp == NULL ||
+ /* Check tm_year overflow */
+ tmp->tm_year < min_year || tmp->tm_year > max_year)
tmp = NULL;
else
pt = t;
tmp = NULL;
pt = 0;
#ifdef MAXLONG
- gm_check (MAXLONG);
+ gm_check (MAXLONG, 69, 0x7fffffff);
#endif
if (tmp == NULL || tmp->tm_year < 0) {
for (i = 63; i >= 0; i--) {
time_t x = pt | ((time_t)1 << i);
- if (x < 0) continue;
- gm_check (x);
+ if (x < 0 || x < pt) continue;
+ gm_check (x, 69, 0x7fffffff);
}
}
printf ("sGMTIME_max=%ld\n", pt);
return (0);
- } /* check_max */
+ } /* check_max */
int check_min ()
{
tmp = NULL;
pt = 0;
#ifdef MINLONG
- gm_check (MINLONG);
+ gm_check (MINLONG, -1900, 70);
#endif
if (tmp == NULL) {
for (i = 36; i >= 0; i--) {
time_t x = pt - ((time_t)1 << i);
if (x > 0) continue;
- gm_check (x);
+ gm_check (x, -1900, 70);
}
}
printf ("sGMTIME_min=%ld\n", pt);
struct tm *tmp;
time_t pt;
-void local_check (time_t t)
+void local_check (time_t t, int min_year, int max_year)
{
if (sizeof (time_t) > 4 && t > 0x7ffffffffffff000LL)
tmp = NULL;
else
tmp = localtime (&t);
- if (tmp == NULL || tmp->tm_year < -1900)
+ if ( tmp == NULL ||
+ /* Check tm_year overflow */
+ tmp->tm_year < min_year || tmp->tm_year > max_year)
tmp = NULL;
else
pt = t;
tmp = NULL;
pt = 0;
#ifdef MAXLONG
- local_check (MAXLONG);
+ local_check (MAXLONG, 69, 0x7fffffff);
#endif
if (tmp == NULL || tmp->tm_year < 0) {
for (i = 63; i >= 0; i--) {
time_t x = pt | ((time_t)1 << i);
- if (x < 0) continue;
- local_check (x);
+ if (x < 0 || x < pt) continue;
+ local_check (x, 69, 0x7fffffff);
}
}
printf ("sLOCALTIME_max=%ld\n", pt);
return (0);
- } /* check_max */
+ } /* check_max */
int check_min ()
{
tmp = NULL;
pt = 0;
#ifdef MINLONG
- local_check (MINLONG);
+ local_check (MINLONG, -1900, 70);
#endif
if (tmp == NULL) {
for (i = 36; i >= 0; i--) {
time_t x = pt - ((time_t)1 << i);
if (x > 0) continue;
- local_check (x);
+ local_check (x, -1900, 70);
}
}
printf ("sLOCALTIME_min=%ld\n", pt);
--- /dev/null
+The y2038 implementation for perl
+===========================================================================
+This is an implementation of POSIX time.h which solves the year 2038 bug on
+systems where time_t is only 32 bits. It is implemented in bog-standard
+ANSI C. The latest version can be found at http://y2038.googlecode.com/
+
+It makes use of the system's native 32 bit functions to perform time zone
+and daylight savings time calculations and thus does *not* need to ship its
+own time zone table.
+
+time64.h currently implements three public functions, localtime64_r(),
+gmtime64_r() and timegm64(). They are implementations of localtime_r(),
+gmtime_r() and timegm64().
+
+To install, simply copy time64.c and time64.h into your project and make
+use of the functions.
+
+To test, run "make test". You must have Perl, prove (which comes with a
+recent version of the Test::Harness Perl module) and bzdiff installed to
+run the full test suite. It will do a number of unit tests, plus test
+against a large table of known good values in different time zones.
+
+Limitations, Issues, etc...
+---------------------------
+localtime64_r() gets its time zone and daylight savings time information by
+mappping the future year back to a similar one between 2010 and 2037, safe
+for localtime_r(). The calculations are accurate according to current time
+zone and daylight savings information, but may become inaccurate if a
+change is made that takes place after 2010.
+
+Future versions will probe for a 64 bit safe system localtime_r() and
+gmtime_r() and use that.
+
+The maximum date is still limited by your tm struct. Most 32 bit systems
+use a signed integer tm_year which means the practical upper limit is the
+year 2147483647 which is somewhere around 2**54. You can use a 64 bit
+clean tm struct by setting USE_TM64 in time64.h
+
+Portability
+-----------
+I would like to add some configuration detection stuff in the future, but
+for now all I can do is document the assumptions...
+
+This code assumes that long longs are 64 bit integers which is technically
+in violation of the C standard. This can be changed in time64.h by
+changing the Time64_T and Int64 typedefs.
+
+There are a number of configuration options in time64.h.
+
+Configure variables
+-------------------
+Configure probes for the maximum and minimum values that gmtime () and
+localtime () accept on the local system. Configure however is only used on
+unix-like systems. For windows and VMS these values are hard-coded. You can
+use timecheck.c in the Porting directory to check those values yourself,
+using the same technique that is used in Configure based on bit-shifting:
+
+ $ cd perl/Porting
+ $ cc -O -o timecheck timecheck.c
+ $ ./timecheck
+ ======================
+ Sizeof time_t = 8
+ gmtime () boundaries:
+ 8: 0x00f0c2ab7c54a97f: 2147485547-12-31 23:59:59
+ 8: -0x0000000e79747c00: 0-01-01 00:00:00
+ localtime () boundaries:
+ 8: 0x00f0c2ab7c549b6f: 2147485547-12-31 23:59:59
+ 8: -0x0000000e79748094: 0-01-01 00:00:00
+ Configure variables:
+ sGMTIME_max='67768036191676799'
+ sGMTIME_min='-62167219200'
+ sLOCALTIME_max='67768036191673199'
+ sLOCALTIME_min='-62167220372'
+
+In the rare case that your system uses a double for time_t, you can use the
+alternate approach to test for these values:
+
+ $ cd perl/Porting
+ $ cc -O -o timecheck2{,.c}
+ $ ./timecheck2
+ gmtime max 67768036191676800
+ localtime max 67768036191673200
+ gmtime min -67768040609740800
+ localtime min -67768040609741968
--- /dev/null
+#include <sys/types.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <values.h>
+
+int opt_v = 0;
+int i;
+struct tm *tmp;
+time_t pt, pt_max, pt_min;
+
+static char hexbuf[80];
+char *hex (time_t t)
+{
+ if ((long long)t < 0)
+ sprintf (hexbuf, " -0x%016lx", -t);
+ else
+ sprintf (hexbuf, " 0x%016lx", t);
+ return (hexbuf);
+ } /* hex */
+
+void gm_check (time_t t, int min_year, int max_year)
+{
+ tmp = gmtime (&t);
+ if ( tmp == NULL ||
+ /* Check tm_year overflow */
+ tmp->tm_year < min_year || tmp->tm_year > max_year) {
+ if (opt_v)
+ fprintf (stderr, "gmtime (%ld) failed with errno %d\n", t, errno);
+ }
+ else {
+ if (opt_v)
+ fprintf (stderr, "%3d:%s: %12ld-%02d-%02d %02d:%02d:%02d\n",
+ i, hex (t),
+ tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ pt = t;
+ }
+ } /* gm_check */
+
+int check_gm_max ()
+{
+ tmp = NULL;
+ pt = 0;
+ if (tmp == NULL || tmp->tm_year < 0) {
+ for (i = 63; i >= 0; i--) {
+ time_t x = pt | ((time_t)1 << i);
+ if (x < 0 || x < pt) continue;
+ gm_check (x, 69, 0x7fffffff);
+ }
+ }
+ pt_max = pt;
+ return (0);
+ } /* check_gm_max */
+
+int check_gm_min ()
+{
+ tmp = NULL;
+ pt = 0;
+ if (tmp == NULL) {
+ for (i = 36; i >= 0; i--) {
+ time_t x = pt - ((time_t)1 << i);
+ if (x > 0) continue;
+ gm_check (x, -1900, 70);
+ }
+ }
+ pt_min = pt;
+ return (0);
+ } /* check_gm_min */
+
+void lt_check (time_t t, int min_year, int max_year)
+{
+ if (sizeof (time_t) > 4 && t > 0x7ffffffffffff000LL)
+ tmp = NULL;
+ else
+ tmp = localtime (&t);
+ if ( tmp == NULL ||
+ /* Check tm_year overflow */
+ tmp->tm_year < min_year || tmp->tm_year > max_year) {
+ if (opt_v)
+ fprintf (stderr, "localtime (%ld) failed with errno %d\n", t, errno);
+ }
+ else {
+ if (opt_v)
+ fprintf (stderr, "%3d:%s: %12ld-%02d-%02d %02d:%02d:%02d\n",
+ i, hex (t),
+ tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ pt = t;
+ }
+ } /* lt_check */
+
+int check_lt_max ()
+{
+ tmp = NULL;
+ pt = 0;
+ if (tmp == NULL || tmp->tm_year < 0) {
+ for (i = 63; i >= 0; i--) {
+ time_t x = pt | ((time_t)1 << i);
+ if (x < 0 || x < pt) continue;
+ lt_check (x, 69, 0x7fffffff);
+ }
+ }
+ pt_max = pt;
+ return (0);
+ } /* check_lt_max */
+
+int check_lt_min ()
+{
+ tmp = NULL;
+ pt = 0;
+ if (tmp == NULL) {
+ for (i = 36; i >= 0; i--) {
+ time_t x = pt - ((time_t)1 << i);
+ if (x > 0) continue;
+ lt_check (x, -1900, 70);
+ }
+ }
+ pt_min = pt;
+ return (0);
+ } /* check_lt_min */
+
+int main (int argc, char *argv[])
+{
+ time_t gm_max, gm_min, lt_max, lt_min;
+ if (argc > 1 && strcmp (argv[1], "-v") == 0) opt_v++;
+
+ check_gm_max (); gm_max = pt_max;
+ check_gm_min (); gm_min = pt_min;
+ check_lt_max (); lt_max = pt_max;
+ check_lt_min (); lt_min = pt_min;
+
+ opt_v++;
+ printf ("======================\n");
+ printf ("Sizeof time_t = %ld\n", (i = sizeof (time_t)));
+ printf ("gmtime () boundaries:\n");
+ gm_check (gm_max, 69, 0x7fffffff);
+ gm_check (gm_min, -1900, 70);
+ printf ("localtime () boundaries:\n");
+ lt_check (lt_max, 69, 0x7fffffff);
+ lt_check (lt_min, -1900, 70);
+ printf ("Configure variables:\n");
+ printf ("sGMTIME_max='%ld'\n", gm_max);
+ printf ("sGMTIME_min='%ld'\n", gm_min);
+ printf ("sLOCALTIME_max='%ld'\n", lt_max);
+ printf ("sLOCALTIME_min='%ld'\n", lt_min);
+ return (0);
+ } /* main */
--- /dev/null
+/* A little program to test the limits of your system's time functions */
+
+#include <time.h>
+#include <stdio.h>
+#include <math.h>
+
+time_t Time_Zero = 0;
+
+/* Visual C++ 2008's difftime() can't do negative times */
+double my_difftime(time_t left, time_t right) {
+ double diff = (double)left - (double)right;
+ return diff;
+}
+
+void check_date_max( struct tm * (*date_func)(const time_t *), char *func_name ) {
+ struct tm *date;
+ time_t time = 0;
+ time_t last_time = 0;
+ time_t time_change;
+ int i;
+
+ for (i = 0; i <= 63; i++) {
+ date = (*date_func)(&time);
+
+ /* date_func() broke or tm_year overflowed */
+ if(date == NULL || date->tm_year < 69)
+ break;
+
+ last_time = time;
+ time += time + 1;
+
+ /* time_t overflowed */
+ if( time < last_time )
+ break;
+ }
+
+ /* Binary search for the exact failure point */
+ time = last_time;
+ time_change = last_time / 2;
+
+ do {
+ time += time_change;
+
+ date = (*date_func)(&time);
+
+ /* date_func() broke or tm_year overflowed or time_t overflowed */
+ if(date == NULL || date->tm_year < 69 || time < last_time) {
+ time = last_time;
+ time_change = time_change / 2;
+ }
+ else {
+ last_time = time;
+ }
+ } while(time_change > 0);
+
+ printf("%20s max %.0f\n", func_name, my_difftime(last_time, Time_Zero));
+}
+
+
+void check_date_min( struct tm * (*date_func)(const time_t *), char *func_name ) {
+ struct tm *date;
+ time_t time = -1;
+ time_t last_time = 0;
+ time_t time_change;
+ int i;
+
+ for (i = 1; i <= 63; i++) {
+ date = (*date_func)(&time);
+
+ /* date_func() broke or tm_year underflowed */
+ if(date == NULL || date->tm_year > 70)
+ break;
+
+ last_time = time;
+ time += time;
+
+ /* time_t underflowed */
+ if( time > last_time )
+ break;
+ }
+
+ /* Binary search for the exact failure point */
+ time = last_time;
+ time_change = last_time / 2;
+
+ do {
+ time += time_change;
+
+ date = (*date_func)(&time);
+
+ /* gmtime() broke or tm_year overflowed or time_t overflowed */
+ if(date == NULL || date->tm_year > 70 || time > last_time) {
+ time = last_time;
+ time_change = time_change / 2;
+ }
+ else {
+ last_time = time;
+ }
+ } while(time_change < 0);
+
+ printf("%20s min %.0f\n", func_name, my_difftime(last_time, Time_Zero));
+}
+
+
+int main(void) {
+ check_date_max(gmtime, "gmtime");
+ check_date_max(localtime, "localtime");
+ check_date_min(gmtime, "gmtime");
+ check_date_min(localtime, "localtime");
+
+ return 0;
+}