Commit f5ba72ea authored by Tom Lane's avatar Tom Lane

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.
parent 0cec2bb0
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -869,37 +869,75 @@ DecodeDateTime(char **field, int *ftype, int nf,
if (fmask & DTK_M(DTZMOD)) if (fmask & DTK_M(DTZMOD))
return -1; 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) #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
tm->tm_year -= 1900; /*
tm->tm_mon -= 1; * Some buggy mktime() implementations may change the year/month/day
tm->tm_isdst = -1; * when given a time right at a DST boundary. To prevent corruption
mktime(tm); * of the caller's data, give mktime() a copy...
tm->tm_year += 1900; */
tm->tm_mon += 1; 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) #if defined(HAVE_TM_ZONE)
*tzp = -(tm->tm_gmtoff); /* tm_gmtoff is /* tm_gmtoff is Sun/DEC-ism */
* 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) #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 */ #endif /* HAVE_INT_TIMEZONE */
#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
*tzp = CTimeZone; tm->tm_isdst = 0;
tz = CTimeZone;
#endif #endif
} }
else else
{ {
tm->tm_isdst = 0; /* Given date is out of range, so assume GMT */
*tzp = 0; tm->tm_isdst = 0;
} tz = 0;
}
} }
return 0; return tz;
} /* DecodeDateTime() */ }
/* DecodeTimeOnly() /* DecodeTimeOnly()
...@@ -1119,22 +1157,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1119,22 +1157,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
tmp->tm_min = tm->tm_min; tmp->tm_min = tm->tm_min;
tmp->tm_sec = tm->tm_sec; tmp->tm_sec = tm->tm_sec;
#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) *tzp = DetermineLocalTimeZone(tmp);
tmp->tm_year -= 1900;
tmp->tm_mon -= 1;
tmp->tm_isdst = -1;
mktime(tmp);
tm->tm_isdst = tmp->tm_isdst; 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; return 0;
......
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
* formatting.c * 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 * Portions Copyright (c) 1999-2000, PostgreSQL Global Development Group
...@@ -2936,37 +2936,7 @@ to_timestamp(PG_FUNCTION_ARGS) ...@@ -2936,37 +2936,7 @@ to_timestamp(PG_FUNCTION_ARGS)
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
NOTICE_TM; NOTICE_TM;
#endif #endif
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) tz = DetermineLocalTimeZone(tm);
{
#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;
}
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
NOTICE_TM; NOTICE_TM;
#endif #endif
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -990,32 +990,7 @@ timestamp_pl_span(PG_FUNCTION_ARGS)
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) 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]); 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)) tz = DetermineLocalTimeZone(tm);
{
#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;
}
if (tm2timestamp(tm, fsec, &tz, &dt) != 0) if (tm2timestamp(tm, fsec, &tz, &dt) != 0)
elog(ERROR, "Unable to add timestamp and interval"); elog(ERROR, "Unable to add timestamp and interval");
...@@ -1631,31 +1606,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) ...@@ -1631,31 +1606,7 @@ timestamp_trunc(PG_FUNCTION_ARGS)
result = 0; result = 0;
} }
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) tz = DetermineLocalTimeZone(tm);
{
#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;
}
if (tm2timestamp(tm, fsec, &tz, &result) != 0) if (tm2timestamp(tm, fsec, &tz, &result) != 0)
elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -255,6 +255,8 @@ extern int DecodeDateDelta(char **field, int *ftype,
int nf, int *dtype, int nf, int *dtype,
struct tm * tm, double *fsec); struct tm * tm, double *fsec);
extern int DetermineLocalTimeZone(struct tm * tm);
extern int EncodeDateOnly(struct tm * tm, int style, char *str); 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 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); extern int EncodeDateTime(struct tm * tm, double fsec, int *tzp, char **tzn, int style, char *str);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment