branch: elpa/datetime
commit 8a1d0ba701ffb9a00ed91ff3e1a6ae749dd506c2
Author: Paul Pogonyshev <pogonys...@gmail.com>
Commit: Paul Pogonyshev <pogonys...@gmail.com>

    Improve on commit 8cfa779 further; again, important mostly for Windows.
---
 datetime.el          |   4 +--
 dev/HarvestData.java |  68 +++++++++++++++++++++++++++++++--------------------
 test/base.el         |  12 ++++++++-
 timezone-data.extmap | Bin 902807 -> 907962 bytes
 4 files changed, 55 insertions(+), 29 deletions(-)

diff --git a/datetime.el b/datetime.el
index 251cc1ef31..d007a7377a 100644
--- a/datetime.el
+++ b/datetime.el
@@ -306,7 +306,7 @@ form:
       (if (and (not (eq system-timezone :aliases)) (extmap-contains-key 
datetime--timezone-extmap system-timezone))
           system-timezone
         (let* ((aliases (extmap-get datetime--timezone-extmap :aliases t))
-               (entry   (assq system-timezone aliases)))
+               (entry   (assoc (symbol-name system-timezone) aliases)))
           (if entry
               (cdr entry)
             (error "Failed to determine system timezone%s; consider 
customizing `datetime-timezone' variable"
@@ -2068,7 +2068,7 @@ create based on timezones `datetime' knows about and 
their rules.
 
 Locale-specific timezone names are contained in a different
 database.  See `datetime-timezone-name-database-version'."
-  8)
+  9)
 
 (defun datetime-timezone-name-database-version ()
   "Return timezone name database version, a simple integer.
diff --git a/dev/HarvestData.java b/dev/HarvestData.java
index 59a71c4d0e..45f907b751 100644
--- a/dev/HarvestData.java
+++ b/dev/HarvestData.java
@@ -326,11 +326,12 @@ public class HarvestData
     protected static void printTimezoneData () throws Exception
     {
         var        data                                  = new LinkedHashMap 
<ZoneId, List <Object>> ();
+        var        matching_abbreviation_aliases         = new LinkedHashMap 
<String, ZoneId> ();
         var        aliases                               = new LinkedHashMap 
<String, Set <ZoneId>> ();
-        var        timezones_with_matching_abbreviations = new HashSet 
<ZoneId> ();
-        var        abbreviation_retriever                = 
DateTimeFormatter.ofPattern ("z", Locale.ENGLISH);
+        var        abbreviation_retriever                = 
DateTimeFormatter.ofPattern ("z",    Locale.ENGLISH);
+        var        full_name_retriever                   = 
DateTimeFormatter.ofPattern ("zzzz", Locale.ENGLISH);
         var        utc_formatter                         = 
DateTimeFormatter.ofPattern ("yyyy-MM-dd HH:mm:ss z");
-        Instant[]  abbreviations_at                      = { Instant.from 
(utc_formatter.parse ("2020-01-01 00:00:00 UTC")),
+        Instant[]  find_aliases_at                       = { Instant.from 
(utc_formatter.parse ("2020-01-01 00:00:00 UTC")),
                                                              Instant.from 
(utc_formatter.parse ("2020-07-01 00:00:00 UTC")) };
 
         for (ZoneId timezone : getAllTimezones ()) {
@@ -440,35 +441,50 @@ public class HarvestData
                 zone_data.add (toLispList (transition_rule_data));
 
                 data.put (timezone, zone_data);
+            }
 
-                for (var at : abbreviations_at) {
-                    var  abbreviation = abbreviation_retriever.format 
(ZonedDateTime.ofInstant (at, timezone));
-                    if (abbreviation.equals (timezone.getId ()))
-                        timezones_with_matching_abbreviations.add (timezone);
-                    else
-                        aliases.computeIfAbsent (abbreviation, __ -> new 
HashSet <> ()).add (timezone);
+            var  abbreviations = (Arrays.stream (find_aliases_at)
+                                  .map ((at) -> abbreviation_retriever.format 
(ZonedDateTime.ofInstant (at, timezone)))
+                                  .collect (Collectors.toSet ()));
+            var  canonical     = abbreviations.contains (timezone.getId ());
+            var  full_names    = (Arrays.stream (find_aliases_at)
+                                  .map ((at) -> full_name_retriever.format 
(ZonedDateTime.ofInstant (at, timezone)))
+                                  .collect (Collectors.toSet ()));
+
+            for (var alias : Stream.concat (abbreviations.stream (), 
full_names.stream ()).collect (Collectors.toSet ())) {
+                if (canonical) {
+                    if (matching_abbreviation_aliases.containsKey (alias)) {
+                        throw new IllegalStateException (String.format 
("cannot decide between %s and %s for alias '%s'",
+                                                                        
matching_abbreviation_aliases.get (alias), timezone, alias));
+                    }
+
+                    matching_abbreviation_aliases.put (alias, timezone);
                 }
+
+                aliases.computeIfAbsent (alias, __ -> new HashSet <> ()).add 
(timezone);
             }
         }
 
         // When computing timezone alias map we use these rules:
         // - if timezone identifier matches its normal abbreviation, its DST 
abbreviation
-        //   becomes an alias (example: "CEST" becomes an alias for "CET");
-        // - else both abbreviations become an alias for the timezone, but 
only if they
-        //   don't clash with anything else (example: "EGT" and "EGST" are 
both aliases
-        //   for "America/Scoresbysund").
-        var  timezones_with_conflicting_aliases = new HashSet <ZoneId> ();
-        for (var timezones : aliases.values ()) {
-            if (timezones.size () > 1)
-                timezones_with_conflicting_aliases.addAll (timezones);
-        }
-
-        timezones_with_conflicting_aliases.removeAll 
(timezones_with_matching_abbreviations);
-
-        for (var it = aliases.values ().iterator (); it.hasNext ();) {
-            var  with_this_alias = it.next ();
-            with_this_alias.removeAll (timezones_with_conflicting_aliases);
-            if (with_this_alias.size () != 1)
+        //   and full names becomes aliases (example: "CEST" and "Central 
European Time"
+        //   become aliases for "CET");
+        // - else both abbreviations and names become an alias for the 
timezone, but only
+        //   if they don't clash with anything else (example: "EGT" and "EGST" 
are both
+        //   aliases for "America/Scoresbysund").
+        for (var it = aliases.entrySet ().iterator (); it.hasNext ();) {
+            var  entry              = it.next ();
+            var  canonical_timezone = matching_abbreviation_aliases.get 
(entry.getKey ());
+
+            if (canonical_timezone != null) {
+                if (entry.getKey ().equals (canonical_timezone.getId ())) {
+                    // To avoid "aliases" like CET -> CET.
+                    it.remove ();
+                }
+                else
+                    entry.setValue (Set.of (canonical_timezone));
+            }
+            else if (entry.getValue ().size () != 1)
                 it.remove ();
         }
 
@@ -481,7 +497,7 @@ public class HarvestData
         // formatting or parsing, currently only when determining OS timezone.
         System.out.format ("(:aliases\n %s)\n",
                            aliases.entrySet ().stream ()
-                           .map ((entry) -> String.format ("(%s . %s)", 
entry.getKey (), entry.getValue ().iterator ().next ().getId ()))
+                           .map ((entry) -> String.format ("(%s . %s)", 
quoteString (entry.getKey ()), entry.getValue ().iterator ().next ().getId ()))
                            .collect (Collectors.joining ("\n ")));
 
         System.out.println (")");
diff --git a/test/base.el b/test/base.el
index c45682a075..dc27b45e86 100644
--- a/test/base.el
+++ b/test/base.el
@@ -229,10 +229,20 @@ am-pm                     = %S"
             (dotimes (k length)
               (should (stringp (aref value k))))))))))
 
-(ert-deftest datetime--determine-system-timezone ()
+(ert-deftest datetime--determine-system-timezone-1 ()
   (datetime--advised ('current-time-zone :override (lambda () '(7200 "CEST")))
     (let ((system-type 'this-system-type-is-not-specialcase))
       (should (eq (datetime--determine-system-timezone) 'CET)))))
 
+(ert-deftest datetime--determine-system-timezone-2 ()
+  (datetime--advised ('current-time-zone :override (lambda () '(3600 "Central 
European Time")))
+    (let ((system-type 'this-system-type-is-not-specialcase))
+      (should (eq (datetime--determine-system-timezone) 'CET)))))
+
+(ert-deftest datetime--determine-system-timezone-3 ()
+  (datetime--advised ('current-time-zone :override (lambda () '(0 "Coordinated 
Universal Time")))
+    (let ((system-type 'this-system-type-is-not-specialcase))
+      (should (eq (datetime--determine-system-timezone) 'UTC)))))
+
 
 (provide 'test/base)
diff --git a/timezone-data.extmap b/timezone-data.extmap
index fbac550399..2c667f264f 100644
Binary files a/timezone-data.extmap and b/timezone-data.extmap differ

Reply via email to