From aee0fa3b68461fdc3208db8987752660810bddf3 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 2 Jan 2014 20:50:42 +0100 Subject: [PATCH] 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. --- configure.ac | 2 +- src/common.h | 5 ++++ src/drv_imap.c | 4 ++-- src/util.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 5f7c331..3a9927d 100644 --- a/configure.ac +++ b/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"]) diff --git a/src/common.h b/src/common.h index d1ec8b7..fb868b3 100644 --- a/src/common.h +++ b/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_t timegm( struct tm *tm ); +#endif + void *nfmalloc( size_t sz ); void *nfcalloc( size_t sz ); void *nfrealloc( void *mem, size_t sz ); diff --git a/src/drv_imap.c b/src/drv_imap.c index 2f1f2b1..5a28392 100644 --- a/src/drv_imap.c +++ b/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 diff --git a/src/util.c b/src/util.c index 1acbfc9..1ba65e5 100644 --- a/src/util.c +++ b/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 , with the help of + Mark Baushke 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 ) {