Browse Source

make date parsing portable, take 2

the global timezone variable is glibc-specific.
so use timegm() instead of mktime() for the conversion.
as that is specific to the BSDs and glibc, provide a fallback.
amends 62a6099.
Oswald Buddenhagen 11 years ago
parent
commit
aee0fa3b68
4 changed files with 73 additions and 3 deletions
  1. 1 1
      configure.ac
  2. 5 0
      src/common.h
  3. 2 2
      src/drv_imap.c
  4. 65 0
      src/util.c

+ 1 - 1
configure.ac

@@ -29,7 +29,7 @@ if test "x$ob_cv_strftime_z" = x"no"; then
 fi
 
 AC_CHECK_HEADERS(sys/poll.h sys/select.h)
-AC_CHECK_FUNCS(vasprintf memrchr)
+AC_CHECK_FUNCS(vasprintf memrchr timegm)
 
 AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
 AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])

+ 5 - 0
src/common.h

@@ -94,6 +94,11 @@ void free_string_list( string_list_t *list );
 void *memrchr( const void *s, int c, size_t n );
 #endif
 
+#ifndef HAVE_TIMEGM
+# include <time.h>
+time_t timegm( struct tm *tm );
+#endif
+
 void *nfmalloc( size_t sz );
 void *nfcalloc( size_t sz );
 void *nfrealloc( void *mem, size_t sz );

+ 2 - 2
src/drv_imap.c

@@ -835,11 +835,11 @@ parse_date( const char *str )
 	memset( &datetime, 0, sizeof(datetime) );
 	if (!(end = strptime( str, "%d-%b-%Y %H:%M:%S ", &datetime )))
 		return -1;
-	if ((date = mktime( &datetime )) == -1)
+	if ((date = timegm( &datetime )) == -1)
 		return -1;
 	if (sscanf( end, "%3d%2d", &hours, &mins ) != 2)
 		return -1;
-	return date - (hours * 60 + mins) * 60 - timezone;
+	return date - (hours * 60 + mins) * 60;
 }
 
 static int

+ 65 - 0
src/util.c

@@ -203,6 +203,71 @@ memrchr( const void *s, int c, size_t n )
 }
 #endif
 
+#ifndef HAVE_TIMEGM
+/*
+   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.  timegm
+   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
+
+   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.
+*/
+
+static time_t
+my_mktime( struct tm *t )
+{
+	time_t tl = mktime( t );
+	if (tl == -1) {
+		t->tm_hour--;
+		tl = mktime( t );
+		if (tl != -1)
+			tl += 3600;
+	}
+	return tl;
+}
+
+time_t
+timegm( struct tm *t )
+{
+	time_t tl, tb;
+	struct tm *tg;
+
+	if ((tl = my_mktime( t )) == -1)
+		return tl;
+	tg = gmtime( &tl );
+	tg->tm_isdst = 0;
+	if ((tb = my_mktime( tg )) == -1)
+		return tb;
+	return tl - (tb - tl);
+}
+#endif
+
 void
 oob( void )
 {