8235238: Parsing a time string ignores any custom TimeZoneNameProvider
Reviewed-by: joehw, rriggs
This commit is contained in:
parent
57ece4c21a
commit
20b1410d0c
@ -4123,7 +4123,8 @@ public final class DateTimeFormatterBuilder {
|
||||
}
|
||||
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 @@ public final class DateTimeFormatterBuilder {
|
||||
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 @@ public final class DateTimeFormatterBuilder {
|
||||
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;
|
||||
|
@ -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());
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
custom.CustomZoneRulesProvider
|
@ -0,0 +1 @@
|
||||
custom.CustomTimeZoneNameProvider
|
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user