| /* |
| * Copyright (c) 2012, 2015, 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 test.java.time.format; |
| |
| import static org.testng.Assert.assertEquals; |
| |
| import java.text.DateFormatSymbols; |
| import java.time.ZoneId; |
| import java.time.ZonedDateTime; |
| import java.time.format.DecimalStyle; |
| import java.time.format.DateTimeFormatter; |
| import java.time.format.DateTimeFormatterBuilder; |
| import java.time.format.TextStyle; |
| import java.time.temporal.ChronoField; |
| import java.time.temporal.TemporalQueries; |
| import java.time.zone.ZoneRulesProvider; |
| import java.util.Arrays; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.Locale; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.TimeZone; |
| import jdk.testlibrary.RandomFactory; |
| |
| import org.testng.annotations.DataProvider; |
| import org.testng.annotations.Test; |
| |
| /* |
| * @test |
| * @bug 8081022 |
| * @key randomness |
| */ |
| |
| /** |
| * Test ZoneTextPrinterParser |
| */ |
| @Test |
| public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { |
| |
| protected static DateTimeFormatter getFormatter(Locale locale, TextStyle style) { |
| return new DateTimeFormatterBuilder().appendZoneText(style) |
| .toFormatter(locale) |
| .withDecimalStyle(DecimalStyle.of(locale)); |
| } |
| |
| public void test_printText() { |
| Random r = RandomFactory.getRandom(); |
| // Android-changed: only run one iteration. |
| int N = 1; |
| Locale[] locales = Locale.getAvailableLocales(); |
| Set<String> zids = ZoneRulesProvider.getAvailableZoneIds(); |
| ZonedDateTime zdt = ZonedDateTime.now(); |
| |
| //System.out.printf("locale==%d, timezone=%d%n", locales.length, zids.size()); |
| while (N-- > 0) { |
| zdt = zdt.withDayOfYear(r.nextInt(365) + 1) |
| .with(ChronoField.SECOND_OF_DAY, r.nextInt(86400)); |
| // Android-changed: loop over locales first to speed up test. TimeZoneNames are cached |
| // per locale, but the cache only holds the most recently used locales. |
| for (Locale locale : locales) { |
| // Android-changed: "ji" isn't correctly aliased to "yi", see http//b/8634320. |
| if (locale.getLanguage().equals("ji")) { |
| continue; |
| } |
| for (String zid : zids) { |
| if (zid.equals("ROC") || zid.startsWith("Etc/GMT")) { |
| continue; // TBD: match jdk behavior? |
| } |
| // Android-changed (http://b/33197219): TimeZone.getDisplayName() for |
| // non-canonical time zones are not correct. |
| if (!zid.equals(getSystemCanonicalID(zid))) { |
| continue; |
| } |
| zdt = zdt.withZoneSameLocal(ZoneId.of(zid)); |
| TimeZone tz = TimeZone.getTimeZone(zid); |
| // Android-changed: We don't have long names for GMT. |
| if (tz.getID().equals("GMT")) { |
| continue; |
| } |
| boolean isDST = tz.inDaylightTime(new Date(zdt.toInstant().toEpochMilli())); |
| printText(locale, zdt, TextStyle.FULL, tz, |
| tz.getDisplayName(isDST, TimeZone.LONG, locale)); |
| printText(locale, zdt, TextStyle.SHORT, tz, |
| tz.getDisplayName(isDST, TimeZone.SHORT, locale)); |
| } |
| } |
| } |
| } |
| |
| // BEGIN Android-added: Get non-custom system canonical time zone Id from ICU. |
| private static String getSystemCanonicalID(String zid) { |
| if (android.icu.util.TimeZone.UNKNOWN_ZONE_ID.equals(zid)) { |
| return zid; |
| } |
| boolean[] isSystemID = { false }; |
| String canonicalID = android.icu.util.TimeZone.getCanonicalID(zid, isSystemID); |
| if (canonicalID == null || !isSystemID[0]) { |
| return null; |
| } |
| return canonicalID; |
| } |
| // END Android-added: Get non-custom system canonical time zone Id from ICU. |
| |
| private void printText(Locale locale, ZonedDateTime zdt, TextStyle style, TimeZone zone, String expected) { |
| String result = getFormatter(locale, style).format(zdt); |
| // Android-changed: TimeZone.getDisplayName() will never return "GMT". |
| if (result.startsWith("GMT") && expected.equals("GMT+00:00")) { |
| return; |
| } |
| if (!result.equals(expected)) { |
| if (result.equals("FooLocation")) { // from rules provider test if same vm |
| return; |
| } |
| System.out.println("----------------"); |
| System.out.printf("tdz[%s]%n", zdt.toString()); |
| System.out.printf("[%-5s, %5s] :[%s]%n", locale.toString(), style.toString(),result); |
| System.out.printf(" %5s, %5s :[%s] %s%n", "", "", expected, zone); |
| } |
| assertEquals(result, expected); |
| } |
| |
| // Android-changed: disable test as it doesn't assert anything and produces a lot of output. |
| @Test(enabled = false) |
| public void test_ParseText() { |
| Locale[] locales = new Locale[] { Locale.ENGLISH, Locale.JAPANESE, Locale.FRENCH }; |
| Set<String> zids = ZoneRulesProvider.getAvailableZoneIds(); |
| for (Locale locale : locales) { |
| parseText(zids, locale, TextStyle.FULL, false); |
| parseText(zids, locale, TextStyle.FULL, true); |
| parseText(zids, locale, TextStyle.SHORT, false); |
| parseText(zids, locale, TextStyle.SHORT, true); |
| } |
| } |
| |
| private static Set<ZoneId> preferred = new HashSet<>(Arrays.asList(new ZoneId[] { |
| ZoneId.of("EST", ZoneId.SHORT_IDS), |
| ZoneId.of("Asia/Taipei"), |
| ZoneId.of("CET"), |
| })); |
| |
| private static Set<ZoneId> preferred_s = new HashSet<>(Arrays.asList(new ZoneId[] { |
| ZoneId.of("EST", ZoneId.SHORT_IDS), |
| ZoneId.of("CET"), |
| ZoneId.of("Australia/South"), |
| ZoneId.of("Australia/West"), |
| ZoneId.of("Asia/Shanghai"), |
| })); |
| |
| private static Set<ZoneId> none = new HashSet<>(); |
| |
| @DataProvider(name="preferredZones") |
| Object[][] data_preferredZones() { |
| // Android-changed: Differences in time zone name handling. |
| // Android and java.time (via the RI) have differences in how they handle Time Zone Names. |
| // - Android doesn't use IANA abbreviates (usually 3-letter abbreviations) except where they |
| // are widely used in a given locale (so CST will not resolve to "Chinese Standard Time"). |
| // - Android doesn't provide long names for zones like "CET". Only the Olson IDs like |
| // "Europe/London" have names attached to them. |
| // - When no preferred zones are provided then no guarantee is made about the specific zone |
| // returned. |
| // - Android uses the display name "Taipei Standard Time" as CLDR does. |
| // Basically Android time zone parsing sticks strictly to what can be done with the data |
| // provided by IANA and CLDR and avoids introducing additional values (like specific order |
| // and additional names) to those. |
| return new Object[][] { |
| // {"America/New_York", "Eastern Standard Time", none, Locale.ENGLISH, TextStyle.FULL}, |
| // {"EST", "Eastern Standard Time", preferred, Locale.ENGLISH, TextStyle.FULL}, |
| // {"Europe/Paris", "Central European Time", none, Locale.ENGLISH, TextStyle.FULL}, |
| // {"CET", "Central European Time", preferred, Locale.UK, TextStyle.FULL}, |
| // {"Asia/Shanghai", "China Standard Time", none, Locale.ENGLISH, TextStyle.FULL}, |
| // {"Asia/Taipei", "China Standard Time", preferred, Locale.ENGLISH, TextStyle.FULL}, |
| // {"America/Chicago", "CST", none, Locale.ENGLISH, TextStyle.SHORT}, |
| // {"Asia/Taipei", "CST", preferred, Locale.ENGLISH, TextStyle.SHORT}, |
| // Australia/South is a valid synonym for Australia/Adelaide, so this test will pass. |
| {"Australia/South", "ACST", preferred_s, new Locale("en", "AU"), TextStyle.SHORT}, |
| // {"America/Chicago", "CDT", none, Locale.ENGLISH, TextStyle.SHORT}, |
| // {"Asia/Shanghai", "CDT", preferred_s, Locale.ENGLISH, TextStyle.SHORT}, |
| }; |
| } |
| |
| @Test(dataProvider="preferredZones") |
| public void test_ParseText(String expected, String text, Set<ZoneId> preferred, Locale locale, TextStyle style) { |
| DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendZoneText(style, preferred) |
| .toFormatter(locale) |
| .withDecimalStyle(DecimalStyle.of(locale)); |
| |
| String ret = fmt.parse(text, TemporalQueries.zone()).getId(); |
| |
| System.out.printf("[%-5s %s] %24s -> %s(%s)%n", |
| locale.toString(), |
| style == TextStyle.FULL ? " full" :"short", |
| text, ret, expected); |
| |
| assertEquals(ret, expected); |
| |
| } |
| |
| |
| private void parseText(Set<String> zids, Locale locale, TextStyle style, boolean ci) { |
| System.out.println("---------------------------------------"); |
| DateTimeFormatter fmt = getFormatter(locale, style, ci); |
| for (String[] names : new DateFormatSymbols(locale).getZoneStrings()) { |
| if (!zids.contains(names[0])) { |
| continue; |
| } |
| String zid = names[0]; |
| String expected = ZoneName.toZid(zid, locale); |
| |
| parse(fmt, zid, expected, zid, locale, style, ci); |
| int i = style == TextStyle.FULL ? 1 : 2; |
| for (; i < names.length; i += 2) { |
| parse(fmt, zid, expected, names[i], locale, style, ci); |
| } |
| } |
| } |
| |
| private void parse(DateTimeFormatter fmt, |
| String zid, String expected, String text, |
| Locale locale, TextStyle style, boolean ci) { |
| if (ci) { |
| text = text.toUpperCase(); |
| } |
| String ret = fmt.parse(text, TemporalQueries.zone()).getId(); |
| // TBD: need an excluding list |
| // assertEquals(...); |
| if (ret.equals(expected) || |
| ret.equals(zid) || |
| ret.equals(ZoneName.toZid(zid)) || |
| ret.equals(expected.replace("UTC", "UCT"))) { |
| return; |
| } |
| System.out.printf("[%-5s %s %s %16s] %24s -> %s(%s)%n", |
| locale.toString(), |
| ci ? "ci" : " ", |
| style == TextStyle.FULL ? " full" :"short", |
| zid, text, ret, expected); |
| } |
| |
| private DateTimeFormatter getFormatter(Locale locale, TextStyle style, boolean ci) { |
| DateTimeFormatterBuilder db = new DateTimeFormatterBuilder(); |
| if (ci) { |
| db = db.parseCaseInsensitive(); |
| } |
| return db.appendZoneText(style) |
| .toFormatter(locale) |
| .withDecimalStyle(DecimalStyle.of(locale)); |
| } |
| |
| } |