[tz] [PATCH 8/8] Improve zdump -v tm_year extrema display
Tim Parenti
tim at timtimeonline.com
Tue Sep 14 02:32:26 UTC 2021
The prior behavior was offset from absolute_{min,max}_time by SECSPERDAY;
the attached fixes this description of the prior behavior in NEWS.
--
Tim Parenti
On Wed, 24 Mar 2021 at 00:01, Paul Eggert via tz <tz at iana.org> wrote:
> * NEWS, zdump.8: Document the new behavior.
> * zdump.c (main): Let showextrema do the work.
> Don’t bother with the one-hour-away timestamp.
> (hunt): New args LOTMP and ONLY_OK. All callers changed.
> (showextrema): New function. Don’t assume there’s just a single
> transition near the boundary; there could be more if a timezone or
> leap-second transition is nearby.
> ---
> NEWS | 5 +++
> zdump.8 | 17 +++++----
> zdump.c | 112 ++++++++++++++++++++++++++++++++++++++++----------------
> 3 files changed, 96 insertions(+), 38 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 20b789c..593c3fa 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -90,6 +90,11 @@ Unreleased, experimental changes
> seconds on the rare platforms where time_t counts leap seconds,
> fixing a bug introduced in 2014g.
>
> + zdump -v now outputs timestamps at boundaries of what localtime
> + and gmtime can represent, instead of the less-useful timestamps
> + one hour after the minimum and one hour before the maximum.
> + (Thanks to Arthur David Olson for prototype code.)
> +
> zdump's -c and -t options are now consistently inclusive for the
> lower time bound and exclusive for the upper. Formerly they were
> inconsistent. (Confusion noted by Martin Burnicki.)
> diff --git a/zdump.8 b/zdump.8
> index 17c0200..a5e2042 100644
> --- a/zdump.8
> +++ b/zdump.8
> @@ -44,12 +44,14 @@ Output a verbose description of time intervals.
> For each
> .I timezone
> on the command line,
> -print the time at the lowest possible time value,
> -the time one day after the lowest possible time value,
> +print the times at the two extreme time values,
> +the times (if present) at and just beyond the boundaries of years that
> +.BR localtime (3)
> +and
> +.BR gmtime (3)
> +can represent, and
> the times both one second before and exactly at
> -each detected time discontinuity,
> -the time at one day less than the highest possible time value,
> -and the time at the highest possible time value.
> +each detected time discontinuity.
> Each line is followed by
> .BI isdst= D
> where
> @@ -66,7 +68,7 @@ seconds east of Greenwich.
> .B \*-V
> Like
> .BR \*-v ,
> -except omit the times relative to the extreme time values.
> +except omit output concerning extreme time and year values.
> This generates output that is easier to compare to that of
> implementations with different time representations.
> .TP
> @@ -200,7 +202,8 @@ This time zone is east of UT, so its UT offsets are
> positive. Also,
> many of its time zone abbreviations are omitted since they duplicate
> the text of the UT offset.
> .SH LIMITATIONS
> -Time discontinuities are found by sampling the results returned by
> localtime
> +Time discontinuities are found by sampling the results returned by
> +.BR localtime (3)
> at twelve-hour intervals.
> This works in all real-world cases;
> one can construct artificial time zones for which this fails.
> diff --git a/zdump.c b/zdump.c
> index fbe53cd..e1db329 100644
> --- a/zdump.c
> +++ b/zdump.c
> @@ -91,8 +91,9 @@ static bool errout;
> static char const *abbr(struct tm const *);
> static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
> static void dumptime(struct tm const *);
> -static time_t hunt(timezone_t, char *, time_t, time_t);
> +static time_t hunt(timezone_t, char *, time_t, struct tm *, time_t, bool);
> static void show(timezone_t, char *, time_t, bool);
> +static void showextrema(timezone_t, char *, time_t, struct tm *, time_t);
> static void showtrans(char const *, struct tm const *, time_t, char const
> *,
> char const *);
> static const char *tformat(void);
> @@ -533,6 +534,7 @@ main(int argc, char *argv[])
> char const *ab;
> time_t t;
> struct tm tm, newtm;
> + struct tm *tmp;
> bool tm_ok;
> if (!tz) {
> perror(argv[i]);
> @@ -547,21 +549,19 @@ main(int argc, char *argv[])
> t = absolute_min_time;
> if (! (iflag | Vflag)) {
> show(tz, argv[i], t, true);
> - t += SECSPERDAY;
> - show(tz, argv[i], t, true);
> - if (my_localtime_rz(tz, &t, &tm) == NULL && t <
> cutlotime) {
> + if (my_localtime_rz(tz, &t, &tm) == NULL
> + && t < cutlotime) {
> time_t newt = cutlotime;
> - if (my_localtime_rz(tz, &newt, &newtm) !=
> NULL) {
> - newt = hunt(tz, argv[i], t, newt);
> - show(tz, argv[i], newt - 1, true);
> - show(tz, argv[i], newt, true);
> - }
> + if (my_localtime_rz(tz, &newt, &newtm)
> + != NULL)
> + showextrema(tz, argv[i], t, NULL, newt);
> }
> }
> if (t + 1 < cutlotime)
> t = cutlotime - 1;
> INITIALIZE (ab);
> - tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
> + tmp = my_localtime_rz(tz, &t, &tm);
> + tm_ok = tmp != NULL;
> if (tm_ok) {
> ab = saveabbr(&abbrev, &abbrevsize, &tm);
> if (iflag) {
> @@ -580,7 +580,7 @@ main(int argc, char *argv[])
> || (tm_ok && (delta(&newtm, &tm) != newt - t
> || newtm.tm_isdst != tm.tm_isdst
> || strcmp(abbr(&newtm), ab) != 0))) {
> - newt = hunt(tz, argv[i], t, newt);
> + newt = hunt(tz, argv[i], t, tmp, newt, false);
> newtmp = localtime_rz(tz, &newt, &newtm);
> newtm_ok = newtmp != NULL;
> if (iflag)
> @@ -600,20 +600,14 @@ main(int argc, char *argv[])
> }
> if (! (iflag | Vflag)) {
> time_t newt = absolute_max_time;
> - newt -= SECSPERDAY;
> t = cuthitime;
> - if (t < newt &&
> - my_localtime_rz(tz, &t, &tm) != NULL &&
> - my_localtime_rz(tz, &newt, &newtm) ==
> NULL) {
> - newt = hunt(tz, argv[i], t, newt);
> - show(tz, argv[i], newt - 1, true);
> - show(tz, argv[i], newt, true);
> + if (t < newt) {
> + tmp = my_localtime_rz(tz, &t, &tm);
> + if (tmp != NULL
> + && my_localtime_rz(tz, &newt, &newtm) ==
> NULL)
> + showextrema(tz, argv[i], t, tmp, newt);
> }
> - t = absolute_max_time;
> - t -= SECSPERDAY;
> - show(tz, argv[i], t, true);
> - t += SECSPERDAY;
> - show(tz, argv[i], t, true);
> + show(tz, argv[i], absolute_max_time, true);
> }
> tzfree(tz);
> }
> @@ -666,19 +660,30 @@ yeartot(intmax_t y)
> return t;
> }
>
> +/* Search for a discontinuity in timezone TZ with name NAME, in the
> + timestamps ranging from LOT (with broken-down time LOTMP if
> + nonnull) through HIT. LOT and HIT disagree about some aspect of
> + timezone. If ONLY_OK, search only for definedness changes, i.e.,
> + localtime succeeds on one side of the transition but fails on the
> + other side. Return the timestamp just before the transition from
> + LOT's settings. */
> +
> static time_t
> -hunt(timezone_t tz, char *name, time_t lot, time_t hit)
> +hunt(timezone_t tz, char *name, time_t lot, struct tm *lotmp, time_t hit,
> + bool only_ok)
> {
> static char * loab;
> static size_t loabsize;
> char const * ab;
> struct tm lotm;
> struct tm tm;
> - bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
> + bool lotm_ok = lotmp != NULL;
> bool tm_ok;
>
> - if (lotm_ok)
> + if (lotm_ok) {
> + lotm = *lotmp;
> ab = saveabbr(&loab, &loabsize, &lotm);
> + }
> for ( ; ; ) {
> /* T = average of LOT and HIT, rounding down.
> Avoid overflow, even on oddball C89 platforms
> @@ -691,11 +696,11 @@ hunt(timezone_t tz, char *name, time_t lot, time_t
> hit)
> if (t == lot)
> break;
> tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
> - if (lotm_ok & tm_ok
> - ? (delta(&tm, &lotm) == t - lot
> - && tm.tm_isdst == lotm.tm_isdst
> - && strcmp(abbr(&tm), ab) == 0)
> - : lotm_ok == tm_ok) {
> + if (lotm_ok == tm_ok
> + && (only_ok
> + || (lotm_ok && tm.tm_isdst == lotm.tm_isdst
> + && delta(&tm, &lotm) == t - lot
> + && strcmp(abbr(&tm), ab) == 0))) {
> lot = t;
> if (tm_ok)
> lotm = tm;
> @@ -819,6 +824,51 @@ show(timezone_t tz, char *zone, time_t t, bool v)
> abbrok(abbr(tmp), zone);
> }
>
> +/* Show timestamps just before and just after a transition between
> + defined and undefined (or vice versa) in either localtime or
> + gmtime. These transitions are for timezone TZ with name ZONE, in
> + the range from LO (with broken-down time LOTMP if that is nonnull)
> + through HI. LO and HI disagree on definedness. */
> +
> +static void
> +showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp,
> time_t hi)
> +{
> + struct tm localtm[2], gmtm[2];
> + time_t t, boundary = hunt(tz, zone, lo, lotmp, hi, true);
> + bool old = false;
> + hi = (SECSPERDAY < hi - boundary
> + ? boundary + SECSPERDAY
> + : hi + (hi < TIME_T_MAX));
> + if (SECSPERDAY < boundary - lo) {
> + lo = boundary - SECSPERDAY;
> + lotmp = my_localtime_rz(tz, &lo, &localtm[old]);
> + }
> + if (lotmp)
> + localtm[old] = *lotmp;
> + else
> + localtm[old].tm_sec = -1;
> + if (! my_gmtime_r(&lo, &gmtm[old]))
> + gmtm[old].tm_sec = -1;
> +
> + /* Search sequentially for definedness transitions. Although this
> + could be sped up by refining 'hunt' to search for either
> + localtime or gmtime definedness transitions, it hardly seems
> + worth the trouble. */
> + for (t = lo + 1; t < hi; t++) {
> + bool new = !old;
> + if (! my_localtime_rz(tz, &t, &localtm[new]))
> + localtm[new].tm_sec = -1;
> + if (! my_gmtime_r(&t, &gmtm[new]))
> + gmtm[new].tm_sec = -1;
> + if (((localtm[old].tm_sec < 0) != (localtm[new].tm_sec < 0))
> + | ((gmtm[old].tm_sec < 0) != (gmtm[new].tm_sec < 0))) {
> + show(tz, zone, t - 1, true);
> + show(tz, zone, t, true);
> + }
> + old = new;
> + }
> +}
> +
> #if HAVE_SNPRINTF
> # define my_snprintf snprintf
> #else
> --
> 2.27.0
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mm.icann.org/pipermail/tz/attachments/20210913/d9f0f8db/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-NEWS-Fix-typo-describing-zic-s-old-boundary-behvaior.patch
Type: application/x-patch
Size: 800 bytes
Desc: not available
URL: <https://mm.icann.org/pipermail/tz/attachments/20210913/d9f0f8db/0001-NEWS-Fix-typo-describing-zic-s-old-boundary-behvaior-0001.patch>
More information about the tz
mailing list