1 /* 2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package test.java.time.format; 25 26 import static org.testng.Assert.assertEquals; 27 28 import java.text.DateFormatSymbols; 29 import java.time.ZoneId; 30 import java.time.ZonedDateTime; 31 import java.time.format.DecimalStyle; 32 import java.time.format.DateTimeFormatter; 33 import java.time.format.DateTimeFormatterBuilder; 34 import java.time.format.TextStyle; 35 import java.time.temporal.ChronoField; 36 import java.time.temporal.TemporalQueries; 37 import java.time.zone.ZoneRulesProvider; 38 import java.util.Arrays; 39 import java.util.Date; 40 import java.util.HashSet; 41 import java.util.Locale; 42 import java.util.Random; 43 import java.util.Set; 44 import java.util.TimeZone; 45 import jdk.testlibrary.RandomFactory; 46 47 import org.testng.annotations.DataProvider; 48 import org.testng.annotations.Test; 49 import android.icu.impl.ZoneMeta; 50 51 /* 52 * @test 53 * @bug 8081022 54 * @key randomness 55 */ 56 57 /** 58 * Test ZoneTextPrinterParser 59 */ 60 @Test 61 public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { 62 getFormatter(Locale locale, TextStyle style)63 protected static DateTimeFormatter getFormatter(Locale locale, TextStyle style) { 64 return new DateTimeFormatterBuilder().appendZoneText(style) 65 .toFormatter(locale) 66 .withDecimalStyle(DecimalStyle.of(locale)); 67 } 68 test_printText()69 public void test_printText() { 70 Random r = RandomFactory.getRandom(); 71 // Android-changed: only run one iteration. 72 int N = 1; 73 Locale[] locales = Locale.getAvailableLocales(); 74 Set<String> zids = ZoneRulesProvider.getAvailableZoneIds(); 75 ZonedDateTime zdt = ZonedDateTime.now(); 76 77 //System.out.printf("locale==%d, timezone=%d%n", locales.length, zids.size()); 78 while (N-- > 0) { 79 zdt = zdt.withDayOfYear(r.nextInt(365) + 1) 80 .with(ChronoField.SECOND_OF_DAY, r.nextInt(86400)); 81 // Android-changed: loop over locales first to speed up test. TimeZoneNames are cached 82 // per locale, but the cache only holds the most recently used locales. 83 for (Locale locale : locales) { 84 // Android-changed: "ji" isn't correctly aliased to "yi", see http//b/8634320. 85 if (locale.getLanguage().equals("ji")) { 86 continue; 87 } 88 for (String zid : zids) { 89 if (zid.equals("ROC") || zid.startsWith("Etc/GMT")) { 90 continue; // TBD: match jdk behavior? 91 } 92 // Android-changed (http://b/33197219): TimeZone.getDisplayName() for 93 // non-canonical time zones are not correct. 94 if (!zid.equals(ZoneMeta.getCanonicalCLDRID(zid))) { 95 continue; 96 } 97 zdt = zdt.withZoneSameLocal(ZoneId.of(zid)); 98 TimeZone tz = TimeZone.getTimeZone(zid); 99 // Android-changed: We don't have long names for GMT. 100 if (tz.getID().equals("GMT")) { 101 continue; 102 } 103 boolean isDST = tz.inDaylightTime(new Date(zdt.toInstant().toEpochMilli())); 104 printText(locale, zdt, TextStyle.FULL, tz, 105 tz.getDisplayName(isDST, TimeZone.LONG, locale)); 106 printText(locale, zdt, TextStyle.SHORT, tz, 107 tz.getDisplayName(isDST, TimeZone.SHORT, locale)); 108 } 109 } 110 } 111 } 112 printText(Locale locale, ZonedDateTime zdt, TextStyle style, TimeZone zone, String expected)113 private void printText(Locale locale, ZonedDateTime zdt, TextStyle style, TimeZone zone, String expected) { 114 String result = getFormatter(locale, style).format(zdt); 115 // Android-changed: TimeZone.getDisplayName() will never return "GMT". 116 if (result.startsWith("GMT") && expected.equals("GMT+00:00")) { 117 return; 118 } 119 if (!result.equals(expected)) { 120 if (result.equals("FooLocation")) { // from rules provider test if same vm 121 return; 122 } 123 System.out.println("----------------"); 124 System.out.printf("tdz[%s]%n", zdt.toString()); 125 System.out.printf("[%-5s, %5s] :[%s]%n", locale.toString(), style.toString(),result); 126 System.out.printf(" %5s, %5s :[%s] %s%n", "", "", expected, zone); 127 } 128 assertEquals(result, expected); 129 } 130 131 // Android-changed: disable test as it doesn't assert anything and produces a lot of output. 132 @Test(enabled = false) test_ParseText()133 public void test_ParseText() { 134 Locale[] locales = new Locale[] { Locale.ENGLISH, Locale.JAPANESE, Locale.FRENCH }; 135 Set<String> zids = ZoneRulesProvider.getAvailableZoneIds(); 136 for (Locale locale : locales) { 137 parseText(zids, locale, TextStyle.FULL, false); 138 parseText(zids, locale, TextStyle.FULL, true); 139 parseText(zids, locale, TextStyle.SHORT, false); 140 parseText(zids, locale, TextStyle.SHORT, true); 141 } 142 } 143 144 private static Set<ZoneId> preferred = new HashSet<>(Arrays.asList(new ZoneId[] { 145 ZoneId.of("EST", ZoneId.SHORT_IDS), 146 ZoneId.of("Asia/Taipei"), 147 ZoneId.of("CET"), 148 })); 149 150 private static Set<ZoneId> preferred_s = new HashSet<>(Arrays.asList(new ZoneId[] { 151 ZoneId.of("EST", ZoneId.SHORT_IDS), 152 ZoneId.of("CET"), 153 ZoneId.of("Australia/South"), 154 ZoneId.of("Australia/West"), 155 ZoneId.of("Asia/Shanghai"), 156 })); 157 158 private static Set<ZoneId> none = new HashSet<>(); 159 160 @DataProvider(name="preferredZones") data_preferredZones()161 Object[][] data_preferredZones() { 162 // Android-changed: Differences in time zone name handling. 163 // Android and java.time (via the RI) have differences in how they handle Time Zone Names. 164 // - Android doesn't use IANA abbreviates (usually 3-letter abbreviations) except where they 165 // are widely used in a given locale (so CST will not resolve to "Chinese Standard Time"). 166 // - Android doesn't provide long names for zones like "CET". Only the Olson IDs like 167 // "Europe/London" have names attached to them. 168 // - When no preferred zones are provided then no guarantee is made about the specific zone 169 // returned. 170 // - Android uses the display name "Taipei Standard Time" as CLDR does. 171 // Basically Android time zone parsing sticks strictly to what can be done with the data 172 // provided by IANA and CLDR and avoids introducing additional values (like specific order 173 // and additional names) to those. 174 return new Object[][] { 175 // {"America/New_York", "Eastern Standard Time", none, Locale.ENGLISH, TextStyle.FULL}, 176 // {"EST", "Eastern Standard Time", preferred, Locale.ENGLISH, TextStyle.FULL}, 177 // {"Europe/Paris", "Central European Time", none, Locale.ENGLISH, TextStyle.FULL}, 178 // {"CET", "Central European Time", preferred, Locale.UK, TextStyle.FULL}, 179 // {"Asia/Shanghai", "China Standard Time", none, Locale.ENGLISH, TextStyle.FULL}, 180 // {"Asia/Taipei", "China Standard Time", preferred, Locale.ENGLISH, TextStyle.FULL}, 181 // {"America/Chicago", "CST", none, Locale.ENGLISH, TextStyle.SHORT}, 182 // {"Asia/Taipei", "CST", preferred, Locale.ENGLISH, TextStyle.SHORT}, 183 // Australia/South is a valid synonym for Australia/Adelaide, so this test will pass. 184 {"Australia/South", "ACST", preferred_s, new Locale("en", "AU"), TextStyle.SHORT}, 185 // {"America/Chicago", "CDT", none, Locale.ENGLISH, TextStyle.SHORT}, 186 // {"Asia/Shanghai", "CDT", preferred_s, Locale.ENGLISH, TextStyle.SHORT}, 187 }; 188 } 189 190 @Test(dataProvider="preferredZones") test_ParseText(String expected, String text, Set<ZoneId> preferred, Locale locale, TextStyle style)191 public void test_ParseText(String expected, String text, Set<ZoneId> preferred, Locale locale, TextStyle style) { 192 DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendZoneText(style, preferred) 193 .toFormatter(locale) 194 .withDecimalStyle(DecimalStyle.of(locale)); 195 196 String ret = fmt.parse(text, TemporalQueries.zone()).getId(); 197 198 System.out.printf("[%-5s %s] %24s -> %s(%s)%n", 199 locale.toString(), 200 style == TextStyle.FULL ? " full" :"short", 201 text, ret, expected); 202 203 assertEquals(ret, expected); 204 205 } 206 207 parseText(Set<String> zids, Locale locale, TextStyle style, boolean ci)208 private void parseText(Set<String> zids, Locale locale, TextStyle style, boolean ci) { 209 System.out.println("---------------------------------------"); 210 DateTimeFormatter fmt = getFormatter(locale, style, ci); 211 for (String[] names : new DateFormatSymbols(locale).getZoneStrings()) { 212 if (!zids.contains(names[0])) { 213 continue; 214 } 215 String zid = names[0]; 216 String expected = ZoneName.toZid(zid, locale); 217 218 parse(fmt, zid, expected, zid, locale, style, ci); 219 int i = style == TextStyle.FULL ? 1 : 2; 220 for (; i < names.length; i += 2) { 221 parse(fmt, zid, expected, names[i], locale, style, ci); 222 } 223 } 224 } 225 parse(DateTimeFormatter fmt, String zid, String expected, String text, Locale locale, TextStyle style, boolean ci)226 private void parse(DateTimeFormatter fmt, 227 String zid, String expected, String text, 228 Locale locale, TextStyle style, boolean ci) { 229 if (ci) { 230 text = text.toUpperCase(); 231 } 232 String ret = fmt.parse(text, TemporalQueries.zone()).getId(); 233 // TBD: need an excluding list 234 // assertEquals(...); 235 if (ret.equals(expected) || 236 ret.equals(zid) || 237 ret.equals(ZoneName.toZid(zid)) || 238 ret.equals(expected.replace("UTC", "UCT"))) { 239 return; 240 } 241 System.out.printf("[%-5s %s %s %16s] %24s -> %s(%s)%n", 242 locale.toString(), 243 ci ? "ci" : " ", 244 style == TextStyle.FULL ? " full" :"short", 245 zid, text, ret, expected); 246 } 247 getFormatter(Locale locale, TextStyle style, boolean ci)248 private DateTimeFormatter getFormatter(Locale locale, TextStyle style, boolean ci) { 249 DateTimeFormatterBuilder db = new DateTimeFormatterBuilder(); 250 if (ci) { 251 db = db.parseCaseInsensitive(); 252 } 253 return db.appendZoneText(style) 254 .toFormatter(locale) 255 .withDecimalStyle(DecimalStyle.of(locale)); 256 } 257 258 } 259