8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.134 2004/08/30 02:54:39 momjian Exp $
11
+ * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.135 2004/11/01 21:34:38 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -1576,24 +1576,25 @@ DecodeDateTime(char **field, int *ftype, int nf,
1576
1576
* tm_isdst field accordingly, and return the actual timezone offset.
1577
1577
*
1578
1578
* Note: it might seem that we should use mktime() for this, but bitter
1579
- * experience teaches otherwise. In particular, mktime() is generally
1580
- * incapable of coping reasonably with "impossible" times within a
1581
- * spring-forward DST transition. Typical implementations of mktime()
1582
- * turn out to be loops around localtime() anyway, so they're not even
1583
- * any faster than this code.
1579
+ * experience teaches otherwise. This code is much faster than most versions
1580
+ * of mktime(), anyway.
1584
1581
*/
1585
1582
int
1586
1583
DetermineLocalTimeZone (struct pg_tm * tm )
1587
1584
{
1588
- int tz ;
1589
- int date ,
1585
+ int date ,
1590
1586
sec ;
1591
1587
pg_time_t day ,
1592
- mysec ,
1593
- locsec ,
1594
- delta1 ,
1595
- delta2 ;
1596
- struct pg_tm * tx ;
1588
+ mytime ,
1589
+ prevtime ,
1590
+ boundary ,
1591
+ beforetime ,
1592
+ aftertime ;
1593
+ long int before_gmtoff ,
1594
+ after_gmtoff ;
1595
+ int before_isdst ,
1596
+ after_isdst ;
1597
+ int res ;
1597
1598
1598
1599
if (HasCTZSet )
1599
1600
{
@@ -1615,82 +1616,71 @@ DetermineLocalTimeZone(struct pg_tm * tm)
1615
1616
if (day / 86400 != date )
1616
1617
goto overflow ;
1617
1618
sec = tm -> tm_sec + (tm -> tm_min + tm -> tm_hour * 60 ) * 60 ;
1618
- mysec = day + sec ;
1619
- /* since sec >= 0, overflow could only be from +day to -mysec */
1620
- if (mysec < 0 && day > 0 )
1619
+ mytime = day + sec ;
1620
+ /* since sec >= 0, overflow could only be from +day to -mytime */
1621
+ if (mytime < 0 && day > 0 )
1621
1622
goto overflow ;
1622
1623
1623
1624
/*
1624
- * Use pg_localtime to convert that pg_time_t to broken-down time, and
1625
- * reassemble to get a representation of local time. (We could get
1626
- * overflow of a few hours in the result, but the delta calculation
1627
- * should still work.)
1625
+ * Find the DST time boundary just before or following the target time.
1626
+ * We assume that all zones have GMT offsets less than 24 hours, and
1627
+ * that DST boundaries can't be closer together than 48 hours, so
1628
+ * backing up 24 hours and finding the "next" boundary will work.
1628
1629
*/
1629
- tx = pg_localtime (& mysec );
1630
- if (!tx )
1631
- goto overflow ; /* probably can't happen */
1632
- day = date2j (tx -> tm_year + 1900 , tx -> tm_mon + 1 , tx -> tm_mday ) -
1633
- UNIX_EPOCH_JDATE ;
1634
- locsec = tx -> tm_sec + (tx -> tm_min + (day * 24 + tx -> tm_hour ) * 60 ) * 60 ;
1630
+ prevtime = mytime - (24 * 60 * 60 );
1631
+ if (mytime < 0 && prevtime > 0 )
1632
+ goto overflow ;
1635
1633
1636
- /*
1637
- * The local time offset corresponding to that GMT time is now
1638
- * computable as mysec - locsec.
1639
- */
1640
- delta1 = mysec - locsec ;
1634
+ res = pg_next_dst_boundary (& prevtime ,
1635
+ & before_gmtoff , & before_isdst ,
1636
+ & boundary ,
1637
+ & after_gmtoff , & after_isdst );
1638
+ if (res < 0 )
1639
+ goto overflow ; /* failure? */
1640
+
1641
+ if (res == 0 )
1642
+ {
1643
+ /* Non-DST zone, life is simple */
1644
+ tm -> tm_isdst = before_isdst ;
1645
+ return - (int ) before_gmtoff ;
1646
+ }
1641
1647
1642
1648
/*
1643
- * However, if that GMT time and the local time we are actually
1644
- * interested in are on opposite sides of a daylight-savings-time
1645
- * transition, then this is not the time offset we want. So, adjust
1646
- * the pg_time_t to be what we think the GMT time corresponding to our
1647
- * target local time is, and repeat the pg_localtime() call and delta
1648
- * calculation.
1649
- *
1650
- * We have to watch out for overflow while adjusting the pg_time_t.
1649
+ * Form the candidate pg_time_t values with local-time adjustment
1651
1650
*/
1652
- if ((delta1 < 0 ) ? (mysec < 0 && (mysec + delta1 ) > 0 ) :
1653
- (mysec > 0 && (mysec + delta1 ) < 0 ))
1651
+ beforetime = mytime - before_gmtoff ;
1652
+ if ((before_gmtoff > 0 ) ? (mytime < 0 && beforetime > 0 ) :
1653
+ (mytime > 0 && beforetime < 0 ))
1654
+ goto overflow ;
1655
+ aftertime = mytime - after_gmtoff ;
1656
+ if ((after_gmtoff > 0 ) ? (mytime < 0 && aftertime > 0 ) :
1657
+ (mytime > 0 && aftertime < 0 ))
1654
1658
goto overflow ;
1655
- mysec += delta1 ;
1656
- tx = pg_localtime (& mysec );
1657
- if (!tx )
1658
- goto overflow ; /* probably can't happen */
1659
- day = date2j (tx -> tm_year + 1900 , tx -> tm_mon + 1 , tx -> tm_mday ) -
1660
- UNIX_EPOCH_JDATE ;
1661
- locsec = tx -> tm_sec + (tx -> tm_min + (day * 24 + tx -> tm_hour ) * 60 ) * 60 ;
1662
- delta2 = mysec - locsec ;
1663
1659
1664
1660
/*
1665
- * We may have to do it again to get the correct delta.
1666
- *
1667
- * It might seem we should just loop until we get the same delta twice in
1668
- * a row, but if we've been given an "impossible" local time (in the
1669
- * gap during a spring-forward transition) we'd never get out of the
1670
- * loop. The behavior we want is that "impossible" times are taken as
1671
- * standard time, and also that ambiguous times (during a fall-back
1672
- * transition) are taken as standard time. Therefore, we bias the code
1673
- * to prefer the standard-time solution.
1661
+ * If both before or both after the boundary time, we know what to do
1674
1662
*/
1675
- if (delta2 != delta1 && tx -> tm_isdst != 0 )
1663
+ if (beforetime <= boundary && aftertime < boundary )
1676
1664
{
1677
- delta2 -= delta1 ;
1678
- if ((delta2 < 0 ) ? (mysec < 0 && (mysec + delta2 ) > 0 ) :
1679
- (mysec > 0 && (mysec + delta2 ) < 0 ))
1680
- goto overflow ;
1681
- mysec += delta2 ;
1682
- tx = pg_localtime (& mysec );
1683
- if (!tx )
1684
- goto overflow ; /* probably can't happen */
1685
- day = date2j (tx -> tm_year + 1900 , tx -> tm_mon + 1 , tx -> tm_mday ) -
1686
- UNIX_EPOCH_JDATE ;
1687
- locsec = tx -> tm_sec + (tx -> tm_min + (day * 24 + tx -> tm_hour ) * 60 ) * 60 ;
1688
- delta2 = mysec - locsec ;
1665
+ tm -> tm_isdst = before_isdst ;
1666
+ return - (int ) before_gmtoff ;
1689
1667
}
1690
- tm -> tm_isdst = tx -> tm_isdst ;
1691
- tz = (int ) delta2 ;
1692
-
1693
- return tz ;
1668
+ if (beforetime > boundary && aftertime >= boundary )
1669
+ {
1670
+ tm -> tm_isdst = after_isdst ;
1671
+ return - (int ) after_gmtoff ;
1672
+ }
1673
+ /*
1674
+ * It's an invalid or ambiguous time due to timezone transition.
1675
+ * Prefer the standard-time interpretation.
1676
+ */
1677
+ if (after_isdst == 0 )
1678
+ {
1679
+ tm -> tm_isdst = after_isdst ;
1680
+ return - (int ) after_gmtoff ;
1681
+ }
1682
+ tm -> tm_isdst = before_isdst ;
1683
+ return - (int ) before_gmtoff ;
1694
1684
1695
1685
overflow :
1696
1686
/* Given date is out of range, so assume UTC */
0 commit comments