• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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