>From 8744ddc1497dc4c2ce3c47d5bca895a2fdf2406b Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Fri, 15 Mar 2019 09:20:31 -0700
Subject: [PROPOSED] zic -r/@T is now exclusive, not inclusive

This is for consistency with RFC 8536 section 5.1 and RFC 7808
section 3.9.  (Problem reported by Christopher Wong.)
* zic.8: Document this.
* zic.c (timerange_option): Subtract one from the specified upper
bound before using it, unless the actual upper bound exceeded the
maximum representable integer.  Check more carefully for integer
overflow and empty ranges.
---
 zic.8 |  8 ++++----
 zic.c | 23 +++++++++++++++--------
 2 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/zic.8 b/zic.8
index 5ca7079..524a113 100644
--- a/zic.8
+++ b/zic.8
@@ -83,9 +83,9 @@ no leap second information appears in output files.
 Reduce the size of output files by limiting their applicability
 to timestamps in the range from
 .I lo
-through
-.IR hi ,
-where
+(inclusive) to
+.I hi
+(exclusive), where
 .I lo
 and
 .I hi
@@ -95,7 +95,7 @@ Omitted counts default to extreme values.
 For example,
 .q "zic \*-r @0"
 omits data intended for negative timestamps (i.e., before the Epoch), and
-.q "zic \*-r @0/@2147483647"
+.q "zic \*-r @0/@2147483648"
 outputs data intended only for nonnegative timestamps that fit into
 31-bit signed integers.
 On platforms with GNU
diff --git a/zic.c b/zic.c
index 493fb65..918c67a 100644
--- a/zic.c
+++ b/zic.c
@@ -605,8 +605,13 @@ change_directory (char const *dir)
 }
 
 #define TIME_T_BITS_IN_FILE 64
+
+/* The minimum and maximum values representable in a TZif file.  */
 static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
 static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The minimum, and one less than the maxmimum, values specified by
+   the -r option.  These default to MIN_TIME and MAX_TIME.  */
 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);
 
@@ -617,22 +622,24 @@ timerange_option(char *timerange)
 {
   intmax_t lo = min_time, hi = max_time;
   char *lo_end = timerange, *hi_end;
-  bool badrange = false;
   if (*timerange == '@') {
+    errno = 0;
     lo = strtoimax (timerange + 1, &lo_end, 10);
-    if (lo_end == timerange + 1)
-      badrange = true;
+    if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
+      return false;
   }
   hi_end = lo_end;
   if (lo_end[0] == '/' && lo_end[1] == '@') {
+    errno = 0;
     hi = strtoimax (lo_end + 2, &hi_end, 10);
-    if (hi_end == lo_end + 2)
-      badrange = true;
+    if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
+      return false;
+    hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
   }
-  if (badrange || *hi_end || hi < lo)
+  if (*hi_end || hi < lo || max_time < lo || hi < min_time)
     return false;
-  lo_time = lo < min_time ? min_time : lo <= max_time ? lo : max_time;
-  hi_time = hi < min_time ? min_time : hi <= max_time ? hi : max_time;
+  lo_time = lo < min_time ? min_time : lo;
+  hi_time = max_time < hi ? max_time : hi;
   return true;
 }
 
-- 
2.20.1

