Subject: Re: bug#79437: 30.2; world-clock silently and incorrectly parses invalid time zone

From: Paul Eggert <[email-masked]>
Date: Sun, 14 Sep 2025 10:30:37 -0700

On 2025-09-13 22:24, Eli Zaretskii wrote:

> Only the second one can be checked relatively easily by looking in the
> directory where the zoneinfo files are installed.  But how does one
> validate the other 2 forms?  And how does one know which format iz
> used to begin with?

Plus, not every TZDB installation has "zoneinfo files" in the usual 
sense; some have a database using some other format.

However, something along those lines could be done, if somebody wanted 
to maintain it (I don't). The idea is not to exactly validate the forms; 
instead, do a reasonably conservative validation that rejects forms that 
(as as we know) are not supported on any Emacs platform. This would 
accept some unlikely mistakes, but would be good enough for most users.

Something like the following, perhaps, and then have world-clock-display 
run (world-clock--check-entries alist) first thing.

None of this is tested; it's just a sketch. And I'm not advocating for 
this change as it's hacky and I would rather not support it, but if 
somebody else wants to take it up it should work acceptably.

   (defconst world-clock--tz-regexp
     (let* ((abbr "\\([A-Za-z]\\{3,\\}\|<[A-Za-z0-9+-]\\{3,\\}>\\)")
            (offset "\\([+-]?[0-9]+\\(:[0-5][0-9]\\(:[0-5][0-9]\\)?\\)?\\)")
            (date "\\(J?[0-9]+\\|M\\([1-9]\\|1[0-2]\\)\\.[1-5]\\.[0-6]\\)")
            (time offset)
            (comma-date-time (concat "," date "\\(/" time "\\)?"))

            ;; TZDB time zones at UTC offset zero at the Epoch.
            ;; The others are filtered out by the call to current-time-zone.
            ;; This list is for circa 2025 TZDB and may need updating.
            (tzdb+0000-regexp
  
"\\(Africa/\\(Abidjan\\|Accra\\|Bamako\\|Banjul\\|Bissau\\|Conakry"
             "" "\\|Dakar\\|Freetown\\|Lome\\|Monrovia\\|Nouakchott"
             "" "\\|Ouagadougou\\|Sao_Tome\\|Timbuktu\\)"
             "\\|America/Danmarkshavn"
             "\\|Atlantic/\\(Azores\\|Reykjavik\\|St_Helena\\)"
             "\\|\\(Etc/\\)?\\(GMT[+-]?0\\|Greenwich\\|UCT\\|UTC"
             "" "\\|Universal\\|Zulu\\)"
             "\\|Iceland")

            ;; POSIX.1-2017 (and earlier) TZ strings.
            (posix-tz-regexp
             (concat "\\(" abbr offset
                     "\\(" abbr offset
                     "?\\(" comma-date-time comma-date-time
                     "\\)?\\)?\\)")))
       (concat "\\`:?\\(" tzdb+0000-regexp
               "\\|" posix-tz-regexp "\\)\\'")))

   (defun world-clock--check-entries (alist)
     (dolist (pair alist)
       (let ((tz (car pair)))
         (unless (string-match-p world-clock--tz-regexp tz)
           (when (zerop (car (current-time-zone 0 tz)))
             (error "Invalid time zone specification" tz))))))

PS. I noticed some other glitches in this area and installed the 
attached patch on master. It's a pain to maintain this stuff!

From 897d32285fc17b8afd889b1f733aed7149b50a5c Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 14 Sep 2025 10:06:51 -0700
Subject: [PATCH] Fix incorrect timezones for London and Paris
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Also, replace obsolete city name "Bangalore" with "Delhi",
and improve doc strings.
* lisp/time.el (zoneinfo-style-world-list)
(legacy-style-world-list): Evidently "Bangalore" was chosen for
Asia/Kolkata to avoid confusion about "Calcutta" vs "Kolkata".
However, it has similar confusion with "Bengaluru".
Use "Delhi" instead, as Delhi is larger, its name has not changed
for some time, and proposals to change its name have so far failed.
See: Sharma MS. Vijay Goel wants Delhi renamed as Dilli, but historians
say it will only spell trouble. Times of India. 2019-07-25.
(zoneinfo-style-world-list): Update doc string. “Posix-style” was
ambiguous as TZDB-style strings are specified only by POSIX.1-2024,
so use “TZDB-style” instead. Say “AREA/LOCATION” to be consistent.
If AREA/LOCATION is unsupported, Emacs signals an error on NetBSD
so don’t imply that some other timezone will be used.
(legacy-style-world-list): Remove ("GMT0BST" "London") and ("CET-1CDT"
"Paris"). These are wrong because neither London nor Paris follow
US DST rules.  Instead, use ("GMT0BST,M3.5.0/1,M10.5.0" "London")
and ("CET-1CEST,M3.5.0,M10.5.0/3" "Paris") if they work, and omit
London and Paris entries otherwise.
---
 lisp/time.el | 36 +++++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/lisp/time.el b/lisp/time.el
index 517eea20ad1..ecacaf08aa4 100644
--- a/lisp/time.el
+++ b/lisp/time.el
@@ -452,7 +452,7 @@ zoneinfo-style-world-list
     ("America/New_York" "New York")
     ("Europe/London" "London")
     ("Europe/Paris" "Paris")
-    ("Asia/Kolkata" "Bangalore")
+    ("Asia/Kolkata" "Delhi")
     ("Asia/Tokyo" "Tokyo"))
   "Alist of zoneinfo-style time zones and places for `world-clock'.
 Each element has the form (TIMEZONE LABEL).
@@ -461,27 +461,37 @@ zoneinfo-style-world-list
 of a specific location, e.g., a city, within that region.
 LABEL is a string to display as the label of that TIMEZONE's time.
 
-This option has effect only on systems that support Posix-style
-zoneinfo files specified as CONTINENT/CITY.  In particular,
+This option has effect only on systems that support TZDB-style
+TZ strings specified as AREA/LOCATION.  In particular,
 MS-Windows doesn't support that; use `legacy-style-world-list' instead.
-Also, AREA/LOCATION must specify a zoneinfo file installed on your
-system, otherwise what timezone will be used depends on the underlying
-OS and library."
+Also, AREA/LOCATION must specify a timezone supported by your system,
+otherwise the behavior depends on the underlying OS and library."
   :type '(repeat (list string string))
   :version "23.1")
 
 (defcustom legacy-style-world-list
-  '(("PST8PDT" "Seattle")
-    ("EST5EDT" "New York")
-    ("GMT0BST" "London")
-    ("CET-1CDT" "Paris")
-    ("IST-5:30" "Bangalore")
-    ("JST-9" "Tokyo"))
+  ;; This list is for circa 2025 timekeeping, and will need changes
+  ;; if any of these locations change names, time zones, or DST rules.
+  (append
+   ;; These entries assume the platform's default DST rules are for the US,
+   ;; which is true of all known platforms that use this variable.
+   '(("PST8PDT" "Seattle")
+     ("EST5EDT" "New York"))
+
+   ;; Use POSIX.1-2017 style TZ if supported; otherwise, give up on London
+   ;; and Paris as "GMT0BST" and "CET-1CDT" would use incorrect DST.
+   (when (string-equal (format-time-string "%z" 0 "XXX0YYY,M9.5.0/1,M4.1.0/3")
+		       "+0100")
+     '(("GMT0BST,M3.5.0/1,M10.5.0" "London")
+       ("CET-1CEST,M3.5.0,M10.5.0/3" "Paris")))
+
+   '(("IST-5:30" "Delhi")
+     ("JST-9" "Tokyo")))
   "Alist of traditional-style time zones and places for `world-clock'.
 Each element has the form (TIMEZONE LABEL).
 TIMEZONE should be a string of the form:
 
-     std[+|-]offset[dst[offset][,date[/time],date[/time]]]
+     std[+|-]offset[dst[[+|-]offset][,date[/time],date[/time]]]
 
 See the documentation of the TZ environment variable on your system,
 for more details about the format of TIMEZONE.
-- 
2.48.1