From: Tom Lane Date: Thu, 3 May 2001 22:53:07 +0000 (+0000) Subject: Consolidate several near-identical uses of mktime() into a single X-Git-Tag: REL7_1_1~8 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f5ba72ea04e71a127031a4436781943008abfa7a;p=postgresql Consolidate several near-identical uses of mktime() into a single routine DetermineLocalTimeZone(). In that routine, be more wary of broken mktime() implementations than the original code was: don't allow mktime to change the already-set y/m/d/h/m/s information, and don't use tm_gmtoff if mktime failed. Possibly this will resolve some of the complaints we've been hearing from users of Middle Eastern timezones on RedHat. --- diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 60346efdcd..1a13aa4a4a 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.63 2001/04/03 18:05:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.64 2001/05/03 22:53:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -869,37 +869,75 @@ DecodeDateTime(char **field, int *ftype, int nf, if (fmask & DTK_M(DTZMOD)) return -1; - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { + *tzp = DetermineLocalTimeZone(tm); + } + } + + return 0; +} /* DecodeDateTime() */ + + +/* DetermineLocalTimeZone() + * Given a struct tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and + * tm_sec fields are set, attempt to determine the applicable local zone + * (ie, regular or daylight-savings time) at that time. Set the struct tm's + * tm_isdst field accordingly, and return the actual timezone offset. + * + * This subroutine exists mainly to centralize uses of mktime() and defend + * against mktime() bugs on various platforms... + */ +int +DetermineLocalTimeZone(struct tm * tm) +{ + int tz; + + if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) + { #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) - tm->tm_year -= 1900; - tm->tm_mon -= 1; - tm->tm_isdst = -1; - mktime(tm); - tm->tm_year += 1900; - tm->tm_mon += 1; + /* + * Some buggy mktime() implementations may change the year/month/day + * when given a time right at a DST boundary. To prevent corruption + * of the caller's data, give mktime() a copy... + */ + struct tm tt, + *tmp = &tt; + + *tmp = *tm; + /* change to Unix conventions for year/month */ + tmp->tm_year -= 1900; + tmp->tm_mon -= 1; + + /* indicate timezone unknown */ + tmp->tm_isdst = -1; + + mktime(tmp); + + tm->tm_isdst = tmp->tm_isdst; #if defined(HAVE_TM_ZONE) - *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is - * Sun/DEC-ism */ + /* tm_gmtoff is Sun/DEC-ism */ + if (tmp->tm_isdst >= 0) + tz = -(tmp->tm_gmtoff); + else + tz = 0; /* assume GMT if mktime failed */ #elif defined(HAVE_INT_TIMEZONE) - *tzp = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); + tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); #endif /* HAVE_INT_TIMEZONE */ #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ - *tzp = CTimeZone; + tm->tm_isdst = 0; + tz = CTimeZone; #endif - } - else - { - tm->tm_isdst = 0; - *tzp = 0; - } - } + } + else + { + /* Given date is out of range, so assume GMT */ + tm->tm_isdst = 0; + tz = 0; } - return 0; -} /* DecodeDateTime() */ + return tz; +} /* DecodeTimeOnly() @@ -1119,22 +1157,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf, tmp->tm_min = tm->tm_min; tmp->tm_sec = tm->tm_sec; -#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) - tmp->tm_year -= 1900; - tmp->tm_mon -= 1; - tmp->tm_isdst = -1; - mktime(tmp); + *tzp = DetermineLocalTimeZone(tmp); tm->tm_isdst = tmp->tm_isdst; - -#if defined(HAVE_TM_ZONE) - *tzp = -(tmp->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) - *tzp = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); -#endif - -#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ - *tzp = CTimeZone; -#endif } return 0; diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 7e68d7e984..1ad99d330e 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.36 2001/03/23 04:49:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.37 2001/05/03 22:53:07 tgl Exp $ * * * Portions Copyright (c) 1999-2000, PostgreSQL Global Development Group @@ -2936,37 +2936,7 @@ to_timestamp(PG_FUNCTION_ARGS) #ifdef DEBUG_TO_FROM_CHAR NOTICE_TM; #endif - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { - -#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) - tm->tm_isdst = -1; - tm->tm_year -= 1900; - tm->tm_mon -= 1; - -#ifdef DEBUG_TO_FROM_CHAR - elog(DEBUG_elog_output, "TO-FROM_CHAR: Call mktime()"); - NOTICE_TM; -#endif - mktime(tm); - tm->tm_year += 1900; - tm->tm_mon += 1; - -#if defined(HAVE_TM_ZONE) - tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) - tz = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); -#endif - -#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ - tz = CTimeZone; -#endif - } - else - { - tm->tm_isdst = 0; - tz = 0; - } + tz = DetermineLocalTimeZone(tm); #ifdef DEBUG_TO_FROM_CHAR NOTICE_TM; #endif diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 186103252b..68099de867 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.48 2001/05/03 19:00:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.49 2001/05/03 22:53:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -990,32 +990,7 @@ timestamp_pl_span(PG_FUNCTION_ARGS) if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { -#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) - tm->tm_year -= 1900; - tm->tm_mon -= 1; - tm->tm_isdst = -1; - mktime(tm); - tm->tm_year += 1900; - tm->tm_mon += 1; - -#if defined(HAVE_TM_ZONE) - tz = -(tm->tm_gmtoff); /* tm_gmtoff is - * Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) - tz = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); -#endif - -#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ - tz = CTimeZone; -#endif - } - else - { - tm->tm_isdst = 0; - tz = 0; - } + tz = DetermineLocalTimeZone(tm); if (tm2timestamp(tm, fsec, &tz, &dt) != 0) elog(ERROR, "Unable to add timestamp and interval"); @@ -1631,31 +1606,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) result = 0; } - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { -#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) - tm->tm_year -= 1900; - tm->tm_mon -= 1; - tm->tm_isdst = -1; - mktime(tm); - tm->tm_year += 1900; - tm->tm_mon += 1; - -#if defined(HAVE_TM_ZONE) - tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */ -#elif defined(HAVE_INT_TIMEZONE) - tz = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); -#endif - -#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ - tz = CTimeZone; -#endif - } - else - { - tm->tm_isdst = 0; - tz = 0; - } + tz = DetermineLocalTimeZone(tm); if (tm2timestamp(tm, fsec, &tz, &result) != 0) elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index af9b7f6ac7..5922d635dc 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: datetime.h,v 1.17 2001/01/24 19:43:28 momjian Exp $ + * $Id: datetime.h,v 1.18 2001/05/03 22:53:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -255,6 +255,8 @@ extern int DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec); +extern int DetermineLocalTimeZone(struct tm * tm); + extern int EncodeDateOnly(struct tm * tm, int style, char *str); extern int EncodeTimeOnly(struct tm * tm, double fsec, int *tzp, int style, char *str); extern int EncodeDateTime(struct tm * tm, double fsec, int *tzp, char **tzn, int style, char *str);