]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/tests/core/src/com/ibm/icu/dev/test/format/TimeZoneFormatTest.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / tests / core / src / com / ibm / icu / dev / test / format / TimeZoneFormatTest.java
1 /*\r
2  ********************************************************************************\r
3  * Copyright (C) 2007-2010, Google, International Business Machines Corporation *\r
4  * and others. All Rights Reserved.                                             *\r
5  ********************************************************************************\r
6  */\r
7 \r
8 package com.ibm.icu.dev.test.format;\r
9 \r
10 import java.text.ParseException;\r
11 import java.text.ParsePosition;\r
12 import java.util.Date;\r
13 \r
14 import com.ibm.icu.lang.UCharacter;\r
15 import com.ibm.icu.text.SimpleDateFormat;\r
16 import com.ibm.icu.util.BasicTimeZone;\r
17 import com.ibm.icu.util.Calendar;\r
18 import com.ibm.icu.util.SimpleTimeZone;\r
19 import com.ibm.icu.util.TimeZone;\r
20 import com.ibm.icu.util.TimeZoneTransition;\r
21 import com.ibm.icu.util.ULocale;\r
22 \r
23 public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {\r
24 \r
25     public static void main(String[] args) throws Exception {\r
26         new TimeZoneFormatTest().run(args);\r
27     }\r
28 \r
29     private static final String[] PATTERNS = {"z", "zzzz", "Z", "ZZZZ", "v", "vvvv", "V", "VVVV"};\r
30 \r
31     /*\r
32      * Test case for checking if a TimeZone is properly set in the result calendar\r
33      * and if the result TimeZone has the expected behavior.\r
34      */\r
35     public void TestTimeZoneRoundTrip() {\r
36         boolean TEST_ALL = "true".equalsIgnoreCase(getProperty("TimeZoneRoundTripAll"));\r
37 \r
38         TimeZone unknownZone = new SimpleTimeZone(-31415, "Etc/Unknown");\r
39         int badDstOffset = -1234;\r
40         int badZoneOffset = -2345;\r
41 \r
42         int[][] testDateData = {\r
43             {2007, 1, 15},\r
44             {2007, 6, 15},\r
45             {1990, 1, 15},\r
46             {1990, 6, 15},\r
47             {1960, 1, 15},\r
48             {1960, 6, 15},\r
49         };\r
50 \r
51         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));\r
52         cal.clear();\r
53 \r
54         // Set up rule equivalency test range\r
55         long low, high;\r
56         cal.set(1900, 0, 1);\r
57         low = cal.getTimeInMillis();\r
58         cal.set(2040, 0, 1);\r
59         high = cal.getTimeInMillis();\r
60 \r
61         // Set up test dates\r
62         Date[] DATES = new Date[testDateData.length];\r
63         cal.clear();\r
64         for (int i = 0; i < DATES.length; i++) {\r
65             cal.set(testDateData[i][0], testDateData[i][1], testDateData[i][2]);\r
66             DATES[i] = cal.getTime();\r
67         }\r
68 \r
69         // Set up test locales\r
70         ULocale[] LOCALES = null;\r
71         if (TEST_ALL || getInclusion() > 5) {\r
72             LOCALES = ULocale.getAvailableLocales();\r
73         } else {\r
74             LOCALES = new ULocale[] {new ULocale("en"), new ULocale("en_CA"), new ULocale("fr"), new ULocale("zh_Hant")};\r
75         }\r
76 \r
77         String[] tzids = TimeZone.getAvailableIDs();\r
78         int[] inOffsets = new int[2];\r
79         int[] outOffsets = new int[2];\r
80 \r
81         // Run the roundtrip test\r
82         for (int locidx = 0; locidx < LOCALES.length; locidx++) {\r
83             for (int patidx = 0; patidx < PATTERNS.length; patidx++) {\r
84                 SimpleDateFormat sdf = new SimpleDateFormat(PATTERNS[patidx], LOCALES[locidx]);\r
85 \r
86                 for (int tzidx = 0; tzidx < tzids.length; tzidx++) {\r
87                     TimeZone tz = TimeZone.getTimeZone(tzids[tzidx]);\r
88 \r
89                     for (int datidx = 0; datidx < DATES.length; datidx++) {\r
90                         // Format\r
91                         sdf.setTimeZone(tz);\r
92                         String tzstr = sdf.format(DATES[datidx]);\r
93 \r
94                         // Before parse, set unknown zone to SimpleDateFormat instance\r
95                         // just for making sure that it does not depends on the time zone\r
96                         // originally set.\r
97                         sdf.setTimeZone(unknownZone);\r
98 \r
99                         // Parse\r
100                         ParsePosition pos = new ParsePosition(0);\r
101                         Calendar outcal = Calendar.getInstance(unknownZone);\r
102                         outcal.set(Calendar.DST_OFFSET, badDstOffset);\r
103                         outcal.set(Calendar.ZONE_OFFSET, badZoneOffset);\r
104 \r
105                         sdf.parse(tzstr, outcal, pos);\r
106 \r
107                         // Check the result\r
108                         TimeZone outtz = outcal.getTimeZone();\r
109 \r
110                         tz.getOffset(DATES[datidx].getTime(), false, inOffsets);\r
111                         outtz.getOffset(DATES[datidx].getTime(), false, outOffsets);\r
112 \r
113                         if (PATTERNS[patidx].equals("VVVV")) {\r
114                             // Location: time zone rule must be preserved except\r
115                             // zones not actually associated with a specific location.\r
116                             // Time zones in this category do not have "/" in its ID.\r
117                             String canonicalID = TimeZone.getCanonicalID(tzids[tzidx]);\r
118                             if (canonicalID != null && !outtz.getID().equals(canonicalID)) {\r
119                                 // Canonical ID did not match - check the rules\r
120                                 boolean bFailure = false;\r
121                                 if ((tz instanceof BasicTimeZone) && (outtz instanceof BasicTimeZone)) {\r
122                                     bFailure = !(canonicalID.indexOf('/') == -1)\r
123                                                 && !((BasicTimeZone)outtz).hasEquivalentTransitions(tz, low, high);\r
124                                 }\r
125                                 if (bFailure) {\r
126                                     errln("Canonical round trip failed; tz=" + tzids[tzidx]\r
127                                             + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]\r
128                                             + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr\r
129                                             + ", outtz=" + outtz.getID());\r
130                                 } else {\r
131                                     logln("Canonical round trip failed (as expected); tz=" + tzids[tzidx]\r
132                                             + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]\r
133                                             + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr\r
134                                             + ", outtz=" + outtz.getID());\r
135                                 }\r
136                             }\r
137                         } else {\r
138                             // Check if localized GMT format or RFC format is used.\r
139                             int numDigits = 0;\r
140                             for (int n = 0; n < tzstr.length(); n++) {\r
141                                 if (UCharacter.isDigit(tzstr.charAt(n))) {\r
142                                     numDigits++;\r
143                                 }\r
144                             }\r
145 \r
146                             if (numDigits >= 3) {\r
147                                 // Localized GMT or RFC: total offset (raw + dst) must be preserved.\r
148                                 int inOffset = inOffsets[0] + inOffsets[1];\r
149                                 int outOffset = outOffsets[0] + outOffsets[1];\r
150                                 if (inOffset != outOffset) {\r
151                                     errln("Offset round trip failed; tz=" + tzids[tzidx]\r
152                                         + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]\r
153                                         + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr\r
154                                         + ", inOffset=" + inOffset + ", outOffset=" + outOffset);\r
155                                 }\r
156                             } else {\r
157                                 // Specific or generic: raw offset must be preserved.\r
158                                 if (inOffsets[0] != outOffsets[0]) {\r
159                                     if (TimeZone.getDefaultTimeZoneType() == TimeZone.TIMEZONE_JDK\r
160                                             && tzids[tzidx].startsWith("SystemV/")) {\r
161                                         // JDK uses rule SystemV for these zones while\r
162                                         // ICU handles these zones as aliases of existing time zones\r
163                                         logln("Raw offset round trip failed; tz=" + tzids[tzidx]\r
164                                             + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]\r
165                                             + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr\r
166                                             + ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]);\r
167 \r
168                                     } else {\r
169                                         errln("Raw offset round trip failed; tz=" + tzids[tzidx]\r
170                                             + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]\r
171                                             + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr\r
172                                             + ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]);\r
173                                     }\r
174                                 }\r
175                             }\r
176                         }\r
177                     }\r
178                 }\r
179             }\r
180         }\r
181 \r
182     }\r
183 \r
184     /*\r
185      * Test case of round trip time and text.  This test case detects every canonical TimeZone's\r
186      * rule transition since 1900 until 2020, then check if time around each transition can\r
187      * round trip as expected.\r
188      */\r
189     public void TestTimeRoundTrip() {\r
190 \r
191         boolean TEST_ALL = "true".equalsIgnoreCase(getProperty("TimeZoneRoundTripAll"));\r
192 \r
193         int startYear, endYear;\r
194 \r
195         if (TEST_ALL || getInclusion() > 5) {\r
196             startYear = 1900;\r
197         } else {\r
198             startYear = 1990;\r
199         }\r
200 \r
201         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));\r
202         endYear = cal.get(Calendar.YEAR) + 3;\r
203 \r
204         cal.set(startYear, Calendar.JANUARY, 1);\r
205         final long START_TIME = cal.getTimeInMillis();\r
206 \r
207         cal.set(endYear, Calendar.JANUARY, 1);\r
208         final long END_TIME = cal.getTimeInMillis();\r
209 \r
210         // Whether each pattern is ambiguous at DST->STD local time overlap\r
211         final boolean[] AMBIGUOUS_DST_DECESSION = {false, false, false, false, true, true, false, true};\r
212         // Whether each pattern is ambiguous at STD->STD/DST->DST local time overlap\r
213         final boolean[] AMBIGUOUS_NEGATIVE_SHIFT = {true, true, false, false, true, true, true, true};\r
214 \r
215         final String BASEPATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";\r
216 \r
217         ULocale[] LOCALES = null;\r
218         boolean REALLY_VERBOSE = false;\r
219 \r
220         // timer for performance analysis\r
221         long[] times = new long[PATTERNS.length];\r
222         long timer;\r
223 \r
224         if (TEST_ALL) {\r
225             // It may take about an hour for testing all locales\r
226             LOCALES = ULocale.getAvailableLocales();\r
227         } else if (getInclusion() > 5) {\r
228             LOCALES = new ULocale[] {\r
229                 new ULocale("ar_EG"), new ULocale("bg_BG"), new ULocale("ca_ES"), new ULocale("da_DK"), new ULocale("de"),\r
230                 new ULocale("de_DE"), new ULocale("el_GR"), new ULocale("en"), new ULocale("en_AU"), new ULocale("en_CA"),\r
231                 new ULocale("en_US"), new ULocale("es"), new ULocale("es_ES"), new ULocale("es_MX"), new ULocale("fi_FI"),\r
232                 new ULocale("fr"), new ULocale("fr_CA"), new ULocale("fr_FR"), new ULocale("he_IL"), new ULocale("hu_HU"),\r
233                 new ULocale("it"), new ULocale("it_IT"), new ULocale("ja"), new ULocale("ja_JP"), new ULocale("ko"),\r
234                 new ULocale("ko_KR"), new ULocale("nb_NO"), new ULocale("nl_NL"), new ULocale("nn_NO"), new ULocale("pl_PL"),\r
235                 new ULocale("pt"), new ULocale("pt_BR"), new ULocale("pt_PT"), new ULocale("ru_RU"), new ULocale("sv_SE"),\r
236                 new ULocale("th_TH"), new ULocale("tr_TR"), new ULocale("zh"), new ULocale("zh_Hans"), new ULocale("zh_Hans_CN"),\r
237                 new ULocale("zh_Hant"), new ULocale("zh_Hant_HK"), new ULocale("zh_Hant_TW")\r
238             };\r
239         } else {\r
240             LOCALES = new ULocale[] {\r
241                 new ULocale("en"),\r
242             };\r
243         }\r
244 \r
245         SimpleDateFormat sdfGMT = new SimpleDateFormat(BASEPATTERN);\r
246         sdfGMT.setTimeZone(TimeZone.getTimeZone("Etc/GMT"));\r
247 \r
248         long testCounts = 0;\r
249         long[] testTimes = new long[4];\r
250         boolean[] expectedRoundTrip = new boolean[4];\r
251         int testLen = 0;\r
252         for (int locidx = 0; locidx < LOCALES.length; locidx++) {\r
253             logln("Locale: " + LOCALES[locidx].toString());\r
254             for (int patidx = 0; patidx < PATTERNS.length; patidx++) {\r
255                 logln("    pattern: " + PATTERNS[patidx]);\r
256                 String pattern = BASEPATTERN + " " + PATTERNS[patidx];\r
257                 SimpleDateFormat sdf = new SimpleDateFormat(pattern, LOCALES[locidx]);\r
258 \r
259                 String[] ids = TimeZone.getAvailableIDs();\r
260                 for (int zidx = 0; zidx < ids.length; zidx++) {\r
261                     String id = TimeZone.getCanonicalID(ids[zidx]);\r
262                     if (id == null || !id.equals(ids[zidx])) {\r
263                         // Skip aliases\r
264                         continue;\r
265                     }\r
266                     BasicTimeZone btz = (BasicTimeZone)TimeZone.getTimeZone(ids[zidx], TimeZone.TIMEZONE_ICU);\r
267                     TimeZone tz = TimeZone.getTimeZone(ids[zidx]);\r
268                     sdf.setTimeZone(tz);\r
269 \r
270                     long t = START_TIME;\r
271                     TimeZoneTransition tzt = null;\r
272                     boolean middle = true;\r
273                     while (t < END_TIME) {\r
274                         if (tzt == null) {\r
275                             testTimes[0] = t;\r
276                             expectedRoundTrip[0] = true;\r
277                             testLen = 1;\r
278                         } else {\r
279                             int fromOffset = tzt.getFrom().getRawOffset() + tzt.getFrom().getDSTSavings();\r
280                             int toOffset = tzt.getTo().getRawOffset() + tzt.getTo().getDSTSavings();\r
281                             int delta = toOffset - fromOffset;\r
282                             if (delta < 0) {\r
283                                 boolean isDstDecession = tzt.getFrom().getDSTSavings() > 0 && tzt.getTo().getDSTSavings() == 0;\r
284                                 testTimes[0] = t + delta - 1;\r
285                                 expectedRoundTrip[0] = true;\r
286                                 testTimes[1] = t + delta;\r
287                                 expectedRoundTrip[1] = isDstDecession ?\r
288                                         !AMBIGUOUS_DST_DECESSION[patidx] : !AMBIGUOUS_NEGATIVE_SHIFT[patidx];\r
289                                 testTimes[2] = t - 1;\r
290                                 expectedRoundTrip[2] = isDstDecession ?\r
291                                         !AMBIGUOUS_DST_DECESSION[patidx] : !AMBIGUOUS_NEGATIVE_SHIFT[patidx];\r
292                                 testTimes[3] = t;\r
293                                 expectedRoundTrip[3] = true;\r
294                                 testLen = 4;\r
295                             } else {\r
296                                 testTimes[0] = t - 1;\r
297                                 expectedRoundTrip[0] = true;\r
298                                 testTimes[1] = t;\r
299                                 expectedRoundTrip[1] = true;\r
300                                 testLen = 2;\r
301                             }\r
302                         }\r
303                         for (int testidx = 0; testidx < testLen; testidx++) {\r
304                             testCounts++;\r
305                             timer = System.currentTimeMillis();\r
306                             String text = sdf.format(new Date(testTimes[testidx]));\r
307                             try {\r
308                                 Date parsedDate = sdf.parse(text);\r
309                                 long restime = parsedDate.getTime();\r
310                                 if (restime != testTimes[testidx]) {\r
311                                     StringBuffer msg = new StringBuffer();\r
312                                     msg.append("Time round trip failed for ")\r
313                                         .append("tzid=").append(ids[zidx])\r
314                                         .append(", locale=").append(LOCALES[locidx])\r
315                                         .append(", pattern=").append(PATTERNS[patidx])\r
316                                         .append(", text=").append(text)\r
317                                         .append(", gmt=").append(sdfGMT.format(new Date(testTimes[testidx])))\r
318                                         .append(", time=").append(testTimes[testidx])\r
319                                         .append(", restime=").append(restime)\r
320                                         .append(", diff=").append(restime - testTimes[testidx]);\r
321                                     if (expectedRoundTrip[testidx]) {\r
322                                         errln("FAIL: " + msg.toString());\r
323                                     } else if (REALLY_VERBOSE) {\r
324                                         logln(msg.toString());\r
325                                     }\r
326                                 }\r
327                             } catch (ParseException pe) {\r
328                                 errln("FAIL: " + pe.getMessage());\r
329                             }\r
330                             times[patidx] += System.currentTimeMillis() - timer;\r
331                         }\r
332                         tzt = btz.getNextTransition(t, false);\r
333                         if (tzt == null) {\r
334                             break;\r
335                         }\r
336                         if (middle) {\r
337                             // Test the date in the middle of two transitions.\r
338                             t += (tzt.getTime() - t)/2;\r
339                             middle = false;\r
340                             tzt = null;\r
341                         } else {\r
342                             t = tzt.getTime();\r
343                         }\r
344                     }\r
345                 }\r
346             }\r
347         }\r
348 \r
349         long total = 0;\r
350         logln("### Elapsed time by patterns ###");\r
351         for (int i = 0; i < PATTERNS.length; i++) {\r
352             logln(times[i] + "ms (" + PATTERNS[i] + ")");\r
353             total += times[i];\r
354         }\r
355         logln("Total: " + total + "ms");\r
356         logln("Iteration: " + testCounts);\r
357     }\r
358 }