]> granicus.if.org Git - postgresql/commitdiff
Fix edge-case behavior of pg_next_dst_boundary().
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 25 Apr 2012 21:25:12 +0000 (17:25 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 25 Apr 2012 21:26:10 +0000 (17:26 -0400)
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.

src/timezone/localtime.c

index 037cdbbdcd7038a531c2526b8d8b2baa9928e985..5079868965c8d3226142f189814c5efa375ef728 100644 (file)
@@ -1292,20 +1292,21 @@ increment_overflow(int *number, int delta)
 }
 
 /*
- * Find the next DST transition time at or after the given time
+ * Find the next DST transition time after the given time
  *
  * *timep is the input value, the other parameters are output values.
  *
  * When the function result is 1, *boundary is set to the time_t
- * representation of the next DST transition time at or after *timep,
+ * representation of the next DST transition time after *timep,
  * *before_gmtoff and *before_isdst are set to the GMT offset and isdst
- * state prevailing just before that boundary, and *after_gmtoff and
- * *after_isdst are set to the state prevailing just after that boundary.
+ * state prevailing just before that boundary (in particular, the state
+ * prevailing at *timep), and *after_gmtoff and *after_isdst are set to
+ * the state prevailing just after that boundary.
  *
- * When the function result is 0, there is no known DST transition at or
+ * When the function result is 0, there is no known DST transition
  * after *timep, but *before_gmtoff and *before_isdst indicate the GMT
  * offset and isdst state prevailing at *timep.  (This would occur in
- * DST-less time zones, for example.)
+ * DST-less time zones, or if a zone has permanently ceased using DST.)
  *
  * A function result of -1 indicates failure (this case does not actually
  * occur in our current implementation).
@@ -1385,16 +1386,16 @@ pg_next_dst_boundary(const pg_time_t *timep,
                return result;
        }
 
-       if (t > sp->ats[sp->timecnt - 1])
+       if (t >= sp->ats[sp->timecnt - 1])
        {
-               /* No known transition >= t, so use last known segment's type */
+               /* No known transition > t, so use last known segment's type */
                i = sp->types[sp->timecnt - 1];
                ttisp = &sp->ttis[i];
                *before_gmtoff = ttisp->tt_gmtoff;
                *before_isdst = ttisp->tt_isdst;
                return 0;
        }
-       if (t <= sp->ats[0])
+       if (t < sp->ats[0])
        {
                /* For "before", use lowest-numbered standard type */
                i = 0;
@@ -1415,10 +1416,10 @@ pg_next_dst_boundary(const pg_time_t *timep,
                *after_isdst = ttisp->tt_isdst;
                return 1;
        }
-       /* Else search to find the containing segment */
+       /* Else search to find the boundary following t */
        {
                int                     lo = 1;
-               int                     hi = sp->timecnt;
+               int                     hi = sp->timecnt - 1;
 
                while (lo < hi)
                {