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()
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;
#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
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");
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);
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);