diff src/util.c @ 279:49b452b667a6 noffle

[svn] * src/util.c: localTimeDiff() cached its value and recalculated it every hour of clock time, regardless of the time the calculated was based on. This is potentially dangerous at daylight saving changes. So instead use the cached last result only when the new request is to be based on a time in the same hour as the cached result. * src/util.c: Replace the alternate Utl_mktimeGMT() implementation used when timegm() is not available. The previous version, as suggested by the glibc timegm() man page, used setenv() and unsetenv() for changing the environment. These aren't POSIX function, and the POSIX putenv() (a) is tricky to manage if the same var is being constantly update and memory isn't to leak, and (b) provides no way to remove an environment entry. So change to an implementation Wget uses. This should compile on not glibc systems - the previous version failed to build on Solaris.
author bears
date Sun, 17 Nov 2002 15:18:19 +0000
parents 3477050e8d10
children 5eece4dfd945
line wrap: on
line diff
--- a/src/util.c	Sun Nov 10 18:49:41 2002 +0000
+++ b/src/util.c	Sun Nov 17 15:18:19 2002 +0000
@@ -1,7 +1,7 @@
 /*
   util.c
 
-  $Id: util.c 403 2002-11-10 11:32:17Z bears $
+  $Id: util.c 411 2002-11-17 15:18:19Z bears $
 */
 
 #if HAVE_CONFIG_H
@@ -300,18 +300,22 @@
  * is < 24hrs. Sounds reasonable to me. It also assumes it can ignore seconds.
  * Returns localtime - GMT seconds. It will also trash the localtime/
  * gmtime/etc. static buffer.
+ *
+ * Do some basic caching by returning the previous result if the new request
+ * is in the same hour as the previous one.
  */
 static int
 localTimeDiff( time_t t )
 {
-    time_t now;
     struct tm local, gmt, *tm;
-    static time_t nextCalc = 0;
+    static time_t resHourStart = 0;
     static int res = 0;
 
-    now = time( NULL );
-    if ( now < nextCalc )
+    if ( labs( t - resHourStart ) < 60 )
 	return res;
+
+    /* Calculating for new hour */
+    resHourStart = ( t / 60 ) * 60;
     
     tm = localtime( &t );
     if ( tm == NULL )
@@ -334,9 +338,6 @@
     res += local.tm_min - gmt.tm_min;
     res *= 60;
 
-    /* Need to recalc at start of next hour */
-    nextCalc = now + ( 60 - local.tm_sec ) + 60 * ( 59 - local.tm_min );
-    
     return res;
 }
 
@@ -345,24 +346,73 @@
 {
 #if HAVE_TIMEGM
     return timegm( t );
-#else    
-    /*
-     * Based on the portable implementation suggested in the glibc man page
-     * for timegm.
+#else
+    /* This comment and implmentation is taken from Wget's mktime_from_utc().
+     * We could 
+     *
+     * Converts struct tm to time_t, assuming the data in tm is UTC rather
+     * than local timezone.
+     *
+     * mktime is similar but assumes struct tm, also known as the
+     * "broken-down" form of time, is in local time zone.  mktime_from_utc
+     * uses mktime to make the conversion understanding that an offset
+     * will be introduced by the local time assumption.
+     *
+     * mktime_from_utc then measures the introduced offset by applying
+     * gmtime to the initial result and applying mktime to the resulting
+     * "broken-down" form.  The difference between the two mktime results
+     * is the measured offset which is then subtracted from the initial
+     * mktime result to yield a calendar time which is the value returned.
+     *
+     * tm_isdst in struct tm is set to 0 to force mktime to introduce a
+     * consistent offset (the non DST offset) since tm and tm+o might be
+     * on opposite sides of a DST change.
+     * 
+     * Some implementations of mktime return -1 for the nonexistent
+     * localtime hour at the beginning of DST.  In this event, use
+     * mktime(tm - 1hr) + 3600.
+     * 
+     * Schematically
+     * mktime(tm)   --> t+o
+     * gmtime(t+o)  --> tm+o
+     * mktime(tm+o) --> t+2o
+     * t+o - (t+2o - t+o) = t
+     *
+     * Note that glibc contains a function of the same purpose named
+     * `timegm' (reverse of gmtime).  But obviously, it is not universally
+     * available, and unfortunately it is not straightforwardly
+     * extractable for use here.  Perhaps configure should detect timegm
+     * and use it where available.
+     * 
+     * Contributed by Roger Beeman <beeman@cisco.com>, with the help of
+     * Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
+     * Further improved by Roger with assistance from Edward J. Sabol
+     * based on input by Jamie Zawinski.
      */
-    time_t ret;
-    char *tz;
+    time_t tl, tb;
+    struct tm *tg;
 
-    tz = getenv("TZ");
-    setenv("TZ", "", 1);
-    tzset();
-    ret = mktime(t);
-    if (tz)
-	setenv("TZ", tz, 1);
-    else
-	unsetenv("TZ");
-    tzset();
-    return ret;
+    tl = mktime (t);
+    if (tl == -1)
+    {
+	t->tm_hour--;
+	tl = mktime (t);
+	if (tl == -1)
+	    return -1; /* can't deal with output from strptime */
+	tl += 3600;
+    }
+    tg = gmtime (&tl);
+    tg->tm_isdst = 0;
+    tb = mktime (tg);
+    if (tb == -1)
+    {
+	tg->tm_hour--;
+	tb = mktime (tg);
+	if (tb == -1)
+	    return -1; /* can't deal with output from gmtime */
+	tb += 3600;
+    }
+    return (tl - (tb - tl));    
 #endif    
 }