With the current localtime.c implementation, increasing
TZ_MAX_CHARS from 50 to its maximum value 256 does not allocate
more memory, so there is little point to the limit of 50.
* NEWS, localtime.c: Mention this.
* tzfile.h (TZ_MAX_CHARS): Increase from 50 to 256.
* zic.c (writezone): If -v, warn about going over the old limit.
---
 NEWS        | 7 +++++++
 localtime.c | 4 ++--
 tzfile.h    | 6 +++---
 zic.c       | 5 +++++
 4 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index 95b1851c..dd592871 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ Unreleased, experimental changes
   Briefly:
     The "right" TZif files are no longer installed by default.
     -DTZ_RUNTIME_LEAPS=0 disables runtime support for leap seconds.
+    TZif files are no longer limited to 50 bytes of abbreviations.
     Several integer overflow bugs have been fixed.
 
   Changes to build procedure
@@ -29,6 +30,12 @@ Unreleased, experimental changes
     POSIX, shrinks tzcode's attack surface, and is more efficient,
     it fails to support Internet RFC 9636's leap seconds.
 
+    zic now can generate, and localtime.c can now use, TZif files that
+    hold up to 256 bytes of abbreviations, counting trailing NULs.
+    The previous limit was 50 bytes, and some tzdata TZif files were
+    already consuming 40 bytes.  zic -v warns if it generates a file
+    that exceeds the old 50-byte limit.
+
     localtime.c no longer accesses the posixrules file generated by
     zic -p.  Hence for obsolete and nonconforming settings like
     TZ="AST4ADT" it now typically falls back on US DST rules, rather
diff --git a/localtime.c b/localtime.c
index ed7a32f2..32835d08 100644
--- a/localtime.c
+++ b/localtime.c
@@ -1227,8 +1227,8 @@ tzloadbody(char const *name, struct state *sp, char 
tzloadflags,
                        if (tzparse(&up->buf[1], ts, sp)) {
 
                          /* Attempt to reuse existing abbreviations.
-                            Without this, America/Anchorage would be right on
-                            the edge after 2037 when TZ_MAX_CHARS is 50, as
+                            Without this, America/Anchorage would
+                            consume 50 bytes for abbreviations, as
                             sp->charcnt equals 40 (for LMT AST AWT APT AHST
                             AHDT YST AKDT AKST) and ts->charcnt equals 10
                             (for AKST AKDT).  Reusing means sp->charcnt can
diff --git a/tzfile.h b/tzfile.h
index 71b60f37..1b1bac4a 100644
--- a/tzfile.h
+++ b/tzfile.h
@@ -95,13 +95,13 @@ struct tzhead {
 
 #ifndef TZ_MAX_TYPES
 /* This must be at least 18 for Europe/Vilnius with 'zic -b fat'.  */
-# define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+# define TZ_MAX_TYPES 256 /* Limited to 256 by Internet RFC 9636.  */
 #endif /* !defined TZ_MAX_TYPES */
 
 #ifndef TZ_MAX_CHARS
 /* This must be at least 40 for America/Anchorage.  */
-# define TZ_MAX_CHARS 50       /* Maximum number of abbreviation characters */
-                               /* (limited by what unsigned chars can hold) */
+# define TZ_MAX_CHARS 256      /* Maximum number of abbreviation characters */
+                               /* (limited to 256 by Internet RFC 9636) */
 #endif /* !defined TZ_MAX_CHARS */
 
 #ifndef TZ_MAX_LEAPS
diff --git a/zic.c b/zic.c
index 73155138..1a2f4741 100644
--- a/zic.c
+++ b/zic.c
@@ -2986,6 +2986,11 @@ writezone(const char *const name, const char *const 
string, char version,
                  continue;
                }
 
+               if (pass == 2 && noise && 50 < thischarcnt)
+                 warning(_("%s: pre-2026 reference clients mishandle"
+                           " more than 50 bytes of abbreviations"),
+                         name);
+
                /* Output a LO_TIME transition if needed; see limitrange.
                   But do not go below the minimum representable value
                   for this pass.  */
-- 
2.51.0

Reply via email to