branch: elpa/datetime
commit 8a1d0ba701ffb9a00ed91ff3e1a6ae749dd506c2
Author: Paul Pogonyshev <[email protected]>
Commit: Paul Pogonyshev <[email protected]>
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