changeset 57431:ac372709206e

8235238: Parsing a time string ignores any custom TimeZoneNameProvider Reviewed-by: joehw, rriggs
author naoto
date Fri, 13 Dec 2019 08:17:28 -0800
parents 4a631f90648a
children c97cf6fefbc1
files src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java test/jdk/java/time/nontestng/java/time/zone/CustomZoneNameTest.java test/jdk/java/time/nontestng/java/time/zone/zoneProvider/META-INF/services/java.time.zone.ZoneRulesProvider test/jdk/java/time/nontestng/java/time/zone/zoneProvider/META-INF/services/java.util.spi.TimeZoneNameProvider test/jdk/java/time/nontestng/java/time/zone/zoneProvider/custom/CustomTimeZoneNameProvider.java test/jdk/java/time/nontestng/java/time/zone/zoneProvider/custom/CustomZoneRulesProvider.java
diffstat 6 files changed, 248 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java	Fri Dec 13 14:28:22 2019 +0000
+++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java	Fri Dec 13 08:17:28 2019 -0800
@@ -4123,7 +4123,8 @@
             }
             Locale locale = context.getLocale();
             boolean isCaseSensitive = context.isCaseSensitive();
-            Set<String> regionIds = ZoneRulesProvider.getAvailableZoneIds();
+            Set<String> regionIds = new HashSet<>(ZoneRulesProvider.getAvailableZoneIds());
+            Set<String> nonRegionIds = new HashSet<>(64);
             int regionIdsSize = regionIds.size();
 
             Map<Locale, Entry<Integer, SoftReference<PrefixTree>>> cached =
@@ -4139,7 +4140,8 @@
                 zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
                 for (String[] names : zoneStrings) {
                     String zid = names[0];
-                    if (!regionIds.contains(zid)) {
+                    if (!regionIds.remove(zid)) {
+                        nonRegionIds.add(zid);
                         continue;
                     }
                     tree.add(zid, zid);    // don't convert zid -> metazone
@@ -4149,12 +4151,27 @@
                         tree.add(names[i], zid);
                     }
                 }
+
+                // add names for provider's custom ids
+                final PrefixTree t = tree;
+                regionIds.stream()
+                    .filter(zid -> !zid.startsWith("Etc") && !zid.startsWith("GMT"))
+                    .forEach(cid -> {
+                        String[] cidNames = TimeZoneNameUtility.retrieveDisplayNames(cid, locale);
+                        int i = textStyle == TextStyle.FULL ? 1 : 2;
+                        for (; i < cidNames.length; i += 2) {
+                            if (cidNames[i] != null && !cidNames[i].isEmpty()) {
+                                t.add(cidNames[i], cid);
+                            }
+                        }
+                    });
+
                 // if we have a set of preferred zones, need a copy and
                 // add the preferred zones again to overwrite
                 if (preferredZones != null) {
                     for (String[] names : zoneStrings) {
                         String zid = names[0];
-                        if (!preferredZones.contains(zid) || !regionIds.contains(zid)) {
+                        if (!preferredZones.contains(zid) || nonRegionIds.contains(zid)) {
                             continue;
                         }
                         int i = textStyle == TextStyle.FULL ? 1 : 2;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/time/nontestng/java/time/zone/CustomZoneNameTest.java	Fri Dec 13 08:17:28 2019 -0800
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+
+/* @test
+ * @bug 8235238
+ * @summary Checks whether custom zone names can be formatted/parsed correctly.
+ * @library zoneProvider
+ * @build custom.CustomZoneRulesProvider custom.CustomTimeZoneNameProvider
+ * @run main/othervm -Djava.locale.providers=SPI,CLDR CustomZoneNameTest
+ */
+public class CustomZoneNameTest {
+
+    private final static long now = 1575669972372L;
+    private final static Instant instant = Instant.ofEpochMilli(now);
+    private final static ZoneId customZone = ZoneId.of("Custom/Timezone");
+
+    // test data
+    private final static Map<String, String>  formats = Map.of(
+        "yyyy-MM-dd HH:mm:ss.SSS VV", "2019-12-06 22:06:12.372 Custom/Timezone",
+        "yyyy-MM-dd HH:mm:ss.SSS z", "2019-12-06 22:06:12.372 CUST_WT",
+        "yyyy-MM-dd HH:mm:ss.SSS zzzz", "2019-12-06 22:06:12.372 Custom Winter Time",
+        "yyyy-MM-dd HH:mm:ss.SSS v", "2019-12-06 22:06:12.372 Custom Time",
+        "yyyy-MM-dd HH:mm:ss.SSS vvvv", "2019-12-06 22:06:12.372 Custom Timezone Time"
+    );
+
+    public static void main(String... args) {
+        testFormatting();
+        testParsing();
+    }
+
+    private static void testFormatting() {
+        var customZDT = ZonedDateTime.ofInstant(instant, customZone);
+        formats.entrySet().stream()
+            .filter(e -> {
+                var formatted = DateTimeFormatter.ofPattern(e.getKey()).format(customZDT);
+                var expected = e.getValue();
+                System.out.println("testFormatting. Pattern: " + e.getKey() +
+                        ", expected: " + expected +
+                        ", formatted: " + formatted);
+                return !formatted.equals(expected);
+            })
+            .findAny()
+            .ifPresent(e -> {
+                throw new RuntimeException(
+                        "Provider's custom name was not retrieved for the format " +
+                        e.getKey());
+            });
+    }
+
+    public static void testParsing() {
+        formats.entrySet().stream()
+            .filter(e -> {
+                var fmt = DateTimeFormatter.ofPattern(e.getKey());
+                var input = e.getValue();
+                var parsedInstant = fmt.parse(input, Instant::from).toEpochMilli();
+                var parsedZone = fmt.parse(input, ZonedDateTime::from).getZone();
+                System.out.println("testParsing. Input: " + input +
+                        ", expected instant: " + now +
+                        ", expected zone: " + customZone +
+                        ", parsed instant: " + parsedInstant +
+                        ", parsed zone: " + parsedZone);
+                return parsedInstant != now ||
+                        !parsedZone.equals(customZone);
+            })
+            .findAny()
+            .ifPresent(e -> {
+                throw new RuntimeException("Parsing failed for the format " +
+                                e.getKey());
+            });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/time/nontestng/java/time/zone/zoneProvider/META-INF/services/java.time.zone.ZoneRulesProvider	Fri Dec 13 08:17:28 2019 -0800
@@ -0,0 +1,1 @@
+custom.CustomZoneRulesProvider
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/time/nontestng/java/time/zone/zoneProvider/META-INF/services/java.util.spi.TimeZoneNameProvider	Fri Dec 13 08:17:28 2019 -0800
@@ -0,0 +1,1 @@
+custom.CustomTimeZoneNameProvider
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/time/nontestng/java/time/zone/zoneProvider/custom/CustomTimeZoneNameProvider.java	Fri Dec 13 08:17:28 2019 -0800
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package custom;
+
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.spi.TimeZoneNameProvider;
+
+public class CustomTimeZoneNameProvider extends TimeZoneNameProvider {
+
+    public static final String ZONE_ID = "Custom/Timezone";
+
+    @Override
+    public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
+        if (ZONE_ID.equals(ID)) {
+            switch (style) {
+                case TimeZone.SHORT:
+                    if (daylight) {
+                        return "CUST_ST";
+                    } else {
+                        return "CUST_WT";
+                    }
+                case TimeZone.LONG:
+                    if (daylight) {
+                        return "Custom Summer Time";
+                    } else {
+                        return "Custom Winter Time";
+                    }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getGenericDisplayName(String ID, int style, Locale locale) {
+        if (ZONE_ID.equals(ID)) {
+            switch (style) {
+                case TimeZone.SHORT:
+                    return "Custom Time";
+                case TimeZone.LONG:
+                    return "Custom Timezone Time";
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isSupportedLocale(Locale locale) {
+        return true;
+    }
+
+    @Override
+    public Locale[] getAvailableLocales() {
+        return new Locale[]{
+            Locale.getDefault()
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/time/nontestng/java/time/zone/zoneProvider/custom/CustomZoneRulesProvider.java	Fri Dec 13 08:17:28 2019 -0800
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package custom;
+
+import java.time.ZoneId;
+import java.time.zone.ZoneRules;
+import java.time.zone.ZoneRulesProvider;
+import java.util.Set;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+public class CustomZoneRulesProvider extends ZoneRulesProvider {
+    @Override
+    protected Set<String> provideZoneIds() {
+        return Set.of("Custom/Timezone");
+    }
+
+    @Override
+    protected ZoneRules provideRules(String zoneId, boolean forCaching) {
+        return ZoneId.of("UTC").getRules();
+    }
+
+    @Override
+    protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
+        var map = new TreeMap<String, ZoneRules>();
+        map.put("bogusVersion", getRules(zoneId, false));
+        return map;
+    }
+}