Skip to content

Commit b0f24b5

Browse files
committed
Fix edge-case behavior of pg_next_dst_boundary().
Due to rather sloppy thinking (on my part, I'm afraid) about the appropriate behavior for boundary conditions, pg_next_dst_boundary() gave undefined, platform-dependent results when the input time is exactly the last recorded DST transition time for the specified time zone, as a result of fetching values one past the end of its data arrays. Change its specification to be that it always finds the next DST boundary *after* the input time, and adjust code to match that. The sole existing caller, DetermineTimeZoneOffset, doesn't actually care about this distinction, since it always uses a probe time earlier than the instant that it does care about. So it seemed best to me to change the API to make the result=1 and result=0 cases more consistent, specifically to ensure that the "before" outputs always describe the state at the given time, rather than hacking the code to obey the previous API comment exactly. Per bug #6605 from Sergey Burladyan. Back-patch to all supported versions.
1 parent 5969ee4 commit b0f24b5

File tree

1 file changed

+12
-11
lines changed

1 file changed

+12
-11
lines changed

src/timezone/localtime.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,20 +1290,21 @@ increment_overflow(int *number, int delta)
12901290
}
12911291

12921292
/*
1293-
* Find the next DST transition time at or after the given time
1293+
* Find the next DST transition time after the given time
12941294
*
12951295
* *timep is the input value, the other parameters are output values.
12961296
*
12971297
* When the function result is 1, *boundary is set to the time_t
1298-
* representation of the next DST transition time at or after *timep,
1298+
* representation of the next DST transition time after *timep,
12991299
* *before_gmtoff and *before_isdst are set to the GMT offset and isdst
1300-
* state prevailing just before that boundary, and *after_gmtoff and
1301-
* *after_isdst are set to the state prevailing just after that boundary.
1300+
* state prevailing just before that boundary (in particular, the state
1301+
* prevailing at *timep), and *after_gmtoff and *after_isdst are set to
1302+
* the state prevailing just after that boundary.
13021303
*
1303-
* When the function result is 0, there is no known DST transition at or
1304+
* When the function result is 0, there is no known DST transition
13041305
* after *timep, but *before_gmtoff and *before_isdst indicate the GMT
13051306
* offset and isdst state prevailing at *timep. (This would occur in
1306-
* DST-less time zones, for example.)
1307+
* DST-less time zones, or if a zone has permanently ceased using DST.)
13071308
*
13081309
* A function result of -1 indicates failure (this case does not actually
13091310
* occur in our current implementation).
@@ -1383,16 +1384,16 @@ pg_next_dst_boundary(const pg_time_t *timep,
13831384
return result;
13841385
}
13851386

1386-
if (t > sp->ats[sp->timecnt - 1])
1387+
if (t >= sp->ats[sp->timecnt - 1])
13871388
{
1388-
/* No known transition >= t, so use last known segment's type */
1389+
/* No known transition > t, so use last known segment's type */
13891390
i = sp->types[sp->timecnt - 1];
13901391
ttisp = &sp->ttis[i];
13911392
*before_gmtoff = ttisp->tt_gmtoff;
13921393
*before_isdst = ttisp->tt_isdst;
13931394
return 0;
13941395
}
1395-
if (t <= sp->ats[0])
1396+
if (t < sp->ats[0])
13961397
{
13971398
/* For "before", use lowest-numbered standard type */
13981399
i = 0;
@@ -1413,10 +1414,10 @@ pg_next_dst_boundary(const pg_time_t *timep,
14131414
*after_isdst = ttisp->tt_isdst;
14141415
return 1;
14151416
}
1416-
/* Else search to find the containing segment */
1417+
/* Else search to find the boundary following t */
14171418
{
14181419
int lo = 1;
1419-
int hi = sp->timecnt;
1420+
int hi = sp->timecnt - 1;
14201421

14211422
while (lo < hi)
14221423
{

0 commit comments

Comments
 (0)