- Introducing Jamie Lokier's function for date to epoch conversion used in the
authorDaniel Stenberg <daniel@haxx.se>
Tue, 23 Sep 2008 11:00:01 +0000 (11:00 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 23 Sep 2008 11:00:01 +0000 (11:00 +0000)
  date parser function. This makes our function less dependent on system-
  provided functions and instead we do all the magic ourselves. We also no
  longer depend on the TZ environment variable.

CHANGES
RELEASE-NOTES
lib/parsedate.c
tests/data/test517

diff --git a/CHANGES b/CHANGES
index 6bba10a..9ea9f3b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,25 @@
                                   Changelog
 
 Daniel Stenberg (23 Sep 2008)
+- Introducing Jamie Lokier's function for date to epoch conversion used in the
+  date parser function. This makes our function less dependent on system-
+  provided functions and instead we do all the magic ourselves. We also no
+  longer depend on the TZ environment variable. Switching to our own converter
+  has some side-effect and they are noted here for future reference (taken
+  from a mail by mr Lokier):
+
+  time_t is not measured in seconds in the ANSI C standard - or even counted
+  uniformly - weird platforms can use other numeric representations of dates
+  in time_t - hence the difftime() function.
+
+  On POSIX time_t is measured in UTC seconds, which means not including leap
+  seconds.  But it's mentioned in a few places that some old POSIX-ish
+  environments include leap seconds in their time_t counts...
+
+  I'm pretty sure [the new implementation is] correct on anything truly POSIX.
+  And it's obviously a lot less dependent on platform quirks and corner cases
+  in many ways than the mktime() version.
+
 - Rob Crittenden brought a patch to "add some locking for thread-safety to NSS
   implementation".
 
index c9cc9a3..a90bd2e 100644 (file)
@@ -26,6 +26,7 @@ This release includes the following bugfixes:
  o recv() failures cause CURLE_RECV_ERROR
  o SFTP over SOCKS crash fixed
  o thread-safety issues addressed for NSS-powered libcurls
+ o removed the use of mktime() and gmtime(_r)() in date parsing and conversions
 
 This release includes the following known bugs:
 
@@ -40,6 +41,7 @@ advice from friends like these:
 
  Keith Mok, Yang Tse, Daniel Fandrich, Guenter Knauf, Dmitriy Sergeyev,
  Linus Nielsen Feltzing, Martin Drasar, Stefan Krause, Dmitry Kurochkin,
- Mike Revi, Andres Garcia, Michael Goffioul, Markus Moeller, Rob Crittenden
+ Mike Revi, Andres Garcia, Michael Goffioul, Markus Moeller, Rob Crittenden,
+ Jamie Lokier
 
         Thanks! (and sorry if I forgot to mention someone)
index 64bb2cc..d1d1fdc 100644 (file)
@@ -222,6 +222,38 @@ enum assume {
   DATE_TIME
 };
 
+/* struct tm to time since epoch in GMT time zone */
+static time_t my_timegm(struct tm * tm)
+{
+  int month_days_cumulative [12] =
+    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+  int month, year, leap_days;
+
+  if(tm->tm_year < 70)
+    /* we don't support years before 1970 as they will cause this function
+       to return a negative value */
+    return -1;
+
+  year = tm->tm_year + 1900;
+  month = tm->tm_mon;
+  if (month < 0) {
+    year += (11 - month) / 12;
+    month = 11 - (11 - month) % 12;
+  }
+  else if (month >= 12) {
+    year -= month / 12;
+    month = month % 12;
+  }
+
+  leap_days = year - (tm->tm_mon <= 1);
+  leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
+               - (1969 / 4) + (1969 / 100) - (1969 / 400));
+
+  return ((((time_t) (year - 1970) * 365
+            + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
+           + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
+}
+
 static time_t parsedate(const char *date)
 {
   time_t t = 0;
@@ -247,7 +279,8 @@ static time_t parsedate(const char *date)
       /* a name coming up */
       char buf[32]="";
       size_t len;
-      sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]", buf);
+      sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]",
+             buf);
       len = strlen(buf);
 
       if(wdaynum == -1) {
@@ -374,45 +407,20 @@ static time_t parsedate(const char *date)
   tm.tm_yday = 0;
   tm.tm_isdst = 0;
 
-  /* mktime() returns a time_t. time_t is often 32 bits, even on many
+  /* my_timegm() returns a time_t. time_t is often 32 bits, even on many
      architectures that feature 64 bit 'long'.
 
      Some systems have 64 bit time_t and deal with years beyond 2038. However,
-     even some of the systems with 64 bit time_t returns -1 for dates beyond
-     03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
+     even on some of the systems with 64 bit time_t mktime() returns -1 for
+     dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
   */
-  t = mktime(&tm);
+  t = my_timegm(&tm);
 
   /* time zone adjust (cast t to int to compare to negative one) */
   if(-1 != (int)t) {
-    struct tm *gmt;
-    long delta;
-    time_t t2;
-
-#ifdef HAVE_GMTIME_R
-    /* thread-safe version */
-    struct tm keeptime2;
-    gmt = (struct tm *)gmtime_r(&t, &keeptime2);
-    if(!gmt)
-      return -1; /* illegal date/time */
-    t2 = mktime(gmt);
-#else
-    /* It seems that at least the MSVC version of mktime() doesn't work
-       properly if it gets the 'gmt' pointer passed in (which is a pointer
-       returned from gmtime() pointing to static memory), so instead we copy
-       the tm struct to a local struct and pass a pointer to that struct as
-       input to mktime(). */
-    struct tm gmt2;
-    gmt = gmtime(&t); /* use gmtime_r() if available */
-    if(!gmt)
-      return -1; /* illegal date/time */
-    gmt2 = *gmt;
-    t2 = mktime(&gmt2);
-#endif
 
-    /* Add the time zone diff (between the given timezone and GMT) and the
-       diff between the local time zone and GMT. */
-    delta = (long)((tzoff!=-1?tzoff:0) + (t - t2));
+    /* Add the time zone diff between local time zone and GMT. */
+    long delta = (long)(tzoff!=-1?tzoff:0);
 
     if((delta>0) && (t + delta < t))
       return -1; /* time_t overflow */
index cd5ecd1..b7d2af6 100644 (file)
@@ -21,7 +21,6 @@ curl_getdate() testing
 # using one of the 'right' zones that take into account leap seconds
 # which causes the cookie expiry times to be different.
 <setenv>
-TZ=GMT
 </setenv>
  <command>
 nothing