Update from the latest y2038.
Michael G Schwern [Sat, 13 Sep 2008 11:26:47 +0000 (04:26 -0700)]
Include the ability to use the system functions.  This speeds up localtime()
several times when the time is < 32 bits.

Don't bother using the system gmtime() as it will probably just have bugs.
We need to wait for config to give us LOCALTIME_MIN/MAX but I think assuming
time_t is at least 32 bits is good enough for now.

This restores the full performance of localtime() for < 32 bit numbers
and gmtime() is now only 10% slower.

localtime64.c
localtime64.h

index 80c0707..4e579a4 100644 (file)
@@ -94,6 +94,10 @@ static const int dow_year_start[28] = {
 #define IS_LEAP(n)     ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
 #define WRAP(a,b,m)    ((a) = ((a) <  0  ) ? ((b)--, (a) + (m)) : (a))
 
+#define SHOULD_USE_SYSTEM_LOCALTIME(a)  ( USE_SYSTEM_LOCALTIME && (a) <= SYSTEM_LOCALTIME_MAX )
+#define SHOULD_USE_SYSTEM_GMTIME(a)     ( USE_SYSTEM_GMTIME &&    (a) <= SYSTEM_GMTIME_MAX )
+
+
 int _is_exception_century(Int64 year)
 {
     int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
@@ -144,6 +148,11 @@ time_t _my_timegm(struct tm *date) {
 }
 
 
+#ifdef NDEBUG
+#define     CHECK_TM(a)
+#else
+#define     CHECK_TM(a)         _check_tm(a);
+
 void _check_tm(struct tm *tm)
 {
     int is_leap = IS_LEAP(tm->tm_year);
@@ -175,6 +184,8 @@ void _check_tm(struct tm *tm)
     assert(tm->tm_gmtoff <=  24 * 60 * 60);
 #endif
 }
+#endif
+
 
 /* The exceptional centuries without leap years cause the cycle to
    shift by 16
@@ -229,6 +240,14 @@ struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
     Time64_T time = *in_time;
     Int64 year = 70;
 
+    /* Use the system gmtime() if time_t is small enough */
+    if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
+        time_t safe_time = *in_time;
+        localtime_r(&safe_time, p);
+        CHECK_TM(p)
+        return p;
+    }
+
 #ifdef HAS_TM_TM_GMTOFF
     p->tm_gmtoff = 0;
 #endif
@@ -317,7 +336,7 @@ struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
     p->tm_sec = v_tm_sec, p->tm_min = v_tm_min, p->tm_hour = v_tm_hour,
         p->tm_mon = v_tm_mon, p->tm_wday = v_tm_wday;
 
-    _check_tm(p);
+    CHECK_TM(p)
 
     return p;
 }
@@ -330,6 +349,14 @@ struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
     Int64 orig_year;
     int month_diff;
 
+    /* Use the system localtime() if time_t is small enough */
+    if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
+        safe_time = *time;
+        localtime_r(&safe_time, local_tm);
+        CHECK_TM(local_tm)
+        return local_tm;
+    }
+
     gmtime64_r(time, &gm_tm);
     orig_year = gm_tm.tm_year;
 
@@ -365,7 +392,7 @@ struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
     if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
         local_tm->tm_yday--;
 
-    _check_tm(local_tm);
+    CHECK_TM(local_tm)
 
     return local_tm;
 }
index b6c65fc..d676c28 100644 (file)
 
    HAS_TM_TM_ZONE
    Defined if your tm struct has a "tm_zone" element.
+
+   SYSTEM_LOCALTIME_MAX
+   SYSTEM_LOCALTIME_MIN
+   SYSTEM_GMTIME_MAX
+   SYSTEM_GMTIME_MIN
+   Maximum and minimum values your system's gmtime() and localtime()
+   can handle.
+
+   USE_SYSTEM_LOCALTIME
+   USE_SYSTEM_GMTIME
+   Should we use the system functions if the time is inside their range?
 */
+#define SYSTEM_LOCALTIME_MAX    2147483647   /* XXX Replace with LOCALTIME_MAX */
+#define SYSTEM_LOCALTIME_MIN    -2147483648  /* XXX Replace with LOCALTIME_MIN */
+#define SYSTEM_GMTIME_MAX       GMTIME_MAX
+#define SYSTEM_GMTIME_MIN       GMTIME_MIN
+
+/* It'll be faster */
+#define USE_SYSTEM_LOCALTIME    1
+
+/* No point risking system bugs, ours works fine */
+#define USE_SYSTEM_GMTIME       0
 
 
 /* 64 bit types.  Set as appropriate for your system. */