88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.116 2003/08/27 23:29:28 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.117 2003/09/13 21:12:38 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -1570,9 +1570,10 @@ DecodeDateTime(char **field, int *ftype, int nf,
15701570 * (ie, regular or daylight-savings time) at that time. Set the struct tm's
15711571 * tm_isdst field accordingly, and return the actual timezone offset.
15721572 *
1573- * This subroutine exists to centralize uses of mktime() and defend against
1574- * mktime() bugs/restrictions on various platforms. This should be
1575- * the *only* call of mktime() in the backend.
1573+ * Note: this subroutine exists because mktime() has such a spectacular
1574+ * variety of, ahem, odd behaviors on various platforms. We used to try to
1575+ * use mktime() here, but finally gave it up as a bad job. Avoid using
1576+ * mktime() anywhere else.
15761577 */
15771578int
15781579DetermineLocalTimeZone (struct tm * tm )
@@ -1586,117 +1587,78 @@ DetermineLocalTimeZone(struct tm * tm)
15861587}
15871588else if (IS_VALID_UTIME (tm -> tm_year ,tm -> tm_mon ,tm -> tm_mday ))
15881589{
1589- #if defined(HAVE_TM_ZONE )|| defined(HAVE_INT_TIMEZONE )
1590+ /*
1591+ * First, generate the time_t value corresponding to the given
1592+ * y/m/d/h/m/s taken as GMT time. This will not overflow (at
1593+ * least not for time_t taken as signed) because of the range
1594+ * check we did above.
1595+ */
1596+ long day ,
1597+ mysec ,
1598+ locsec ,
1599+ delta1 ,
1600+ delta2 ;
1601+ time_t mytime ;
1602+ struct tm * tx ;
1603+
1604+ day = date2j (tm -> tm_year ,tm -> tm_mon ,tm -> tm_mday )- UNIX_EPOCH_JDATE ;
1605+ mysec = tm -> tm_sec + (tm -> tm_min + (day * 24 + tm -> tm_hour )* 60 )* 60 ;
1606+ mytime = (time_t )mysec ;
15901607
15911608/*
1592- * Some buggy mktime() implementations may change the
1593- * year/month/day when given a time right at a DST boundary. To
1594- * prevent corruption of the caller's data, give mktime() a
1595- * copy...
1609+ * Use localtime to convert that time_t to broken-down time,
1610+ * and reassemble to get a representation of local time.
15961611 */
1597- struct tm tt ,
1598- * tmp = & tt ;
1612+ tx = localtime (& mytime );
1613+ day = date2j (tx -> tm_year + 1900 ,tx -> tm_mon + 1 ,tx -> tm_mday )-
1614+ UNIX_EPOCH_JDATE ;
1615+ locsec = tx -> tm_sec + (tx -> tm_min + (day * 24 + tx -> tm_hour )* 60 )* 60 ;
15991616
1600- * tmp = * tm ;
1601- /* change to Unix conventions for year/month */
1602- tmp -> tm_year -= 1900 ;
1603- tmp -> tm_mon -= 1 ;
1617+ /*
1618+ * The local time offset corresponding to that GMT time is now
1619+ * computable as mysec - locsec.
1620+ */
1621+ delta1 = mysec - locsec ;
16041622
1605- /* indicate timezone unknown */
1606- tmp -> tm_isdst = -1 ;
1623+ /*
1624+ * However, if that GMT time and the local time we are
1625+ * actually interested in are on opposite sides of a
1626+ * daylight-savings-time transition, then this is not the time
1627+ * offset we want.So, adjust the time_t to be what we think
1628+ * the GMT time corresponding to our target local time is, and
1629+ * repeat the localtime() call and delta calculation.
1630+ */
1631+ mysec += delta1 ;
1632+ mytime = (time_t )mysec ;
1633+ tx = localtime (& mytime );
1634+ day = date2j (tx -> tm_year + 1900 ,tx -> tm_mon + 1 ,tx -> tm_mday )-
1635+ UNIX_EPOCH_JDATE ;
1636+ locsec = tx -> tm_sec + (tx -> tm_min + (day * 24 + tx -> tm_hour )* 60 )* 60 ;
1637+ delta2 = mysec - locsec ;
16071638
1608- if (mktime (tmp )!= ((time_t )- 1 )&&
1609- tmp -> tm_isdst >=0 )
1610- {
1611- /* mktime() succeeded, trust its result */
1612- tm -> tm_isdst = tmp -> tm_isdst ;
1613-
1614- #if defined(HAVE_TM_ZONE )
1615- /* tm_gmtoff is Sun/DEC-ism */
1616- tz = - (tmp -> tm_gmtoff );
1617- #elif defined(HAVE_INT_TIMEZONE )
1618- tz = ((tmp -> tm_isdst > 0 ) ? (TIMEZONE_GLOBAL - 3600 ) :TIMEZONE_GLOBAL );
1619- #endif /* HAVE_INT_TIMEZONE */
1620- }
1621- else
1639+ /*
1640+ * We may have to do it again to get the correct delta.
1641+ *
1642+ * It might seem we should just loop until we get the same delta
1643+ * twice in a row, but if we've been given an "impossible" local
1644+ * time (in the gap during a spring-forward transition) we'd never
1645+ * get out of the loop. The behavior we want is that "impossible"
1646+ * times are taken as standard time, and also that ambiguous times
1647+ * (during a fall-back transition) are taken as standard time.
1648+ * Therefore, we bias the code to prefer the standard-time solution.
1649+ */
1650+ if (delta2 != delta1 && tx -> tm_isdst != 0 )
16221651{
1623- /*
1624- * We have a buggy (not to say deliberately brain damaged)
1625- * mktime(). Work around it by using localtime() instead.
1626- *
1627- * First, generate the time_t value corresponding to the given
1628- * y/m/d/h/m/s taken as GMT time. This will not overflow (at
1629- * least not for time_t taken as signed) because of the range
1630- * check we did above.
1631- */
1632- long day ,
1633- mysec ,
1634- locsec ,
1635- delta1 ,
1636- delta2 ;
1637- time_t mytime ;
1638-
1639- day = date2j (tm -> tm_year ,tm -> tm_mon ,tm -> tm_mday )- UNIX_EPOCH_JDATE ;
1640- mysec = tm -> tm_sec + (tm -> tm_min + (day * 24 + tm -> tm_hour )* 60 )* 60 ;
1652+ mysec += (delta2 - delta1 );
16411653mytime = (time_t )mysec ;
1642-
1643- /*
1644- * Use localtime to convert that time_t to broken-down time,
1645- * and reassemble to get a representation of local time.
1646- */
1647- tmp = localtime (& mytime );
1648- day = date2j (tmp -> tm_year + 1900 ,tmp -> tm_mon + 1 ,tmp -> tm_mday )-
1649- UNIX_EPOCH_JDATE ;
1650- locsec = tmp -> tm_sec + (tmp -> tm_min + (day * 24 + tmp -> tm_hour )* 60 )* 60 ;
1651-
1652- /*
1653- * The local time offset corresponding to that GMT time is now
1654- * computable as mysec - locsec.
1655- */
1656- delta1 = mysec - locsec ;
1657-
1658- /*
1659- * However, if that GMT time and the local time we are
1660- * actually interested in are on opposite sides of a
1661- * daylight-savings-time transition, then this is not the time
1662- * offset we want.So, adjust the time_t to be what we think
1663- * the GMT time corresponding to our target local time is, and
1664- * repeat the localtime() call and delta calculation. We may
1665- * have to do it twice before we have a trustworthy delta.
1666- *
1667- * Note: think not to put a loop here, since if we've been given
1668- * an "impossible" local time (in the gap during a
1669- * spring-forward transition) we'd never get out of the loop.
1670- * Twice is enough to give the behavior we want, which is that
1671- * "impossible" times are taken as standard time, while at a
1672- * fall-back boundary ambiguous times are also taken as
1673- * standard.
1674- */
1675- mysec += delta1 ;
1676- mytime = (time_t )mysec ;
1677- tmp = localtime (& mytime );
1678- day = date2j (tmp -> tm_year + 1900 ,tmp -> tm_mon + 1 ,tmp -> tm_mday )-
1654+ tx = localtime (& mytime );
1655+ day = date2j (tx -> tm_year + 1900 ,tx -> tm_mon + 1 ,tx -> tm_mday )-
16791656UNIX_EPOCH_JDATE ;
1680- locsec = tmp -> tm_sec + (tmp -> tm_min + (day * 24 + tmp -> tm_hour )* 60 )* 60 ;
1657+ locsec = tx -> tm_sec + (tx -> tm_min + (day * 24 + tx -> tm_hour )* 60 )* 60 ;
16811658delta2 = mysec - locsec ;
1682- if (delta2 != delta1 )
1683- {
1684- mysec += (delta2 - delta1 );
1685- mytime = (time_t )mysec ;
1686- tmp = localtime (& mytime );
1687- day = date2j (tmp -> tm_year + 1900 ,tmp -> tm_mon + 1 ,tmp -> tm_mday )-
1688- UNIX_EPOCH_JDATE ;
1689- locsec = tmp -> tm_sec + (tmp -> tm_min + (day * 24 + tmp -> tm_hour )* 60 )* 60 ;
1690- delta2 = mysec - locsec ;
1691- }
1692- tm -> tm_isdst = tmp -> tm_isdst ;
1693- tz = (int )delta2 ;
16941659}
1695- #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1696- /* Assume UTC if no system timezone info available */
1697- tm -> tm_isdst = 0 ;
1698- tz = 0 ;
1699- #endif
1660+ tm -> tm_isdst = tx -> tm_isdst ;
1661+ tz = (int )delta2 ;
17001662}
17011663else
17021664{