From d369a2b7755d763490575fc18de748def0b3ba77 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 23 Sep 2008 11:00:01 +0000 Subject: [PATCH] - 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. --- CHANGES | 19 ++++++++++++ RELEASE-NOTES | 4 ++- lib/parsedate.c | 72 +++++++++++++++++++++++++--------------------- tests/data/test517 | 1 - 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index 6bba10a61..9ea9f3b64 100644 --- 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". diff --git a/RELEASE-NOTES b/RELEASE-NOTES index c9cc9a324..a90bd2e32 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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) diff --git a/lib/parsedate.c b/lib/parsedate.c index 64bb2cc41..d1d1fdc82 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -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 */ diff --git a/tests/data/test517 b/tests/data/test517 index cd5ecd14c..b7d2af607 100644 --- a/tests/data/test517 +++ b/tests/data/test517 @@ -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. -TZ=GMT nothing