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