Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5ba04cd

Browse files
committed
Invent pg_next_dst_boundary() and rewrite DetermineLocalTimeZone() to
use it, as per my proposal of yesterday. This gives us a means ofdetermining the zone offset to impute to an unlabeled timestamp thatis both efficient and reliable, unlike all our previous tries involvingmktime() and localtime(). The behavior for invalid or ambiguous timesat a DST transition is fixed to be really and truly "assume standardtime", fixing a bug that has come and gone repeatedly but was backagain in 7.4. (There is some ongoing discussion about whether we shouldraise an error instead, but for the moment I'll make it do what it waspreviously intended to do.)
1 parent7fad5ff commit5ba04cd

File tree

3 files changed

+171
-80
lines changed

3 files changed

+171
-80
lines changed

‎src/backend/utils/adt/datetime.c

Lines changed: 65 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* 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 $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1576,24 +1576,25 @@ DecodeDateTime(char **field, int *ftype, int nf,
15761576
* tm_isdst field accordingly, and return the actual timezone offset.
15771577
*
15781578
* 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.
15841581
*/
15851582
int
15861583
DetermineLocalTimeZone(structpg_tm*tm)
15871584
{
1588-
inttz;
1589-
intdate ,
1585+
intdate,
15901586
sec;
15911587
pg_time_tday,
1592-
mysec,
1593-
locsec,
1594-
delta1,
1595-
delta2;
1596-
structpg_tm*tx;
1588+
mytime,
1589+
prevtime,
1590+
boundary,
1591+
beforetime,
1592+
aftertime;
1593+
longintbefore_gmtoff,
1594+
after_gmtoff;
1595+
intbefore_isdst,
1596+
after_isdst;
1597+
intres;
15971598

15981599
if (HasCTZSet)
15991600
{
@@ -1615,82 +1616,71 @@ DetermineLocalTimeZone(struct pg_tm * tm)
16151616
if (day /86400!=date)
16161617
gotooverflow;
16171618
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)
16211622
gotooverflow;
16221623

16231624
/*
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 stillwork.)
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 willwork.
16281629
*/
1629-
tx=pg_localtime(&mysec);
1630-
if (!tx)
1631-
gotooverflow;/* 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+
gotooverflow;
16351633

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+
gotooverflow;/* 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+
}
16411647

16421648
/*
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
16511650
*/
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+
gotooverflow;
1655+
aftertime=mytime-after_gmtoff;
1656+
if ((after_gmtoff>0) ? (mytime<0&&aftertime>0) :
1657+
(mytime>0&&aftertime<0))
16541658
gotooverflow;
1655-
mysec+=delta1;
1656-
tx=pg_localtime(&mysec);
1657-
if (!tx)
1658-
gotooverflow;/* 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;
16631659

16641660
/*
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
16741662
*/
1675-
if (delta2!=delta1&&tx->tm_isdst!=0)
1663+
if (beforetime <=boundary&&aftertime<boundary)
16761664
{
1677-
delta2-=delta1;
1678-
if ((delta2<0) ? (mysec<0&& (mysec+delta2)>0) :
1679-
(mysec>0&& (mysec+delta2)<0))
1680-
gotooverflow;
1681-
mysec+=delta2;
1682-
tx=pg_localtime(&mysec);
1683-
if (!tx)
1684-
gotooverflow;/* 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;
16891667
}
1690-
tm->tm_isdst=tx->tm_isdst;
1691-
tz= (int)delta2;
1692-
1693-
returntz;
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;
16941684

16951685
overflow:
16961686
/* Given date is out of range, so assume UTC */

‎src/include/pgtime.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
77
*
88
* IDENTIFICATION
9-
* $PostgreSQL: pgsql/src/include/pgtime.h,v 1.4 2004/08/29 05:06:55 momjian Exp $
9+
* $PostgreSQL: pgsql/src/include/pgtime.h,v 1.5 2004/11/01 21:34:41 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -37,12 +37,19 @@ struct pg_tm
3737
constchar*tm_zone;
3838
};
3939

40-
externstructpg_tm*pg_localtime(constpg_time_t*);
41-
externstructpg_tm*pg_gmtime(constpg_time_t*);
42-
externboolpg_tzset(constchar*tzname);
40+
externstructpg_tm*pg_localtime(constpg_time_t*timep);
41+
externstructpg_tm*pg_gmtime(constpg_time_t*timep);
42+
externintpg_next_dst_boundary(constpg_time_t*timep,
43+
longint*before_gmtoff,
44+
int*before_isdst,
45+
pg_time_t*boundary,
46+
longint*after_gmtoff,
47+
int*after_isdst);
4348
externsize_tpg_strftime(char*s,size_tmax,constchar*format,
4449
conststructpg_tm*tm);
50+
4551
externvoidpg_timezone_initialize(void);
52+
externboolpg_tzset(constchar*tzname);
4653
externbooltz_acceptable(void);
4754
externconstchar*select_default_timezone(void);
4855
externconstchar*pg_get_current_timezone(void);

‎src/timezone/localtime.c

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
44
*
55
* IDENTIFICATION
6-
* $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.8 2004/08/29 05:07:02 momjian Exp $
6+
* $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.9 2004/11/01 21:34:44 tgl Exp $
77
*/
88

99
/*
@@ -1059,6 +1059,100 @@ timesub(const pg_time_t *timep, const long offset,
10591059
tmp->tm_gmtoff=offset;
10601060
}
10611061

1062+
/*
1063+
* Find the next DST transition time at or after the given time
1064+
*
1065+
* *timep is the input value, the other parameters are output values.
1066+
*
1067+
* When the function result is 1, *boundary is set to the time_t
1068+
* representation of the next DST transition time at or after *timep,
1069+
* *before_gmtoff and *before_isdst are set to the GMT offset and isdst
1070+
* state prevailing just before that boundary, and *after_gmtoff and
1071+
* *after_isdst are set to the state prevailing just after that boundary.
1072+
*
1073+
* When the function result is 0, there is no known DST transition at or
1074+
* after *timep, but *before_gmtoff and *before_isdst indicate the GMT
1075+
* offset and isdst state prevailing at *timep. (This would occur in
1076+
* DST-less time zones, for example.)
1077+
*
1078+
* A function result of -1 indicates failure (this case does not actually
1079+
* occur in our current implementation).
1080+
*/
1081+
int
1082+
pg_next_dst_boundary(constpg_time_t*timep,
1083+
longint*before_gmtoff,
1084+
int*before_isdst,
1085+
pg_time_t*boundary,
1086+
longint*after_gmtoff,
1087+
int*after_isdst)
1088+
{
1089+
registerstructstate*sp;
1090+
registerconststructttinfo*ttisp;
1091+
inti;
1092+
intj;
1093+
constpg_time_tt=*timep;
1094+
1095+
sp=lclptr;
1096+
if (sp->timecnt==0)
1097+
{
1098+
/* non-DST zone, use lowest-numbered standard type */
1099+
i=0;
1100+
while (sp->ttis[i].tt_isdst)
1101+
if (++i >=sp->typecnt)
1102+
{
1103+
i=0;
1104+
break;
1105+
}
1106+
ttisp=&sp->ttis[i];
1107+
*before_gmtoff=ttisp->tt_gmtoff;
1108+
*before_isdst=ttisp->tt_isdst;
1109+
return0;
1110+
}
1111+
if (t>sp->ats[sp->timecnt-1])
1112+
{
1113+
/* No known transition >= t, so use last known segment's type */
1114+
i=sp->types[sp->timecnt-1];
1115+
ttisp=&sp->ttis[i];
1116+
*before_gmtoff=ttisp->tt_gmtoff;
1117+
*before_isdst=ttisp->tt_isdst;
1118+
return0;
1119+
}
1120+
if (t <=sp->ats[0])
1121+
{
1122+
/* For "before", use lowest-numbered standard type */
1123+
i=0;
1124+
while (sp->ttis[i].tt_isdst)
1125+
if (++i >=sp->typecnt)
1126+
{
1127+
i=0;
1128+
break;
1129+
}
1130+
ttisp=&sp->ttis[i];
1131+
*before_gmtoff=ttisp->tt_gmtoff;
1132+
*before_isdst=ttisp->tt_isdst;
1133+
*boundary=sp->ats[0];
1134+
/* And for "after", use the first segment's type */
1135+
i=sp->types[0];
1136+
ttisp=&sp->ttis[i];
1137+
*after_gmtoff=ttisp->tt_gmtoff;
1138+
*after_isdst=ttisp->tt_isdst;
1139+
return1;
1140+
}
1141+
/* Else search to find the containing segment */
1142+
for (i=1;i<sp->timecnt;++i)
1143+
if (t <=sp->ats[i])
1144+
break;
1145+
j=sp->types[i-1];
1146+
ttisp=&sp->ttis[j];
1147+
*before_gmtoff=ttisp->tt_gmtoff;
1148+
*before_isdst=ttisp->tt_isdst;
1149+
*boundary=sp->ats[i];
1150+
j=sp->types[i];
1151+
ttisp=&sp->ttis[j];
1152+
*after_gmtoff=ttisp->tt_gmtoff;
1153+
*after_isdst=ttisp->tt_isdst;
1154+
return1;
1155+
}
10621156

10631157
/*
10641158
* Return the name of the current timezone

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp