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 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 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 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) 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") 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") 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 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 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 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