[tz] [PATCH] Add -R option to zic

Paul Eggert eggert at cs.ucla.edu
Tue Jul 12 13:20:41 UTC 2022


(Need suggested by Almaz Mingaleev.)
* NEWS, zic.8: Document option.
* zic.c (usage): Mention new option.
(redundant_time): New static var.
(redundant_time_option): New function.
(main, writezone, outzone): Support -R.
---
 NEWS  |  4 ++++
 zic.8 | 11 +++++++++++
 zic.c | 44 +++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index 60deee4..850e38b 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ Unreleased, experimental changes
   Briefly:
     Iran no longer observes DST after 2022.
     Rename Europe/Kiev to Europe/Kyiv.
+    New zic -R option
 
   Changes to future timestamps
 
@@ -38,6 +39,9 @@ Unreleased, experimental changes
 
   Changes to code
 
+    zic has a new option '-R @N' to output explicit transitions < N.
+    (Need suggested by Almaz Mingaleev.)
+
     'zic -r @N' no longer outputs bad data when N < first transition.
     (Problem introduced in 2021d and reported by Peter Krefting.)
 
diff --git a/zic.8 b/zic.8
index 3f228a6..0cd0781 100644
--- a/zic.8
+++ b/zic.8
@@ -159,6 +159,17 @@ Also see the
 .B "\*-b slim"
 option for another way to shrink output size.
 .TP
+.BI "\*-R @" hi
+Generate redundant trailing explicit transitions for timestamps
+that occur less than
+.I hi
+seconds since the Epoch, even though the transitions could be
+more concisely represented via the extended POSIX TZ string.
+This option does not affect the represented timestamps.
+Although it accommodates nonstandard TZif readers
+that ignore the extended POSIX TZ string,
+it increases the size of the altered output files.
+.TP
 .BI "\*-t " file
 When creating local time information, put the configuration link in
 the named file rather than in the standard location.
diff --git a/zic.c b/zic.c
index 19e145a..e081c3d 100644
--- a/zic.c
+++ b/zic.c
@@ -579,7 +579,8 @@ usage(FILE *stream, int status)
 	  _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
 	    "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
 	    " [ -L leapseconds ] \\\n"
-	    "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -t localtime-link ] \\\n"
+	    "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
+	    "\t[ -t localtime-link ] \\\n"
 	    "\t[ filename ... ]\n\n"
 	    "Report bugs to %s.\n"),
 	  progname, progname, REPORT_BUGS_TO);
@@ -681,6 +682,9 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
 static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
 static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
 
+/* The time specified by the -R option, defaulting to MIN_TIME.  */
+static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+
 /* The time specified by an Expires line, or negative if no such line.  */
 static zic_t leapexpires = -1;
 
@@ -712,6 +716,22 @@ timerange_option(char *timerange)
   return true;
 }
 
+/* Generate redundant time stamps up to OPT.  Return true if successful.  */
+static bool
+redundant_time_option(char *opt)
+{
+  if (*opt == '@') {
+    intmax_t redundant;
+    char *opt_end;
+    redundant = strtoimax(opt + 1, &opt_end, 10);
+    if (opt_end != opt + 1 && !*opt_end) {
+      redundant_time = max(redundant_time, redundant);
+      return true;
+    }
+  }
+  return false;
+}
+
 static const char *	psxrules;
 static const char *	lcltime;
 static const char *	directory;
@@ -764,7 +784,8 @@ main(int argc, char **argv)
 		} else if (strcmp(argv[k], "--help") == 0) {
 			usage(stdout, EXIT_SUCCESS);
 		}
-	while ((c = getopt(argc, argv, "b:d:l:L:p:r:st:vy:")) != EOF && c != -1)
+	while ((c = getopt(argc, argv, "b:d:l:L:p:r:R:st:vy:")) != EOF
+	       && c != -1)
 		switch (c) {
 			default:
 				usage(stderr, EXIT_FAILURE);
@@ -851,12 +872,23 @@ _("%s: invalid time range: %s\n"),
 				}
 				timerange_given = true;
 				break;
+			case 'R':
+				if (! redundant_time_option(optarg)) {
+				  fprintf(stderr, _("%s: invalid time: %s\n"),
+					  progname, optarg);
+				  return EXIT_FAILURE;
+				}
+				break;
 			case 's':
 				warning(_("-s ignored"));
 				break;
 		}
 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
 		usage(stderr, EXIT_FAILURE);	/* usage message by request */
+	if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
+	  fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
+	  return EXIT_FAILURE;
+	}
 	if (bloat == 0) {
 	  static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
 	  if (strcmp(bloat_default, "slim") == 0)
@@ -2139,7 +2171,10 @@ writezone(const char *const name, const char *const string, char version,
 	rangeall.defaulttype = defaulttype;
 	rangeall.count = timecnt;
 	rangeall.leapcount = leapcnt;
-	range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
+	range64 = limitrange(rangeall, lo_time,
+			     max(hi_time,
+				 redundant_time - (ZIC_MIN < redundant_time)),
+			     ats, types);
 	range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
 
 	/* TZif version 4 is needed if a no-op transition is appended to
@@ -2893,6 +2928,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 			max_year = min_year + years_of_observations;
 		}
 	}
+	max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
+				  + EPOCH_YEAR + 1));
 	max_year0 = max_year;
 	if (want_bloat()) {
 	  /* For the benefit of older systems,
@@ -3061,6 +3098,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 				offset = oadd(zp->z_stdoff, rp->r_save);
 				if (!want_bloat() && !useuntil && !do_extend
 				    && prevrp && lo_time <= prevktime
+				    && redundant_time <= ktime
 				    && rp->r_hiyear == ZIC_MAX
 				    && prevrp->r_hiyear == ZIC_MAX)
 				  break;
-- 
2.34.1



More information about the tz mailing list