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