Make time64 use NV for time_t, I32 for year, not Quad_t.
Craig A. Berry [Sat, 25 Apr 2009 22:51:38 +0000 (17:51 -0500)]
This means it should run on anything that does not have a 64-bit
integer type available but does have a double.  Presumably this
includes platforms that define PERL_MICRO, so we now use the
same extended time implementation for everything that runs Perl.

pp_sys.c
t/op/time.t
time64.c
time64.h
time64_config.h

index 0179323..bf362f0 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
 #include "EXTERN.h"
 #define PERL_IN_PP_SYS_C
 #include "perl.h"
-#if !defined(PERL_MICRO) && defined(Quad_t)
-#  include "time64.h"
-#  include "time64.c"
-#endif
+#include "time64.h"
+#include "time64.c"
 
 #ifdef I_SHADOW
 /* Shadow password support for solaris - pdo@cs.umd.edu
@@ -4469,15 +4467,9 @@ PP(pp_gmtime)
 {
     dVAR;
     dSP;
-#if defined(PERL_MICRO) || !defined(Quad_t)
-    Time_t when;
-    const struct tm *err;
-    struct tm tmbuf;
-#else
     Time64_T when;
     struct TM tmbuf;
     struct TM *err;
-#endif
     const char *opname = PL_op->op_type == OP_LOCALTIME ? "localtime" : "gmtime";
     static const char * const dayname[] =
        {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
@@ -4485,30 +4477,12 @@ PP(pp_gmtime)
        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 
-#if defined(PERL_MICRO) || !defined(Quad_t)
-    if (MAXARG < 1)
-       (void)time(&when);
-    else
-       when = (Time_t)SvIVx(POPs);
-
-    if (PL_op->op_type == OP_LOCALTIME)
-       err = localtime(&when);
-    else
-       err = gmtime(&when);
-
-    if (!err)
-       tmbuf = *err;
-#else
     if (MAXARG < 1) {
        time_t now;
        (void)time(&now);
        when = (Time64_T)now;
     }
     else {
-       /* XXX POPq uses an SvIV so it won't work with 32 bit integer scalars
-          using a double causes an unfortunate loss of accuracy on high numbers.
-          What we really need is an SvQV.
-       */
        double input = Perl_floor(POPn);
        when = (Time64_T)input;
        if (when != input && ckWARN(WARN_OVERFLOW)) {
@@ -4521,7 +4495,6 @@ PP(pp_gmtime)
         err = S_localtime64_r(&when, &tmbuf);
     else
        err = S_gmtime64_r(&when, &tmbuf);
-#endif
 
     if (err == NULL && ckWARN(WARN_OVERFLOW)) {
        /* XXX %lld broken for quads */
index 89ea04b..2ea1733 100644 (file)
@@ -81,8 +81,9 @@ 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.
+    # The range should be limited only by the 53-bit mantissa of an IEEE double (or 
+    # whatever kind of double you've got).  Here we just prove that we're comfortably 
+    # beyond the range possible with 32-bit time_t.
     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"],
index 764ac72..ca31acf 100755 (executable)
--- a/time64.c
+++ b/time64.c
@@ -371,19 +371,19 @@ static struct TM *S_gmtime64_r (const Time64_T *in_time, struct TM *p)
     p->tm_zone   = "UTC";
 #endif
 
-    v_tm_sec =  (int)(time % 60);
-    time /= 60;
-    v_tm_min =  (int)(time % 60);
-    time /= 60;
-    v_tm_hour = (int)(time % 24);
-    time /= 24;
-    v_tm_tday = time;
+    v_tm_sec  = (int)fmod(time, 60.0);
+    time      = time >= 0 ? floor(time / 60.0) : ceil(time / 60.0);
+    v_tm_min  = (int)fmod(time, 60.0);
+    time      = time >= 0 ? floor(time / 60.0) : ceil(time / 60.0);
+    v_tm_hour = (int)fmod(time, 24.0);
+    time      = time >= 0 ? floor(time / 24.0) : ceil(time / 24.0);
+    v_tm_tday = (int)time;
 
     WRAP (v_tm_sec, v_tm_min, 60);
     WRAP (v_tm_min, v_tm_hour, 60);
     WRAP (v_tm_hour, v_tm_tday, 24);
 
-    v_tm_wday = (int)((v_tm_tday + 4) % 7);
+    v_tm_wday = (int)fmod((v_tm_tday + 4.0), 7.0);
     if (v_tm_wday < 0)
         v_tm_wday += 7;
     m = v_tm_tday;
@@ -395,7 +395,7 @@ static struct TM *S_gmtime64_r (const Time64_T *in_time, struct TM *p)
 
     if (m >= 0) {
         /* Gregorian cycles, this is huge optimization for distant times */
-        cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
+        cycles = (int)floor(m / (Time64_T) days_in_gregorian_cycle);
         if( cycles ) {
             m -= (cycles * (Time64_T) days_in_gregorian_cycle);
             year += (cycles * years_in_gregorian_cycle);
@@ -419,7 +419,7 @@ static struct TM *S_gmtime64_r (const Time64_T *in_time, struct TM *p)
         year--;
 
         /* Gregorian cycles */
-        cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
+        cycles = (int)ceil((m / (Time64_T) days_in_gregorian_cycle) + 1);
         if( cycles ) {
             m -= (cycles * (Time64_T) days_in_gregorian_cycle);
             year += (cycles * years_in_gregorian_cycle);
index 000f101..07bb33f 100644 (file)
--- a/time64.h
+++ b/time64.h
@@ -8,7 +8,7 @@
 /* Set our custom types */
 typedef INT_64_T        Int64;
 typedef Int64           Time64_T;
-typedef Int64           Year;
+typedef I32             Year;
 
 
 /* A copy of the tm struct but with a 64 bit year */
index 6a1cd9d..42cc12c 100644 (file)
 
 
 /* INT_64_T
-   A 64 bit integer type to use to store time and others.
+   A numeric type to store time and others. 
    Must be defined.
 */
-#define INT_64_T                Quad_t
+#define INT_64_T                NV
 
 
 /* USE_TM64
@@ -77,9 +77,9 @@
    can handle.  We will use your system functions if the time falls
    inside these ranges.
 */
-#define SYSTEM_LOCALTIME_MAX    CAT2(LOCALTIME_MAX,UL)
-#define SYSTEM_LOCALTIME_MIN    CAT2(LOCALTIME_MIN,UL)
-#define SYSTEM_GMTIME_MAX       CAT2(GMTIME_MAX,UL)
-#define SYSTEM_GMTIME_MIN       CAT2(GMTIME_MIN,UL)
+#define SYSTEM_LOCALTIME_MAX    CAT2(LOCALTIME_MAX,.0)
+#define SYSTEM_LOCALTIME_MIN    CAT2(LOCALTIME_MIN,.0)
+#define SYSTEM_GMTIME_MAX       CAT2(GMTIME_MAX,.0)
+#define SYSTEM_GMTIME_MIN       CAT2(GMTIME_MIN,.0)
 
 #endif /* TIME64_CONFIG_H */