]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/tests/core/src/com/ibm/icu/dev/test/calendar/IBMCalendarTest.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / tests / core / src / com / ibm / icu / dev / test / calendar / IBMCalendarTest.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2000-2013, International Business Machines Corporation and
4  * others. All Rights Reserved.
5  *******************************************************************************
6  */
7 package com.ibm.icu.dev.test.calendar;
8
9 import java.text.ParseException;
10 import java.util.Date;
11 import java.util.Locale;
12 import java.util.Set;
13
14 import com.ibm.icu.impl.CalendarAstronomer;
15 import com.ibm.icu.impl.LocaleUtility;
16 import com.ibm.icu.impl.ZoneMeta;
17 import com.ibm.icu.text.DateFormat;
18 import com.ibm.icu.text.SimpleDateFormat;
19 import com.ibm.icu.util.BuddhistCalendar;
20 import com.ibm.icu.util.Calendar;
21 import com.ibm.icu.util.ChineseCalendar;
22 import com.ibm.icu.util.GregorianCalendar;
23 import com.ibm.icu.util.JapaneseCalendar;
24 import com.ibm.icu.util.TaiwanCalendar;
25 import com.ibm.icu.util.TimeZone;
26 import com.ibm.icu.util.TimeZone.SystemTimeZoneType;
27 import com.ibm.icu.util.ULocale;
28
29 /**
30  * @summary Tests of new functionality in IBMCalendar
31  */
32 public class IBMCalendarTest extends CalendarTest {
33
34     public static void main(String[] args) throws Exception {
35         new IBMCalendarTest().run(args);
36     }
37
38     /**
39      * Test weekend support in IBMCalendar.
40      *
41      * NOTE: This test will have to be updated when the isWeekend() etc.
42      *       API is finalized later.
43      *
44      *       In particular, the test will have to be rewritten to instantiate
45      *       a Calendar in the given locale (using getInstance()) and call
46      *       that Calendar's isWeekend() etc. methods.
47      */
48     public void TestWeekend() {
49         SimpleDateFormat fmt = new SimpleDateFormat("EEE MMM dd yyyy G HH:mm:ss.SSS");
50
51         // NOTE
52         // This test tests for specific locale data.  This is probably okay
53         // as far as US data is concerned, but if the Arabic/Yemen data
54         // changes, this test will have to be updated.
55
56         // Test specific days
57         Object[] DATA1 = {
58             Locale.US, new int[] { // Saturday:Sunday
59                 2000, Calendar.MARCH, 17, 23,  0, 0, // Fri 23:00
60                 2000, Calendar.MARCH, 18,  0, -1, 0, // Fri 23:59:59.999
61                 2000, Calendar.MARCH, 18,  0,  0, 1, // Sat 00:00
62                 2000, Calendar.MARCH, 18, 15,  0, 1, // Sat 15:00
63                 2000, Calendar.MARCH, 19, 23,  0, 1, // Sun 23:00
64                 2000, Calendar.MARCH, 20,  0, -1, 1, // Sun 23:59:59.999
65                 2000, Calendar.MARCH, 20,  0,  0, 0, // Mon 00:00
66                 2000, Calendar.MARCH, 20,  8,  0, 0, // Mon 08:00
67             },
68             new Locale("ar", "OM"), new int[] { // Thursday:Friday
69                 2000, Calendar.MARCH, 15, 23,  0, 0, // Wed 23:00
70                 2000, Calendar.MARCH, 16,  0, -1, 0, // Wed 23:59:59.999
71                 2000, Calendar.MARCH, 16,  0,  0, 1, // Thu 00:00
72                 2000, Calendar.MARCH, 16, 15,  0, 1, // Thu 15:00
73                 2000, Calendar.MARCH, 17, 23,  0, 1, // Fri 23:00
74                 2000, Calendar.MARCH, 18,  0, -1, 1, // Fri 23:59:59.999
75                 2000, Calendar.MARCH, 18,  0,  0, 0, // Sat 00:00
76                 2000, Calendar.MARCH, 18,  8,  0, 0, // Sat 08:00
77             },
78         };
79
80         // Test days of the week
81         Object[] DATA2 = {
82             Locale.US, new int[] {
83                 Calendar.MONDAY,   Calendar.WEEKDAY,
84                 Calendar.FRIDAY,   Calendar.WEEKDAY,
85                 Calendar.SATURDAY, Calendar.WEEKEND,
86                 Calendar.SUNDAY,   Calendar.WEEKEND,
87             },
88             new Locale("ar", "OM"), new int[] { // Thursday:Friday
89                 Calendar.WEDNESDAY,Calendar.WEEKDAY,
90                 Calendar.SATURDAY, Calendar.WEEKDAY,
91                 Calendar.THURSDAY, Calendar.WEEKEND,
92                 Calendar.FRIDAY,   Calendar.WEEKEND,
93             },
94             new Locale("hi", "IN"), new int[] { // Sunday only
95                 Calendar.MONDAY,   Calendar.WEEKDAY,
96                 Calendar.FRIDAY,   Calendar.WEEKDAY,
97                 Calendar.SATURDAY, Calendar.WEEKDAY,
98                 Calendar.SUNDAY,   Calendar.WEEKEND,
99             },
100         };
101
102         // We only test the getDayOfWeekType() and isWeekend() APIs.
103         // The getWeekendTransition() API is tested indirectly via the
104         // isWeekend() API, which calls it.
105
106         for (int i1=0; i1<DATA1.length; i1+=2) {
107             Locale loc = (Locale)DATA1[i1];
108             int[] data = (int[]) DATA1[i1+1];
109             Calendar cal = Calendar.getInstance(loc);
110             logln("Locale: " + loc);
111             for (int i=0; i<data.length; i+=6) {
112                 cal.clear();
113                 cal.set(data[i], data[i+1], data[i+2], data[i+3], 0, 0);
114                 if (data[i+4] != 0) {
115                     cal.setTime(new Date(cal.getTime().getTime() + data[i+4]));
116                 }
117                 boolean isWeekend = cal.isWeekend();
118                 boolean ok = isWeekend == (data[i+5] != 0);
119                 if (ok) {
120                     logln("Ok:   " + fmt.format(cal.getTime()) + " isWeekend=" + isWeekend);
121                 } else {
122                     errln("FAIL: " + fmt.format(cal.getTime()) + " isWeekend=" + isWeekend +
123                           ", expected=" + (!isWeekend));
124                 }
125             }
126         }
127
128         for (int i2=0; i2<DATA2.length; i2+=2) {
129             Locale loc = (Locale)DATA2[i2];
130             int[] data = (int[]) DATA2[i2+1];
131             logln("Locale: " + loc);
132             Calendar cal = Calendar.getInstance(loc);
133             for (int i=0; i<data.length; i+=2) {
134                 int type = cal.getDayOfWeekType(data[i]);
135                 int exp  = data[i+1];
136                 if (type == exp) {
137                     logln("Ok:   DOW " + data[i] + " type=" + type);
138                 } else {
139                     errln("FAIL: DOW " + data[i] + " type=" + type +
140                           ", expected=" + exp);
141                 }
142             }
143         }
144     }
145
146     /**
147      * Run a test of a quasi-Gregorian calendar.  This is a calendar
148      * that behaves like a Gregorian but has different year/era mappings.
149      * The int[] data array should have the format:
150      *
151      * { era, year, gregorianYear, month, dayOfMonth, ... }
152      */
153     void quasiGregorianTest(Calendar cal, int[] data) {
154         // As of JDK 1.4.1_01, using the Sun JDK GregorianCalendar as
155         // a reference throws us off by one hour.  This is most likely
156         // due to the JDK 1.4 incorporation of historical time zones.
157         //java.util.Calendar grego = java.util.Calendar.getInstance();
158         Calendar grego = Calendar.getInstance();
159         for (int i=0; i<data.length; ) {
160             int era = data[i++];
161             int year = data[i++];
162             int gregorianYear = data[i++];
163             int month = data[i++];
164             int dayOfMonth = data[i++];
165
166             grego.clear();
167             grego.set(gregorianYear, month, dayOfMonth);
168             Date D = grego.getTime();
169
170             cal.clear();
171             cal.set(Calendar.ERA, era);
172             cal.set(year, month, dayOfMonth);
173             Date d = cal.getTime();
174             if (d.equals(D)) {
175                 logln("OK: " + era + ":" + year + "/" + (month+1) + "/" + dayOfMonth +
176                       " => " + d);
177             } else {
178                 errln("Fail: " + era + ":" + year + "/" + (month+1) + "/" + dayOfMonth +
179                       " => " + d + ", expected " + D);
180             }
181
182             cal.clear();
183             cal.setTime(D);
184             int e = cal.get(Calendar.ERA);
185             int y = cal.get(Calendar.YEAR);
186             if (y == year && e == era) {
187                 logln("OK: " + D + " => " + cal.get(Calendar.ERA) + ":" +
188                       cal.get(Calendar.YEAR) + "/" +
189                       (cal.get(Calendar.MONTH)+1) + "/" + cal.get(Calendar.DATE));
190             } else {
191                 logln("Fail: " + D + " => " + cal.get(Calendar.ERA) + ":" +
192                       cal.get(Calendar.YEAR) + "/" +
193                       (cal.get(Calendar.MONTH)+1) + "/" + cal.get(Calendar.DATE) +
194                       ", expected " + era + ":" + year + "/" + (month+1) + "/" +
195                       dayOfMonth);
196             }
197         }
198     }
199
200     /**
201      * Verify that BuddhistCalendar shifts years to Buddhist Era but otherwise
202      * behaves like GregorianCalendar.
203      */
204     public void TestBuddhist() {
205         quasiGregorianTest(new BuddhistCalendar(),
206                            new int[] {
207                                // BE 2542 == 1999 CE
208                                0, 2542, 1999, Calendar.JUNE, 4
209                            });
210     }
211
212     public void TestBuddhistCoverage() {
213     {
214         // new BuddhistCalendar(ULocale)
215         BuddhistCalendar cal = new BuddhistCalendar(ULocale.getDefault());
216         if(cal == null){
217             errln("could not create BuddhistCalendar with ULocale");
218         }
219     }
220
221     {
222         // new BuddhistCalendar(TimeZone,ULocale)
223         BuddhistCalendar cal = new BuddhistCalendar(TimeZone.getDefault(),ULocale.getDefault());
224         if(cal == null){
225             errln("could not create BuddhistCalendar with TimeZone ULocale");
226         }
227     }
228
229     {
230         // new BuddhistCalendar(TimeZone)
231         BuddhistCalendar cal = new BuddhistCalendar(TimeZone.getDefault());
232         if(cal == null){
233             errln("could not create BuddhistCalendar with TimeZone");
234         }
235     }
236
237     {
238         // new BuddhistCalendar(Locale)
239         BuddhistCalendar cal = new BuddhistCalendar(Locale.getDefault());
240         if(cal == null){
241             errln("could not create BuddhistCalendar with Locale");
242         }
243     }
244
245     {
246         // new BuddhistCalendar(TimeZone, Locale)
247         BuddhistCalendar cal = new BuddhistCalendar(TimeZone.getDefault(), Locale.getDefault());
248         if(cal == null){
249             errln("could not create BuddhistCalendar with TimeZone and Locale");
250         }
251     }
252
253     {
254         // new BuddhistCalendar(Date)
255         BuddhistCalendar cal = new BuddhistCalendar(new Date());
256         if(cal == null){
257             errln("could not create BuddhistCalendar with Date");
258         }
259     }
260
261     {
262         // new BuddhistCalendar(int year, int month, int date)
263         BuddhistCalendar cal = new BuddhistCalendar(2543, Calendar.MAY, 22);
264         if(cal == null){
265             errln("could not create BuddhistCalendar with year,month,data");
266         }
267     }
268
269     {
270         // new BuddhistCalendar(int year, int month, int date, int hour, int minute, int second)
271         BuddhistCalendar cal = new BuddhistCalendar(2543, Calendar.MAY, 22, 1, 1, 1);
272         if(cal == null){
273             errln("could not create BuddhistCalendar with year,month,date,hour,minute,second");
274         }
275     }
276
277     {
278         // data
279         BuddhistCalendar cal = new BuddhistCalendar(2543, Calendar.MAY, 22);
280         Date time = cal.getTime();
281
282         String[] calendarLocales = {
283         "th_TH"
284         };
285
286         String[] formatLocales = {
287         "en", "ar", "hu", "th"
288         };
289
290         for (int i = 0; i < calendarLocales.length; ++i) {
291         String calLocName = calendarLocales[i];
292         Locale calLocale = LocaleUtility.getLocaleFromName(calLocName);
293         cal = new BuddhistCalendar(calLocale);
294
295         for (int j = 0; j < formatLocales.length; ++j) {
296             String locName = formatLocales[j];
297             Locale formatLocale = LocaleUtility.getLocaleFromName(locName);
298             DateFormat format = DateFormat.getDateTimeInstance(cal, DateFormat.FULL, DateFormat.FULL, formatLocale);
299             logln(calLocName + "/" + locName + " --> " + format.format(time));
300         }
301         }
302     }
303     }
304
305     /**
306      * Test limits of the Buddhist calendar.
307      */
308     public void TestBuddhistLimits() {
309         // Final parameter is either number of days, if > 0, or test
310         // duration in seconds, if < 0.
311         Calendar cal = Calendar.getInstance();
312         cal.set(2007, Calendar.JANUARY, 1);
313         BuddhistCalendar buddhist = new BuddhistCalendar();
314         doLimitsTest(buddhist, null, cal.getTime());
315         doTheoreticalLimitsTest(buddhist, false);
316     }
317
318     /**
319      * Default calendar for Thai (Ticket#6302)
320      */
321     public void TestThaiDefault() {
322         // Buddhist calendar is used as the default calendar for
323         // Thai locale
324         Calendar cal = Calendar.getInstance(new ULocale("th_TH"));
325         String type = cal.getType();
326         if (!type.equals("buddhist")) {
327             errln("FAIL: Buddhist calendar is not returned for locale " + cal.toString());
328         }
329     }
330     
331     /**
332      * Verify that TaiwanCalendar shifts years to Minguo Era but otherwise
333      * behaves like GregorianCalendar.
334      */
335     public void TestTaiwan() {
336         quasiGregorianTest(new TaiwanCalendar(),
337                            new int[] {
338                                TaiwanCalendar.BEFORE_MINGUO, 8, 1904, Calendar.FEBRUARY, 29,
339                                TaiwanCalendar.MINGUO, 1, 1912, Calendar.JUNE, 4,
340                                TaiwanCalendar.MINGUO, 3, 1914, Calendar.FEBRUARY, 12,
341                                TaiwanCalendar.MINGUO, 96,2007, Calendar.FEBRUARY, 12,
342                            });
343     }
344
345     /**
346      * Test limits of the Taiwan calendar.
347      */
348     public void TestTaiwanLimits() {
349         // Final parameter is either number of days, if > 0, or test
350         // duration in seconds, if < 0.
351         Calendar cal = Calendar.getInstance();
352         cal.set(2007, Calendar.JANUARY, 1);
353         TaiwanCalendar taiwan = new TaiwanCalendar();
354         doLimitsTest(taiwan, null, cal.getTime());
355         doTheoreticalLimitsTest(taiwan, false);
356     }
357
358     public void TestTaiwanCoverage() {
359     {
360         // new TaiwanCalendar(ULocale)
361         TaiwanCalendar cal = new TaiwanCalendar(ULocale.getDefault());
362         if(cal == null){
363             errln("could not create TaiwanCalendar with ULocale");
364         }
365     }
366
367     {
368         // new TaiwanCalendar(TimeZone,ULocale)
369         TaiwanCalendar cal = new TaiwanCalendar(TimeZone.getDefault(),ULocale.getDefault());
370         if(cal == null){
371             errln("could not create TaiwanCalendar with TimeZone ULocale");
372         }
373     }
374
375     {
376         // new TaiwanCalendar(TimeZone)
377         TaiwanCalendar cal = new TaiwanCalendar(TimeZone.getDefault());
378         if(cal == null){
379             errln("could not create TaiwanCalendar with TimeZone");
380         }
381     }
382
383     {
384         // new TaiwanCalendar(Locale)
385         TaiwanCalendar cal = new TaiwanCalendar(Locale.getDefault());
386         if(cal == null){
387             errln("could not create TaiwanCalendar with Locale");
388         }
389     }
390
391     {
392         // new TaiwanCalendar(TimeZone, Locale)
393         TaiwanCalendar cal = new TaiwanCalendar(TimeZone.getDefault(), Locale.getDefault());
394         if(cal == null){
395             errln("could not create TaiwanCalendar with TimeZone and Locale");
396         }
397     }
398
399     {
400         // new TaiwanCalendar(Date)
401         TaiwanCalendar cal = new TaiwanCalendar(new Date());
402         if(cal == null){
403             errln("could not create TaiwanCalendar with Date");
404         }
405     }
406
407     {
408         // new TaiwanCalendar(int year, int month, int date)
409         TaiwanCalendar cal = new TaiwanCalendar(34, Calendar.MAY, 22);
410         if(cal == null){
411             errln("could not create TaiwanCalendar with year,month,data");
412         }
413     }
414
415     {
416         // new TaiwanCalendar(int year, int month, int date, int hour, int minute, int second)
417         TaiwanCalendar cal = new TaiwanCalendar(34, Calendar.MAY, 22, 1, 1, 1);
418         if(cal == null){
419             errln("could not create TaiwanCalendar with year,month,date,hour,minute,second");
420         }
421     }
422
423     {
424         // data
425         TaiwanCalendar cal = new TaiwanCalendar(34, Calendar.MAY, 22);
426         Date time = cal.getTime();
427
428         String[] calendarLocales = {
429         "en","zh"
430         };
431
432         String[] formatLocales = {
433         "en", "ar", "hu", "th"
434         };
435
436         for (int i = 0; i < calendarLocales.length; ++i) {
437         String calLocName = calendarLocales[i];
438         Locale calLocale = LocaleUtility.getLocaleFromName(calLocName);
439         cal = new TaiwanCalendar(calLocale);
440
441         for (int j = 0; j < formatLocales.length; ++j) {
442             String locName = formatLocales[j];
443             Locale formatLocale = LocaleUtility.getLocaleFromName(locName);
444             DateFormat format = DateFormat.getDateTimeInstance(cal, DateFormat.FULL, DateFormat.FULL, formatLocale);
445             logln(calLocName + "/" + locName + " --> " + format.format(time));
446         }
447         }
448     }
449     }
450
451     /**
452      * Verify that JapaneseCalendar shifts years to Japanese Eras but otherwise
453      * behaves like GregorianCalendar.
454      */
455     public void TestJapanese() {
456         // First make sure this test works for GregorianCalendar
457         int[] control = {
458             GregorianCalendar.AD, 1868, 1868, Calendar.SEPTEMBER, 8,
459             GregorianCalendar.AD, 1868, 1868, Calendar.SEPTEMBER, 9,
460             GregorianCalendar.AD, 1869, 1869, Calendar.JUNE, 4,
461             GregorianCalendar.AD, 1912, 1912, Calendar.JULY, 29,
462             GregorianCalendar.AD, 1912, 1912, Calendar.JULY, 30,
463             GregorianCalendar.AD, 1912, 1912, Calendar.AUGUST, 1,
464         };
465         quasiGregorianTest(new GregorianCalendar(), control);
466
467         int[] data = {
468             JapaneseCalendar.MEIJI, 1, 1868, Calendar.SEPTEMBER, 8,
469             JapaneseCalendar.MEIJI, 1, 1868, Calendar.SEPTEMBER, 9,
470             JapaneseCalendar.MEIJI, 2, 1869, Calendar.JUNE, 4,
471             JapaneseCalendar.MEIJI, 45, 1912, Calendar.JULY, 29,
472             JapaneseCalendar.TAISHO, 1, 1912, Calendar.JULY, 30,
473             JapaneseCalendar.TAISHO, 1, 1912, Calendar.AUGUST, 1,
474         };
475         quasiGregorianTest(new JapaneseCalendar(), data);
476     }
477
478     /**
479      * Test limits of the Gregorian calendar.
480      */
481     public void TestGregorianLimits() {
482         // Final parameter is either number of days, if > 0, or test
483         // duration in seconds, if < 0.
484         Calendar cal = Calendar.getInstance();
485         cal.set(2004, Calendar.JANUARY, 1);
486         GregorianCalendar gregorian = new GregorianCalendar();
487         doLimitsTest(gregorian, null, cal.getTime());
488         doTheoreticalLimitsTest(gregorian, false);
489     }
490
491     /**
492      * Test behavior of fieldDifference around leap years.  Also test a large
493      * field difference to check binary search.
494      */
495     public void TestLeapFieldDifference() {
496         Calendar cal = Calendar.getInstance();
497         cal.set(2004, Calendar.FEBRUARY, 29);
498         Date date2004 = cal.getTime();
499         cal.set(2000, Calendar.FEBRUARY, 29);
500         Date date2000 = cal.getTime();
501         int y = cal.fieldDifference(date2004, Calendar.YEAR);
502         int d = cal.fieldDifference(date2004, Calendar.DAY_OF_YEAR);
503         if (d == 0) {
504             logln("Ok: 2004/Feb/29 - 2000/Feb/29 = " + y + " years, " + d + " days");
505         } else {
506             errln("FAIL: 2004/Feb/29 - 2000/Feb/29 = " + y + " years, " + d + " days");
507         }
508         cal.setTime(date2004);
509         y = cal.fieldDifference(date2000, Calendar.YEAR);
510         d = cal.fieldDifference(date2000, Calendar.DAY_OF_YEAR);
511         if (d == 0) {
512             logln("Ok: 2000/Feb/29 - 2004/Feb/29 = " + y + " years, " + d + " days");
513         } else {
514             errln("FAIL: 2000/Feb/29 - 2004/Feb/29 = " + y + " years, " + d + " days");
515         }
516         // Test large difference
517         cal.set(2001, Calendar.APRIL, 5); // 2452005
518         Date ayl = cal.getTime();
519         cal.set(1964, Calendar.SEPTEMBER, 7); // 2438646
520         Date asl = cal.getTime();
521         d = cal.fieldDifference(ayl, Calendar.DAY_OF_MONTH);
522         cal.setTime(ayl);
523         int d2 = cal.fieldDifference(asl, Calendar.DAY_OF_MONTH);
524         if (d == -d2 && d == 13359) {
525             logln("Ok: large field difference symmetrical " + d);
526         } else {
527             logln("FAIL: large field difference incorrect " + d + ", " + d2 +
528                   ", expect +/- 13359");
529         }
530     }
531
532     /**
533      * Test ms_MY "Malay (Malaysia)" locale.  Bug 1543.
534      */
535     public void TestMalaysianInstance() {
536         Locale loc = new Locale("ms", "MY");  // Malay (Malaysia)
537         Calendar cal = Calendar.getInstance(loc);
538         if(cal == null){
539             errln("could not create Malaysian instance");
540         }
541     }
542
543     /**
544      * setFirstDayOfWeek and setMinimalDaysInFirstWeek may change the
545      * field <=> time mapping, since they affect the interpretation of
546      * the WEEK_OF_MONTH or WEEK_OF_YEAR fields.
547      */
548     public void TestWeekShift() {
549         Calendar cal = new GregorianCalendar(
550                              TimeZone.getTimeZone("America/Los_Angeles"),
551                              new Locale("en", "US"));
552         cal.setTime(new Date(997257600000L)); // Wed Aug 08 01:00:00 PDT 2001
553         // In pass one, change the first day of week so that the weeks
554         // shift in August 2001.  In pass two, change the minimal days
555         // in the first week so that the weeks shift in August 2001.
556         //     August 2001
557         // Su Mo Tu We Th Fr Sa
558         //           1  2  3  4
559         //  5  6  7  8  9 10 11
560         // 12 13 14 15 16 17 18
561         // 19 20 21 22 23 24 25
562         // 26 27 28 29 30 31
563         for (int pass=0; pass<2; ++pass) {
564             if (pass==0) {
565                 cal.setFirstDayOfWeek(Calendar.WEDNESDAY);
566                 cal.setMinimalDaysInFirstWeek(4);
567             } else {
568                 cal.setFirstDayOfWeek(Calendar.SUNDAY);
569                 cal.setMinimalDaysInFirstWeek(4);
570             }
571             cal.add(Calendar.DATE, 1); // Force recalc
572             cal.add(Calendar.DATE, -1);
573
574             Date time1 = cal.getTime(); // Get time -- should not change
575
576             // Now change a week parameter and then force a recalc.
577             // The bug is that the recalc should not be necessary --
578             // calendar should do so automatically.
579             if (pass==0) {
580                 cal.setFirstDayOfWeek(Calendar.THURSDAY);
581             } else {
582                 cal.setMinimalDaysInFirstWeek(5);
583             }
584
585             int woy1 = cal.get(Calendar.WEEK_OF_YEAR);
586             int wom1 = cal.get(Calendar.WEEK_OF_MONTH);
587
588             cal.add(Calendar.DATE, 1); // Force recalc
589             cal.add(Calendar.DATE, -1);
590
591             int woy2 = cal.get(Calendar.WEEK_OF_YEAR);
592             int wom2 = cal.get(Calendar.WEEK_OF_MONTH);
593
594             Date time2 = cal.getTime();
595
596             if (!time1.equals(time2)) {
597                 errln("FAIL: shifting week should not alter time");
598             } else {
599                 logln(time1.toString());
600             }
601             if (woy1 == woy2 && wom1 == wom2) {
602                 logln("Ok: WEEK_OF_YEAR: " + woy1 +
603                       ", WEEK_OF_MONTH: " + wom1);
604             } else {
605                 errln("FAIL: WEEK_OF_YEAR: " + woy1 + " => " + woy2 +
606                       ", WEEK_OF_MONTH: " + wom1 + " => " + wom2 +
607                       " after week shift");
608             }
609         }
610     }
611
612     /**
613      * Make sure that when adding a day, we actually wind up in a
614      * different day.  The DST adjustments we use to keep the hour
615      * constant across DST changes can backfire and change the day.
616      */
617     public void TestTimeZoneTransitionAdd() {
618         Locale locale = Locale.US; // could also be CHINA
619         SimpleDateFormat dateFormat =
620             new SimpleDateFormat("MM/dd/yyyy HH:mm z", locale);
621
622         String tz[] = TimeZone.getAvailableIDs();
623
624         for (int z=0; z<tz.length; ++z) {
625             TimeZone t = TimeZone.getTimeZone(tz[z]);
626             dateFormat.setTimeZone(t);
627
628             Calendar cal = Calendar.getInstance(t, locale);
629             cal.clear();
630             // Scan the year 2003, overlapping the edges of the year
631             cal.set(Calendar.YEAR, 2002);
632             cal.set(Calendar.MONTH, Calendar.DECEMBER);
633             cal.set(Calendar.DAY_OF_MONTH, 25);
634
635             for (int i=0; i<365+10; ++i) {
636                 Date yesterday = cal.getTime();
637                 int yesterday_day = cal.get(Calendar.DAY_OF_MONTH);
638                 cal.add(Calendar.DAY_OF_MONTH, 1);
639                 if (yesterday_day == cal.get(Calendar.DAY_OF_MONTH)) {
640                     errln(tz[z] + " " +
641                           dateFormat.format(yesterday) + " +1d= " +
642                           dateFormat.format(cal.getTime()));
643                 }
644             }
645         }
646     }
647
648     public void TestJB1684() {
649         class TestData {
650             int year;
651             int month;
652             int date;
653             int womyear;
654             int wommon;
655             int wom;
656             int dow;
657             String data;
658             String normalized;
659
660             public TestData(int year, int month, int date,
661                             int womyear, int wommon, int wom, int dow,
662                             String data, String normalized) {
663                 this.year = year;
664                 this.month = month-1;
665                 this.date = date;
666                 this.womyear = womyear;
667                 this.wommon = wommon-1;
668                 this.wom = wom;
669                 this.dow = dow;
670                 this.data = data; // year, month, week of month, day
671                 this.normalized = data;
672                 if (normalized != null) this.normalized = normalized;
673             }
674         }
675
676         //      July 2001            August 2001           January 2002
677         // Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
678         //  1  2  3  4  5  6  7            1  2  3  4         1  2  3  4  5
679         //  8  9 10 11 12 13 14   5  6  7  8  9 10 11   6  7  8  9 10 11 12
680         // 15 16 17 18 19 20 21  12 13 14 15 16 17 18  13 14 15 16 17 18 19
681         // 22 23 24 25 26 27 28  19 20 21 22 23 24 25  20 21 22 23 24 25 26
682         // 29 30 31              26 27 28 29 30 31     27 28 29 30 31
683         TestData[] tests = {
684             new TestData(2001, 8,  6,  2001,8,2,Calendar.MONDAY,    "2001 08 02 Mon", null),
685             new TestData(2001, 8,  7,  2001,8,2,Calendar.TUESDAY,   "2001 08 02 Tue", null),
686             new TestData(2001, 8,  5,/*12,*/ 2001,8,2,Calendar.SUNDAY,    "2001 08 02 Sun", null),
687             new TestData(2001, 8,6, /*7,  30,*/ 2001,7,6,Calendar.MONDAY,    "2001 07 06 Mon", "2001 08 02 Mon"),
688             new TestData(2001, 8,7, /*7,  31,*/ 2001,7,6,Calendar.TUESDAY,   "2001 07 06 Tue", "2001 08 02 Tue"),
689             new TestData(2001, 8,  5,  2001,7,6,Calendar.SUNDAY,    "2001 07 06 Sun", "2001 08 02 Sun"),
690             new TestData(2001, 7,  30, 2001,8,1,Calendar.MONDAY,    "2001 08 01 Mon", "2001 07 05 Mon"),
691             new TestData(2001, 7,  31, 2001,8,1,Calendar.TUESDAY,   "2001 08 01 Tue", "2001 07 05 Tue"),
692             new TestData(2001, 7,29, /*8,  5,*/  2001,8,1,Calendar.SUNDAY,    "2001 08 01 Sun", "2001 07 05 Sun"),
693             new TestData(2001, 12, 31, 2001,12,6,Calendar.MONDAY,   "2001 12 06 Mon", null),
694             new TestData(2002, 1,  1,  2002,1,1,Calendar.TUESDAY,   "2002 01 01 Tue", null),
695             new TestData(2002, 1,  2,  2002,1,1,Calendar.WEDNESDAY, "2002 01 01 Wed", null),
696             new TestData(2002, 1,  3,  2002,1,1,Calendar.THURSDAY,  "2002 01 01 Thu", null),
697             new TestData(2002, 1,  4,  2002,1,1,Calendar.FRIDAY,    "2002 01 01 Fri", null),
698             new TestData(2002, 1,  5,  2002,1,1,Calendar.SATURDAY,  "2002 01 01 Sat", null),
699             new TestData(2001,12,30, /*2002, 1,  6,*/  2002,1,1,Calendar.SUNDAY,    "2002 01 01 Sun", "2001 12 06 Sun"),
700         };
701
702         int pass = 0, error = 0, warning = 0;
703
704         final String pattern = "yyyy MM WW EEE";
705         GregorianCalendar cal = new GregorianCalendar();
706         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
707         sdf.setCalendar(cal);
708
709         cal.setFirstDayOfWeek(Calendar.SUNDAY);
710         cal.setMinimalDaysInFirstWeek(1);
711
712         for (int i = 0; i < tests.length; ++i) {
713             TestData test = tests[i];
714             log("\n-----\nTesting round trip of " + test.year +
715                   " " + (test.month + 1) +
716                   " " + test.date +
717                   " (written as) " + test.data);
718
719             cal.clear();
720             cal.set(test.year, test.month, test.date);
721             Date ms = cal.getTime();
722
723             cal.clear();
724             cal.set(Calendar.YEAR, test.womyear);
725             cal.set(Calendar.MONTH, test.wommon);
726             cal.set(Calendar.WEEK_OF_MONTH, test.wom);
727             cal.set(Calendar.DAY_OF_WEEK, test.dow);
728             Date ms2 = cal.getTime();
729
730             if (!ms2.equals(ms)) {
731                 log("\nError: GregorianCalendar.DOM gave " + ms +
732                     "\n       GregorianCalendar.WOM gave " + ms2);
733                 error++;
734             } else {
735                 pass++;
736             }
737
738             ms2 = null;
739             try {
740                 ms2 = sdf.parse(test.data);
741             }
742             catch (ParseException e) {
743                 errln("parse exception: " + e);
744             }
745
746             if (!ms2.equals(ms)) {
747                 log("\nError: GregorianCalendar gave      " + ms +
748                     "\n       SimpleDateFormat.parse gave " + ms2);
749                 error++;
750             } else {
751                 pass++;
752             }
753
754             String result = sdf.format(ms);
755             if (!result.equals(test.normalized)) {
756                 log("\nWarning: format of '" + test.data + "' gave" +
757                     "\n                   '" + result + "'" +
758                     "\n          expected '" + test.normalized + "'");
759                 warning++;
760             } else {
761                 pass++;
762             }
763
764             Date ms3 = null;
765             try {
766                 ms3 = sdf.parse(result);
767             }
768             catch (ParseException e) {
769                 errln("parse exception 2: " + e);
770             }
771
772             if (!ms3.equals(ms)) {
773                 error++;
774                 log("\nError: Re-parse of '" + result + "' gave time of " +
775                     "\n        " + ms3 +
776                     "\n    not " + ms);
777             } else {
778                 pass++;
779             }
780         }
781         String info = "\nPassed: " + pass + ", Warnings: " + warning + ", Errors: " + error;
782         if (error > 0) {
783             errln(info);
784         } else {
785             logln(info);
786         }
787     }
788
789     /**
790      * Test the ZoneMeta API.
791      */
792     public void TestZoneMeta() {
793         // Test index by country API
794
795         // Format: {country, zone1, zone2, ..., zoneN}
796         String COUNTRY[][] = { {""},
797                                {"US", "America/Los_Angeles", "PST"} };
798         StringBuffer buf = new StringBuffer();
799         for (int i=0; i<COUNTRY.length; ++i) {
800             Set<String> a = ZoneMeta.getAvailableIDs(SystemTimeZoneType.ANY, COUNTRY[i][0], null);
801             buf.setLength(0);
802             buf.append("Country \"" + COUNTRY[i][0] + "\": [");
803             // Use bitmask to track which of the expected zones we see
804             int mask = 0;
805             boolean first = true;
806             for (String z : a) {
807                 if (first) {
808                     first = false;
809                 } else {
810                     buf.append(", ");
811                 }
812                 buf.append(z);
813                 for (int k = 1; k < COUNTRY[i].length; ++k) {
814                     if ((mask & (1 << k)) == 0 && z.equals(COUNTRY[i][k])) {
815                         mask |= (1 << k);
816                     }
817                 }
818             }
819             buf.append("]");
820             mask >>= 1;
821             // Check bitmask to see if we saw all expected zones
822             if (mask == (1 << (COUNTRY[i].length-1))-1) {
823                 logln(buf.toString());
824             } else {
825                 errln(buf.toString());
826             }
827         }
828
829         // Test equivalent IDs API
830
831         int n = ZoneMeta.countEquivalentIDs("PST");
832         boolean ok = false;
833         buf.setLength(0);
834         buf.append("Equivalent to PST: ");
835         for (int i=0; i<n; ++i) {
836             String id = ZoneMeta.getEquivalentID("PST", i);
837             if (id.equals("America/Los_Angeles")) {
838                 ok = true;
839             }
840             if (i!=0) buf.append(", ");
841             buf.append(id);
842         }
843         if (ok) {
844             logln(buf.toString());
845         } else {
846             errln(buf.toString());
847         }
848     }
849
850     public void TestComparable() {
851     GregorianCalendar c0 = new GregorianCalendar();
852     GregorianCalendar c1 = new GregorianCalendar();
853     c1.add(Calendar.DAY_OF_MONTH, 1);
854     if (c0.compareTo(c1) >= 0) {
855         errln("calendar " + c0 + " not < " + c1);
856     }
857     c0.add(Calendar.MONTH, 1);
858     if (c0.compareTo(c1) <= 0) {
859         errln("calendar " + c0 + " not > " + c1);
860     }
861
862     c0.setTimeInMillis(c1.getTimeInMillis());
863     if (c0.compareTo(c1) != 0) {
864         errln("calendar " + c0 + " not == " + c1);
865     }
866
867     }
868
869     /**
870      * Miscellaneous tests to increase coverage.
871      */
872     public void TestCoverage() {
873         // BuddhistCalendar
874         BuddhistCalendar bcal = new BuddhistCalendar();
875         /*int i =*/ bcal.getMinimum(Calendar.ERA);
876         bcal.add(Calendar.YEAR, 1);
877         bcal.add(Calendar.MONTH, 1);
878         /*Date d = */bcal.getTime();
879
880         // CalendarAstronomer
881         // (This class should probably be made package-private.)
882         CalendarAstronomer astro = new CalendarAstronomer();
883         /*String s = */astro.local(0);
884
885         // ChineseCalendar
886         ChineseCalendar ccal = new ChineseCalendar(TimeZone.getDefault(),
887                                                    Locale.getDefault());
888         ccal.add(Calendar.MONTH, 1);
889         ccal.add(Calendar.YEAR, 1);
890         ccal.roll(Calendar.MONTH, 1);
891         ccal.roll(Calendar.YEAR, 1);
892         ccal.getTime();
893
894         // ICU 2.6
895         Calendar cal = Calendar.getInstance(Locale.US);
896         logln(cal.toString());
897         logln(cal.getDisplayName(Locale.US));
898         int weekendOnset=-1;
899         int weekendCease=-1;
900         for (int i=Calendar.SUNDAY; i<=Calendar.SATURDAY; ++i) {
901             if (cal.getDayOfWeekType(i) == Calendar.WEEKEND_ONSET) {
902                 weekendOnset = i;
903             }
904             if (cal.getDayOfWeekType(i) == Calendar.WEEKEND_CEASE) {
905                 weekendCease = i;
906             }
907         }
908         // can't call this unless we get a transition day (unusual),
909         // but make the call anyway for coverage reasons
910         try {
911             /*int x=*/ cal.getWeekendTransition(weekendOnset);
912             /*int x=*/ cal.getWeekendTransition(weekendCease);
913         } catch (IllegalArgumentException e) {}
914         /*int x=*/ cal.isWeekend(new Date());
915
916         // new GregorianCalendar(ULocale)
917         GregorianCalendar gcal = new GregorianCalendar(ULocale.getDefault());
918         if(gcal==null){
919             errln("could not create GregorianCalendar with ULocale");
920         } else {
921             logln("Calendar display name: " + gcal.getDisplayName(ULocale.getDefault()));
922         }
923
924         //cover getAvailableULocales
925         final ULocale[] locales = Calendar.getAvailableULocales();
926         long count = locales.length;
927         if (count == 0)
928             errln("getAvailableULocales return empty list");
929         logln("" + count + " available ulocales in Calendar.");
930
931         // Jitterbug 4451, for coverage
932         class StubCalendar extends Calendar{
933             /**
934              * For serialization
935              */
936             private static final long serialVersionUID = -4558903444622684759L;
937             protected int handleGetLimit(int field, int limitType) {return 0;}
938             protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) {return 0;}
939             protected int handleGetExtendedYear() {return 0;}
940             public void run(){
941                 if (Calendar.gregorianPreviousMonthLength(2000,2) != 29){
942                     errln("Year 2000 Feb should have 29 days.");
943                 }
944                 long millis = Calendar.julianDayToMillis(Calendar.MAX_JULIAN);
945                 if(millis != Calendar.MAX_MILLIS){
946                     errln("Did not get the expected value from julianDayToMillis. Got:" + millis);
947                 }
948                 DateFormat df = handleGetDateFormat("",Locale.getDefault());
949                 if (!df.equals(handleGetDateFormat("",ULocale.getDefault()))){
950                     errln ("Calendar.handleGetDateFormat(String, Locale) should delegate to ( ,ULocale)");
951                 }
952                 if (!getType().equals("unknown")){
953                     errln ("Calendar.getType() should be 'unknown'");
954                 }
955             }
956         }
957         StubCalendar stub = new StubCalendar();
958         stub.run();
959     }
960
961     // Tests for jb 4541
962     public void TestJB4541() {
963         ULocale loc = new ULocale("en_US");
964
965         // !!! Shouldn't we have an api like this?
966         // !!! Question: should this reflect those actually available in this copy of ICU, or 
967         // the list of types we assume is available?
968         // String[] calTypes = Calendar.getAvailableTypes();
969         final String[] calTypes = {
970             "buddhist", "chinese", "coptic", "ethiopic", "gregorian", "hebrew", 
971             "islamic", "islamic-civil", "japanese", "roc"
972         };
973         
974         // constructing a DateFormat with a locale indicating a calendar type should construct a
975         // date format appropriate to that calendar
976         final Date time = new Date();
977         for (int i = 0; i < calTypes.length; ++i) {
978             ULocale aLoc = loc.setKeywordValue("calendar", calTypes[i]);
979             logln("locale: " + aLoc);
980
981             DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL,
982                                                            DateFormat.FULL,
983                                                            aLoc);
984
985             logln("df type: " + df.getClass().getName() + " loc: " + df.getLocale(ULocale.VALID_LOCALE));
986
987             Calendar cal = df.getCalendar();
988             assertEquals("calendar types", cal.getType(), calTypes[i]);
989             DateFormat df2 = cal.getDateTimeFormat(DateFormat.FULL, DateFormat.FULL, ULocale.US);
990             logln("df2 type: " + df2.getClass().getName() + " loc: " + df2.getLocale(ULocale.VALID_LOCALE));
991             assertEquals("format results", df.format(time), df2.format(time));
992         }
993
994         // dateFormat.setCalendar should throw exception if wrong format for calendar
995         if (false) {
996             DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, 
997                                                            DateFormat.FULL, 
998                                                            new ULocale("en_US@calendar=chinese"));
999
1000             logln("dateformat type: " + df.getClass().getName());
1001
1002             Calendar cal = Calendar.getInstance(new ULocale("en_US@calendar=chinese"));
1003                                                 
1004             logln("calendar type: " + cal.getClass().getName());
1005         }
1006     }
1007
1008     public void TestTypes() {
1009         String[] locs = {
1010                 "en_US_VALLEYGIRL",
1011                 "en_US_VALLEYGIRL@collation=phonebook;calendar=japanese",
1012                 "en_US_VALLEYGIRL@collation=phonebook;calendar=gregorian",
1013                 "ja_JP@calendar=japanese",
1014                 "th_TH@calendar=buddhist",
1015                 "th-TH-u-ca-gregory",
1016                 "ja_JP_TRADITIONAL",
1017                 "th_TH_TRADITIONAL",
1018                 "th_TH_TRADITIONAL@calendar=gregorian",
1019                 "en_US",
1020                 "th_TH",    // Default calendar for th_TH is buddhist
1021                 "th",       // th's default region is TH and buddhist is used as default for TH
1022                 "en_TH",    // Default calendar for any locales with region TH is buddhist
1023                 "th_TH@calendar=iso8601",   // iso8601 calendar type
1024         };
1025
1026         String[] types = {
1027                 "gregorian",
1028                 "japanese",
1029                 "gregorian",
1030                 "japanese",
1031                 "buddhist",
1032                 "gregorian",
1033                 "japanese",
1034                 "buddhist",
1035                 "gregorian",
1036                 "gregorian",
1037                 "buddhist",
1038                 "buddhist",
1039                 "buddhist",
1040                 "gregorian",    // iso8601 is a gregiran sub type
1041         };
1042
1043         for (int i = 0; i < locs.length; i++) {
1044             Calendar cal = Calendar.getInstance(new ULocale(locs[i]));
1045             if (!cal.getType().equals(types[i])) {
1046                 errln(locs[i] + " Calendar type " + cal.getType() + " instead of " + types[i]);
1047             }
1048         }
1049     }
1050
1051     public void TestISO8601() {
1052         final ULocale[] TEST_LOCALES = {
1053             new ULocale("en_US@calendar=iso8601"),
1054             new ULocale("en_US@calendar=Iso8601"),
1055             new ULocale("th_TH@calendar=iso8601"),
1056             new ULocale("ar_EG@calendar=iso8601")
1057         };
1058
1059         final int[][] TEST_DATA = {
1060             // {<year>, <week# of Jan 1>, <week# year of Jan 1>}
1061             {2008, 1, 2008},
1062             {2009, 1, 2009},
1063             {2010, 53, 2009},
1064             {2011, 52, 2010},
1065             {2012, 52, 2011},
1066             {2013, 1, 2013},
1067             {2014, 1, 2014},
1068         };
1069
1070         for (ULocale locale : TEST_LOCALES) {
1071             Calendar cal = Calendar.getInstance(locale);
1072             // No matter what locale is used, if calendar type is "iso8601",
1073             // calendar type must be Gregorian
1074             if (!cal.getType().equals("gregorian")) {
1075                 errln("Error: Gregorian calendar is not used for locale: " + locale);
1076             }
1077
1078             for (int[] data : TEST_DATA) {
1079                 cal.set(data[0], Calendar.JANUARY, 1);
1080                 int weekNum = cal.get(Calendar.WEEK_OF_YEAR);
1081                 int weekYear = cal.get(Calendar.YEAR_WOY);
1082
1083                 if (weekNum != data[1] || weekYear != data[2]) {
1084                     errln("Error: Incorrect week of year on January 1st, " + data[0]
1085                             + " for locale " + locale
1086                             + ": Returned [weekNum=" + weekNum + ", weekYear=" + weekYear
1087                             + "], Expected [weekNum=" + data[1] + ", weekYear=" + data[2] + "]");
1088                 }
1089             }
1090         }
1091     }
1092
1093     private static class CalFields {
1094         private int year;
1095         private int month;
1096         private int day;
1097         private int hour;
1098         private int min;
1099         private int sec;
1100
1101         CalFields(int year, int month, int day, int hour, int min, int sec) {
1102             this.year = year;
1103             this.month = month;
1104             this.day = day;
1105             this.hour = hour;
1106             this.min = min;
1107             this.sec = sec;
1108         }
1109
1110         void setTo(Calendar cal) {
1111             cal.clear();
1112             cal.set(year,  month - 1, day, hour, min, sec);
1113         }
1114
1115         public String toString() {
1116             return String.format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec);
1117         }
1118
1119         public boolean equals(Object other) {
1120             if (other instanceof CalFields) {
1121                 CalFields otr = (CalFields)other;
1122                 return (year == otr.year
1123                         && month == otr.month
1124                         && day == otr.day
1125                         && hour == otr.hour
1126                         && min == otr.min
1127                         && sec == otr.sec);
1128             }
1129             return false;
1130         }
1131  
1132         static CalFields createFrom(Calendar cal) {
1133             int year = cal.get(Calendar.YEAR);
1134             int month = cal.get(Calendar.MONTH) + 1;
1135             int day = cal.get(Calendar.DAY_OF_MONTH);
1136             int hour = cal.get(Calendar.HOUR_OF_DAY);
1137             int min = cal.get(Calendar.MINUTE);
1138             int sec = cal.get(Calendar.SECOND);
1139
1140             return new CalFields(year, month, day, hour, min, sec);
1141         }
1142     }
1143
1144     public void TestAmbiguousWallTimeAPIs() {
1145         Calendar cal = Calendar.getInstance();
1146
1147         assertEquals("Default repeated wall time option", cal.getRepeatedWallTimeOption(), Calendar.WALLTIME_LAST);
1148         assertEquals("Default skipped wall time option", cal.getSkippedWallTimeOption(), Calendar.WALLTIME_LAST);
1149
1150         Calendar cal2 = (Calendar)cal.clone();
1151
1152         assertTrue("Equality", cal2.equals(cal));
1153         assertTrue("Hash code", cal.hashCode() == cal2.hashCode());
1154
1155         cal2.setRepeatedWallTimeOption(Calendar.WALLTIME_FIRST);
1156         cal2.setSkippedWallTimeOption(Calendar.WALLTIME_FIRST);
1157
1158         assertFalse("Equality after mod", cal2.equals(cal));
1159         assertFalse("Hash code after mod", cal.hashCode() == cal2.hashCode());
1160
1161         assertEquals("getRepeatedWallTimeOption after mod", cal2.getRepeatedWallTimeOption(), Calendar.WALLTIME_FIRST);
1162         assertEquals("getSkippedWallTimeOption after mod", cal2.getSkippedWallTimeOption(), Calendar.WALLTIME_FIRST);
1163
1164         try {
1165             cal.setRepeatedWallTimeOption(Calendar.WALLTIME_NEXT_VALID);
1166             errln("IAE expected on setRepeatedWallTimeOption(WALLTIME_NEXT_VALID");
1167         } catch (IllegalArgumentException e) {
1168             // expected
1169         }
1170     }
1171
1172     public void TestRepeatedWallTime() {
1173         final Object[][] TESTDATA = {
1174             // Time zone            Input wall time                     WALLTIME_LAST in GMT                WALLTIME_FIRST in GMT
1175             {"America/New_York",    new CalFields(2011,11,6,0,59,59),   new CalFields(2011,11,6,4,59,59),   new CalFields(2011,11,6,4,59,59)},
1176             {"America/New_York",    new CalFields(2011,11,6,1,0,0),     new CalFields(2011,11,6,6,0,0),     new CalFields(2011,11,6,5,0,0)},
1177             {"America/New_York",    new CalFields(2011,11,6,1,0,1),     new CalFields(2011,11,6,6,0,1),     new CalFields(2011,11,6,5,0,1)},
1178             {"America/New_York",    new CalFields(2011,11,6,1,30,0),    new CalFields(2011,11,6,6,30,0),    new CalFields(2011,11,6,5,30,0)},
1179             {"America/New_York",    new CalFields(2011,11,6,1,59,59),   new CalFields(2011,11,6,6,59,59),   new CalFields(2011,11,6,5,59,59)},
1180             {"America/New_York",    new CalFields(2011,11,6,2,0,0),     new CalFields(2011,11,6,7,0,0),     new CalFields(2011,11,6,7,0,0)},
1181             {"America/New_York",    new CalFields(2011,11,6,2,0,1),     new CalFields(2011,11,6,7,0,1),     new CalFields(2011,11,6,7,0,1)},
1182
1183             {"Australia/Lord_Howe", new CalFields(2011,4,3,1,29,59),    new CalFields(2011,4,2,14,29,59),   new CalFields(2011,4,2,14,29,59)},
1184             {"Australia/Lord_Howe", new CalFields(2011,4,3,1,30,0),     new CalFields(2011,4,2,15,0,0),     new CalFields(2011,4,2,14,30,0)},
1185             {"Australia/Lord_Howe", new CalFields(2011,4,3,1,45,0),     new CalFields(2011,4,2,15,15,0),    new CalFields(2011,4,2,14,45,0)},
1186             {"Australia/Lord_Howe", new CalFields(2011,4,3,1,59,59),    new CalFields(2011,4,2,15,29,59),   new CalFields(2011,4,2,14,59,59)},
1187             {"Australia/Lord_Howe", new CalFields(2011,4,3,2,0,0),      new CalFields(2011,4,2,15,30,0),    new CalFields(2011,4,2,15,30,0)},
1188             {"Australia/Lord_Howe", new CalFields(2011,4,3,2,0,1),      new CalFields(2011,4,2,15,30,1),    new CalFields(2011,4,2,15,30,1)},
1189         };
1190
1191         Calendar calGMT = Calendar.getInstance(TimeZone.GMT_ZONE);
1192
1193         Calendar calDefault = Calendar.getInstance();
1194         Calendar calLast = Calendar.getInstance();
1195         Calendar calFirst = Calendar.getInstance();
1196
1197         calFirst.setRepeatedWallTimeOption(Calendar.WALLTIME_FIRST);
1198         calLast.setRepeatedWallTimeOption(Calendar.WALLTIME_LAST);
1199
1200         for (Object[] test : TESTDATA) {
1201             String tzid = (String)test[0];
1202             TimeZone tz = TimeZone.getTimeZone(tzid);
1203             CalFields in = (CalFields)test[1];
1204             CalFields expLastGMT = (CalFields)test[2];
1205             CalFields expFirstGMT = (CalFields)test[3];
1206
1207             // WALLTIME_LAST
1208             calLast.setTimeZone(tz);
1209             in.setTo(calLast);
1210             calGMT.setTimeInMillis(calLast.getTimeInMillis());
1211             CalFields outLastGMT = CalFields.createFrom(calGMT);
1212             if (!outLastGMT.equals(expLastGMT)) {
1213                 errln("Fail: WALLTIME_LAST " + in + "[" + tzid + "] is parsed as " + outLastGMT + "[GMT]. Expected: " + expLastGMT + "[GMT]");
1214             }
1215
1216             // default
1217             calDefault.setTimeZone(tz);
1218             in.setTo(calDefault);
1219             calGMT.setTimeInMillis(calDefault.getTimeInMillis());
1220             CalFields outDefGMT = CalFields.createFrom(calGMT);
1221             if (!outDefGMT.equals(expLastGMT)) {
1222                 errln("Fail: (default) " + in + "[" + tzid + "] is parsed as " + outDefGMT + "[GMT]. Expected: " + expLastGMT + "[GMT]");
1223             }
1224
1225             // WALLTIME_FIRST
1226             calFirst.setTimeZone(tz);
1227             in.setTo(calFirst);
1228             calGMT.setTimeInMillis(calFirst.getTimeInMillis());
1229             CalFields outFirstGMT = CalFields.createFrom(calGMT);
1230             if (!outFirstGMT.equals(expFirstGMT)) {
1231                 errln("Fail: WALLTIME_FIRST " + in + "[" + tzid + "] is parsed as " + outFirstGMT + "[GMT]. Expected: " + expFirstGMT + "[GMT]");
1232             }
1233         }
1234     }
1235
1236     public void TestSkippedWallTime() {
1237         final Object[][] TESTDATA = {
1238             // Time zone            Input wall time                     Valid wall time?
1239             {"America/New_York",    new CalFields(2011,3,13,1,59,59),   true,
1240                 //  WALLTIME_LAST in GMT                WALLTIME_FIRST in GMT           WALLTIME_NEXT_VALID in GMT
1241                 new CalFields(2011,3,13,6,59,59),   new CalFields(2011,3,13,6,59,59),   new CalFields(2011,3,13,6,59,59)},
1242
1243             {"America/New_York",    new CalFields(2011,3,13,2,0,0),     false,
1244                 new CalFields(2011,3,13,7,0,0),     new CalFields(2011,3,13,6,0,0),     new CalFields(2011,3,13,7,0,0)},
1245
1246             {"America/New_York",    new CalFields(2011,3,13,2,1,0),     false,
1247                 new CalFields(2011,3,13,7,1,0),     new CalFields(2011,3,13,6,1,0),     new CalFields(2011,3,13,7,0,0)},
1248
1249             {"America/New_York",    new CalFields(2011,3,13,2,30,0),    false,
1250                 new CalFields(2011,3,13,7,30,0),    new CalFields(2011,3,13,6,30,0),    new CalFields(2011,3,13,7,0,0)},
1251
1252             {"America/New_York",    new CalFields(2011,3,13,2,59,59),   false,
1253                 new CalFields(2011,3,13,7,59,59),   new CalFields(2011,3,13,6,59,59),   new CalFields(2011,3,13,7,0,0)},
1254
1255             {"America/New_York",    new CalFields(2011,3,13,3,0,0),     true,
1256                 new CalFields(2011,3,13,7,0,0),     new CalFields(2011,3,13,7,0,0),     new CalFields(2011,3,13,7,0,0)},
1257
1258             {"Pacific/Apia",        new CalFields(2011,12,29,23,59,59), true,
1259                 new CalFields(2011,12,30,9,59,59),  new CalFields(2011,12,30,9,59,59),  new CalFields(2011,12,30,9,59,59)},
1260
1261             {"Pacific/Apia",        new CalFields(2011,12,30,0,0,0),    false,
1262                 new CalFields(2011,12,30,10,0,0),  new CalFields(2011,12,29,10,0,0),  new CalFields(2011,12,30,10,0,0)},
1263
1264             {"Pacific/Apia",        new CalFields(2011,12,30,12,0,0),   false,
1265                 new CalFields(2011,12,30,22,0,0),  new CalFields(2011,12,29,22,0,0),  new CalFields(2011,12,30,10,0,0)},
1266
1267             {"Pacific/Apia",        new CalFields(2011,12,30,23,59,59), false,
1268                 new CalFields(2011,12,31,9,59,59), new CalFields(2011,12,30,9,59,59), new CalFields(2011,12,30,10,0,0)},
1269
1270             {"Pacific/Apia",        new CalFields(2011,12,31,0,0,0),    true,
1271                 new CalFields(2011,12,30,10,0,0),  new CalFields(2011,12,30,10,0,0),  new CalFields(2011,12,30,10,0,0)},
1272         };
1273
1274         Calendar calGMT = Calendar.getInstance(TimeZone.GMT_ZONE);
1275
1276         Calendar calDefault = Calendar.getInstance();
1277         Calendar calLast = Calendar.getInstance();
1278         Calendar calFirst = Calendar.getInstance();
1279         Calendar calNextAvail = Calendar.getInstance();
1280
1281         calLast.setSkippedWallTimeOption(Calendar.WALLTIME_LAST);
1282         calFirst.setSkippedWallTimeOption(Calendar.WALLTIME_FIRST);
1283         calNextAvail.setSkippedWallTimeOption(Calendar.WALLTIME_NEXT_VALID);
1284
1285         for (Object[] test : TESTDATA) {
1286             String tzid = (String)test[0];
1287             TimeZone tz = TimeZone.getTimeZone(tzid);
1288             CalFields in = (CalFields)test[1];
1289             boolean isValid = (Boolean)test[2];
1290             CalFields expLastGMT = (CalFields)test[3];
1291             CalFields expFirstGMT = (CalFields)test[4];
1292             CalFields expNextAvailGMT = (CalFields)test[5];
1293
1294             for (int i = 0; i < 2; i++) {
1295                 boolean bLenient = (i == 0);
1296
1297                 // WALLTIME_LAST
1298                 calLast.setLenient(bLenient);
1299                 calLast.setTimeZone(tz);
1300                 try {
1301                     in.setTo(calLast);
1302                     calGMT.setTimeInMillis(calLast.getTimeInMillis());
1303                     CalFields outLastGMT = CalFields.createFrom(calGMT);
1304                     if (!bLenient && !isValid) {
1305                         errln("Fail: IllegalArgumentException expected - " + in + "[" + tzid + "] (WALLTIME_LAST)");
1306                     } else if (!outLastGMT.equals(expLastGMT)) {
1307                         errln("Fail: WALLTIME_LAST " + in + "[" + tzid + "] is parsed as " + outLastGMT + "[GMT]. Expected: " + expLastGMT + "[GMT]");
1308                     }
1309                 } catch (IllegalArgumentException e) {
1310                     if (bLenient || isValid) {
1311                         errln("Fail: Unexpected IllegalArgumentException - " + in + "[" + tzid + "] (WALLTIME_LAST)");
1312                     }
1313                 }
1314
1315                 // default
1316                 calDefault.setLenient(bLenient);
1317                 calDefault.setTimeZone(tz);
1318                 try {
1319                     in.setTo(calDefault);
1320                     calGMT.setTimeInMillis(calDefault.getTimeInMillis());
1321                     CalFields outDefGMT = CalFields.createFrom(calGMT);
1322                     if (!bLenient && !isValid) {
1323                         errln("Fail: IllegalArgumentException expected - " + in + "[" + tzid + "] (default)");
1324                     } else if (!outDefGMT.equals(expLastGMT)) {
1325                         errln("Fail: (default) " + in + "[" + tzid + "] is parsed as " + outDefGMT + "[GMT]. Expected: " + expLastGMT + "[GMT]");
1326                     }
1327                 } catch (IllegalArgumentException e) {
1328                     if (bLenient || isValid) {
1329                         errln("Fail: Unexpected IllegalArgumentException - " + in + "[" + tzid + "] (default)");
1330                     }
1331                 }
1332
1333                 // WALLTIME_FIRST
1334                 calFirst.setLenient(bLenient);
1335                 calFirst.setTimeZone(tz);
1336                 try {
1337                     in.setTo(calFirst);
1338                     calGMT.setTimeInMillis(calFirst.getTimeInMillis());
1339                     CalFields outFirstGMT = CalFields.createFrom(calGMT);
1340                     if (!bLenient && !isValid) {
1341                         errln("Fail: IllegalArgumentException expected - " + in + "[" + tzid + "] (WALLTIME_FIRST)");
1342                     } else if (!outFirstGMT.equals(expFirstGMT)) {
1343                         errln("Fail: WALLTIME_FIRST " + in + "[" + tzid + "] is parsed as " + outFirstGMT + "[GMT]. Expected: " + expFirstGMT + "[GMT]");
1344                     }
1345                 } catch (IllegalArgumentException e) {
1346                     if (bLenient || isValid) {
1347                         errln("Fail: Unexpected IllegalArgumentException - " + in + "[" + tzid + "] (WALLTIME_FIRST)");
1348                     }
1349                 }
1350
1351                 // WALLTIME_NEXT_VALID
1352                 calNextAvail.setLenient(bLenient);
1353                 calNextAvail.setTimeZone(tz);
1354                 try {
1355                     in.setTo(calNextAvail);
1356                     calGMT.setTimeInMillis(calNextAvail.getTimeInMillis());
1357                     CalFields outNextAvailGMT = CalFields.createFrom(calGMT);
1358                     if (!bLenient && !isValid) {
1359                         errln("Fail: IllegalArgumentException expected - " + in + "[" + tzid + "] (WALLTIME_NEXT_VALID)");
1360                     } else if (!outNextAvailGMT.equals(expNextAvailGMT)) {
1361                         errln("Fail: WALLTIME_NEXT_VALID " + in + "[" + tzid + "] is parsed as " + outNextAvailGMT + "[GMT]. Expected: " + expNextAvailGMT + "[GMT]");
1362                     }
1363                 } catch (IllegalArgumentException e) {
1364                     if (bLenient || isValid) {
1365                         errln("Fail: Unexpected IllegalArgumentException - " + in + "[" + tzid + "] (WALLTIME_NEXT_VALID)");
1366                     }
1367                 }
1368             }
1369         }
1370     }
1371
1372     public void TestFieldDifference() {
1373         class TFDItem {
1374             public String tzname;
1375             public String locale;
1376             public long start;
1377             public long target;
1378             public boolean progressive; // true to compute progressive difference for each field, false to reset calendar after each call
1379             int yDiff;
1380             int MDiff;
1381             int dDiff;
1382             int HDiff;
1383             int mDiff;
1384             int sDiff; // 0x7FFFFFFF indicates overflow error expected
1385              // Simple constructor
1386             public TFDItem(String tz, String loc, long st, long tg, boolean prg, int yD, int MD, int dD, int HD, int mD, int sD ) {
1387                 tzname = tz;
1388                 locale = loc;
1389                 start = st;
1390                 target = tg;
1391                 progressive = prg;
1392                 yDiff = yD;
1393                 MDiff = MD;
1394                 dDiff = dD;
1395                 HDiff = HD;
1396                 mDiff = mD;
1397                 sDiff = sD;
1398             }
1399         };
1400         final TFDItem[] tfdItems = {
1401             //           timezobe      locale        start            target            prog   yDf  MDf    dDf     HDf       mDf         sDf
1402             // For these we compute the progressive difference for each field - not resetting the calendar after each call
1403             new TFDItem( "US/Pacific", "en_US",        1267459800000L,  1277772600000L, true,    0,   3,    27,      9,       40,          0 ), // 2010-Mar-01 08:10 -> 2010-Jun-28 17:50
1404             new TFDItem( "US/Pacific", "en_US",        1267459800000L,  1299089280000L, true,    1,   0,     1,      1,       58,          0 ), // 2010-Mar-01 08:10 -> 2011-Mar-02 10:08
1405             // For these we compute the total difference for each field - resetting the calendar after each call
1406             new TFDItem( "GMT",        "en_US",        0,               1073692800000L, false,  34, 408, 12427, 298248, 17894880, 1073692800 ), // 1970-Jan-01 00:00 -> 2004-Jan-10 00:00
1407             new TFDItem( "GMT",        "en_US",        0,               1073779200000L, false,  34, 408, 12428, 298272, 17896320, 1073779200 ), // 1970-Jan-01 00:00 -> 2004-Jan-11 00:00
1408             new TFDItem( "GMT",        "en_US",        0,               2147472000000L, false,  68, 816, 24855, 596520, 35791200, 2147472000 ), // 1970-Jan-01 00:00 -> 2038-Jan-19 00:00
1409 //          new TFDItem( "GMT",        "en_US",        0,               2147558400000L, false,  68, 816, 24856, 596544, 35792640, 0x7FFFFFFF ), // 1970-Jan-01 00:00 -> 2038-Jan-20 00:00, seconds overflow => exception in ICU4J
1410             new TFDItem( "GMT",        "en_US",        0,              -1073692800000L, false, -34,-408,-12427,-298248,-17894880,-1073692800 ), // 1970-Jan-01 00:00 -> 1935-Dec-24 00:00
1411             new TFDItem( "GMT",        "en_US",        0,              -1073779200000L, false, -34,-408,-12428,-298272,-17896320,-1073779200 ), // 1970-Jan-01 00:00 -> 1935-Dec-23 00:00
1412             // check fwd/backward on either side of era boundary and across era boundary
1413             new TFDItem( "GMT",        "en_US",      -61978089600000L,-61820409600000L, false,   4,  59,  1825,  43800,  2628000,  157680000 ), // CE   5-Dec-31 00:00 -> CE  10-Dec-30 00:00
1414             new TFDItem( "GMT",        "en_US",      -61820409600000L,-61978089600000L, false,  -4, -59, -1825, -43800, -2628000, -157680000 ), // CE  10-Dec-30 00:00 -> CE   5-Dec-31 00:00
1415             new TFDItem( "GMT",        "en_US",      -62451129600000L,-62293449600000L, false,   4,  59,  1825,  43800,  2628000,  157680000 ), // BCE 10-Jan-04 00:00 -> BCE  5-Jan-03 00:00
1416             new TFDItem( "GMT",        "en_US",      -62293449600000L,-62451129600000L, false,  -4, -59, -1825, -43800, -2628000, -157680000 ), // BCE  5-Jan-03 00:00 -> BCE 10-Jan-04 00:00
1417             new TFDItem( "GMT",        "en_US",      -62293449600000L,-61978089600000L, false,   9, 119,  3650,  87600,  5256000,  315360000 ), // BCE  5-Jan-03 00:00 -> CE   5-Dec-31 00:00
1418             new TFDItem( "GMT",        "en_US",      -61978089600000L,-62293449600000L, false,  -9,-119, -3650, -87600, -5256000, -315360000 ), // CE   5-Dec-31 00:00 -> BCE  5-Jan-03 00:00
1419             new TFDItem( "GMT", "en@calendar=roc",    -1672704000000L, -1515024000000L, false,   4,  59,  1825,  43800,  2628000,  157680000 ), // MG   5-Dec-30 00:00 -> MG  10-Dec-29 00:00
1420             new TFDItem( "GMT", "en@calendar=roc",    -1515024000000L, -1672704000000L, false,  -4, -59, -1825, -43800, -2628000, -157680000 ), // MG  10-Dec-29 00:00 -> MG   5-Dec-30 00:00
1421             new TFDItem( "GMT", "en@calendar=roc",    -2145744000000L, -1988064000000L, false,   4,  59,  1825,  43800,  2628000,  157680000 ), // BMG 10-Jan-03 00:00 -> BMG  5-Jan-02 00:00
1422             new TFDItem( "GMT", "en@calendar=roc",    -1988064000000L, -2145744000000L, false,  -4, -59, -1825, -43800, -2628000, -157680000 ), // BMG  5-Jan-02 00:00 -> BMG 10-Jan-03 00:00
1423             new TFDItem( "GMT", "en@calendar=roc",    -1988064000000L, -1672704000000L, false,   9, 119,  3650,  87600,  5256000,  315360000 ), // BMG  5-Jan-02 00:00 -> MG   5-Dec-30 00:00
1424             new TFDItem( "GMT", "en@calendar=roc",    -1672704000000L, -1988064000000L, false,  -9,-119, -3650, -87600, -5256000, -315360000 ), // MG   5-Dec-30 00:00 -> BMG  5-Jan-02 00:00
1425             new TFDItem( "GMT", "en@calendar=coptic",-53026531200000L,-52868851200000L, false,   4,  64,  1825,  43800,  2628000,  157680000 ), // Er1  5-Nas-05 00:00 -> Er1 10-Nas-04 00:00
1426             new TFDItem( "GMT", "en@calendar=coptic",-52868851200000L,-53026531200000L, false,  -4, -64, -1825, -43800, -2628000, -157680000 ), // Er1 10-Nas-04 00:00 -> Er1  5-Nas-05 00:00
1427             new TFDItem( "GMT", "en@calendar=coptic",-53499571200000L,-53341891200000L, false,   4,  64,  1825,  43800,  2628000,  157680000 ), // Er0 10-Tou-04 00:00 -> Er0  5-Tou-02 00:00
1428             new TFDItem( "GMT", "en@calendar=coptic",-53341891200000L,-53499571200000L, false,  -4, -64, -1825, -43800, -2628000, -157680000 ), // Er0  5-Tou-02 00:00 -> Er0 10-Tou-04 00:00
1429             new TFDItem( "GMT", "en@calendar=coptic",-53341891200000L,-53026531200000L, false,   9, 129,  3650,  87600,  5256000,  315360000 ), // Er0  5-Tou-02 00:00 -> Er1  5-Nas-05 00:00
1430             new TFDItem( "GMT", "en@calendar=coptic",-53026531200000L,-53341891200000L, false,  -9,-129, -3650, -87600, -5256000, -315360000 ), // Er1  5-Nas-05 00:00 -> Er0  5-Tou-02 00:00
1431         };
1432         for (TFDItem tfdItem: tfdItems) {
1433             TimeZone timezone = TimeZone.getFrozenTimeZone(tfdItem.tzname);
1434             Calendar ucal = Calendar.getInstance(timezone, new ULocale(tfdItem.locale));
1435             ucal.setTimeInMillis(tfdItem.target);
1436             Date targetDate = ucal.getTime();
1437             int yDf, MDf, dDf, HDf, mDf, sDf;
1438             if (tfdItem.progressive) {
1439                 ucal.setTimeInMillis(tfdItem.start);
1440                 yDf = ucal.fieldDifference(targetDate, YEAR);
1441                 MDf = ucal.fieldDifference(targetDate, MONTH);
1442                 dDf = ucal.fieldDifference(targetDate, DATE);
1443                 HDf = ucal.fieldDifference(targetDate, HOUR);
1444                 mDf = ucal.fieldDifference(targetDate, MINUTE);
1445                 sDf = ucal.fieldDifference(targetDate, SECOND);
1446                 if ( yDf != tfdItem.yDiff || MDf != tfdItem.MDiff || dDf != tfdItem.dDiff || HDf != tfdItem.HDiff || mDf != tfdItem.mDiff || sDf != tfdItem.sDiff ) {
1447                     errln("Fail: for locale \"" + tfdItem.locale + "\", start " + tfdItem.start + ", target " +  tfdItem.target + ", expected y-M-d-H-m-s progressive diffs " +
1448                             tfdItem.yDiff +","+ tfdItem.MDiff +","+ tfdItem.dDiff +","+ tfdItem.HDiff +","+ tfdItem.mDiff +","+ tfdItem.sDiff + ", got " +
1449                             yDf +","+ MDf +","+ dDf +","+ HDf +","+ mDf +","+ sDf);
1450                 }
1451             } else {
1452                 ucal.setTimeInMillis(tfdItem.start);
1453                 yDf = ucal.fieldDifference(targetDate, YEAR);
1454                 ucal.setTimeInMillis(tfdItem.start);
1455                 MDf = ucal.fieldDifference(targetDate, MONTH);
1456                 ucal.setTimeInMillis(tfdItem.start);
1457                 dDf = ucal.fieldDifference(targetDate, DATE);
1458                 ucal.setTimeInMillis(tfdItem.start);
1459                 HDf = ucal.fieldDifference(targetDate, HOUR);
1460                 ucal.setTimeInMillis(tfdItem.start);
1461                 mDf = ucal.fieldDifference(targetDate, MINUTE);
1462                 if ( yDf != tfdItem.yDiff || MDf != tfdItem.MDiff || dDf != tfdItem.dDiff || HDf != tfdItem.HDiff || mDf != tfdItem.mDiff ) {
1463                     errln("Fail: for locale \"" + tfdItem.locale + "\", start " + tfdItem.start + ", target " +  tfdItem.target + ", expected y-M-d-H-m total diffs " +
1464                             tfdItem.yDiff +","+ tfdItem.MDiff +","+ tfdItem.dDiff +","+ tfdItem.HDiff +","+ tfdItem.mDiff + ", got " +
1465                             yDf +","+ MDf +","+ dDf +","+ HDf +","+ mDf);
1466                 }
1467                 ucal.setTimeInMillis(tfdItem.start);
1468                 sDf = ucal.fieldDifference(targetDate, SECOND);
1469                 if ( sDf != 0x7FFFFFFF && sDf != tfdItem.sDiff ) {
1470                     errln("Fail: for locale \"" + tfdItem.locale + "\", start " + tfdItem.start + ", target " +  tfdItem.target + ", expected seconds total diffs " +
1471                             tfdItem.sDiff + ", got " + sDf);
1472                 }
1473             }
1474         }
1475     }
1476
1477     public void TestAddRollEra0AndEraBounds() {
1478         final String[] localeIDs = {
1479             // calendars with non-modern era 0 that goes backwards, max era == 1
1480             "en@calendar=gregorian",
1481             "en@calendar=roc",
1482             "en@calendar=coptic",
1483             // calendars with non-modern era 0 that goes forwards, max era > 1
1484             "en@calendar=japanese",
1485             "en@calendar=chinese",
1486             // calendars with non-modern era 0 that goes forwards, max era == 1
1487             "en@calendar=ethiopic",
1488             // calendars with only one era  = 0, forwards
1489             "en@calendar=buddhist",
1490             "en@calendar=hebrew",
1491             "en@calendar=islamic",
1492             "en@calendar=indian",
1493             //"en@calendar=persian", // no persian calendar in ICU4J yet
1494             "en@calendar=ethiopic-amete-alem",
1495         };
1496         TimeZone zoneGMT = TimeZone.getFrozenTimeZone("GMT");
1497         for (String localeID : localeIDs) {
1498             Calendar ucalTest = Calendar.getInstance(zoneGMT, new ULocale(localeID));
1499             String calType = ucalTest.getType();
1500             boolean era0YearsGoBackwards = (calType.equals("gregorian") || calType.equals("roc") || calType.equals("coptic"));
1501             int yrBefore, yrAfter, yrMax, eraAfter, eraMax, eraNow;
1502             
1503             ucalTest.clear();
1504             ucalTest.set(Calendar.YEAR, 2);
1505             ucalTest.set(Calendar.ERA, 0);
1506             yrBefore = ucalTest.get(Calendar.YEAR);
1507             ucalTest.add(Calendar.YEAR, 1);
1508             yrAfter = ucalTest.get(Calendar.YEAR);
1509             if ( (era0YearsGoBackwards && yrAfter>yrBefore) || (!era0YearsGoBackwards && yrAfter<yrBefore) ) {
1510                 errln("Fail: era 0 add 1 year does not move forward in time for " + localeID);
1511             }
1512
1513             ucalTest.clear();
1514             ucalTest.set(Calendar.YEAR, 2);
1515             ucalTest.set(Calendar.ERA, 0);
1516             yrBefore = ucalTest.get(Calendar.YEAR);
1517             ucalTest.roll(Calendar.YEAR, 1);
1518             yrAfter = ucalTest.get(Calendar.YEAR);
1519             if ( (era0YearsGoBackwards && yrAfter>yrBefore) || (!era0YearsGoBackwards && yrAfter<yrBefore) ) {
1520                 errln("Fail: era 0 roll 1 year does not move forward in time for " + localeID);
1521             }
1522
1523             ucalTest.clear();
1524             ucalTest.set(Calendar.YEAR, 1);
1525             ucalTest.set(Calendar.ERA, 0);
1526             if (era0YearsGoBackwards) {
1527                 ucalTest.roll(Calendar.YEAR, 1);
1528                 yrAfter = ucalTest.get(Calendar.YEAR);
1529                 eraAfter = ucalTest.get(Calendar.ERA);
1530                 if (eraAfter != 0 || yrAfter != 1) {
1531                     errln("Fail: era 0 roll 1 year from year 1 does not stay within era or pin to year 1 for "
1532                             + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1533                 }
1534             } else {
1535                 // roll backward in time to where era 0 years go negative, except for the Chinese
1536                 // calendar, which uses negative eras instead of having years outside the range 1-60
1537                 ucalTest.roll(Calendar.YEAR, -2);
1538                 yrAfter = ucalTest.get(Calendar.YEAR);
1539                 eraAfter = ucalTest.get(Calendar.ERA);
1540                 if ( !calType.equals("chinese") && (eraAfter != 0 || yrAfter != -1) ) {
1541                     errln("Fail: era 0 roll -2 years from year 1 does not stay within era or produce year -1 for "
1542                             + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1543                 }
1544             }
1545
1546             ucalTest.clear();
1547             {
1548                 int eraMin = ucalTest.getMinimum(Calendar.ERA);
1549                 if (eraMin != 0 && calType.compareTo("chinese") != 0) {
1550                     errln("Fail: getMinimum returns minimum era " + eraMin + " (should be 0) for calType " + calType);
1551                 }
1552             }
1553
1554             ucalTest.clear();
1555             ucalTest.set(Calendar.YEAR, 1);
1556             ucalTest.set(Calendar.ERA, 0);
1557             eraMax = ucalTest.getMaximum(Calendar.ERA);
1558             if (eraMax > 0) {
1559                 // try similar tests for era 1 (if calendar has it), in which years always go forward
1560
1561                 ucalTest.clear();
1562                 ucalTest.set(Calendar.YEAR, 2);
1563                 ucalTest.set(Calendar.ERA, 1);
1564                 yrBefore = ucalTest.get(Calendar.YEAR);
1565                 ucalTest.add(Calendar.YEAR, 1);
1566                 yrAfter = ucalTest.get(Calendar.YEAR);
1567                 if ( yrAfter<yrBefore ) {
1568                     errln("Fail: era 1 add 1 year does not move forward in time for " + localeID);
1569                 }
1570     
1571                 ucalTest.clear();
1572                 ucalTest.set(Calendar.YEAR, 2);
1573                 ucalTest.set(Calendar.ERA, 1);
1574                 yrBefore = ucalTest.get(Calendar.YEAR);
1575                 ucalTest.roll(Calendar.YEAR, 1);
1576                 yrAfter = ucalTest.get(Calendar.YEAR);
1577                 if ( yrAfter<yrBefore ) {
1578                     errln("Fail: era 1 roll 1 year does not move forward in time for " + localeID);
1579                 }
1580
1581                 ucalTest.clear();
1582                 ucalTest.set(Calendar.YEAR, 1);
1583                 ucalTest.set(Calendar.ERA, 1);
1584                 yrMax = ucalTest.getActualMaximum(Calendar.YEAR);
1585                 ucalTest.roll(Calendar.YEAR, -1); // roll down which should pin or wrap to end
1586                 yrAfter = ucalTest.get(Calendar.YEAR);
1587                 eraAfter = ucalTest.get(Calendar.ERA);
1588                 // if yrMax is reasonable we should wrap to that, else we should pin at yr 1
1589                 if (yrMax >= 32768) {
1590                     if (eraAfter != 1 || yrAfter != 1) {
1591                         errln("Fail: era 1 roll -1 year from year 1 does not stay within era or pin to year 1 for "
1592                                 + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1593                     }
1594                 } else if (eraAfter != 1 || yrAfter != yrMax) {
1595                     errln("Fail: era 1 roll -1 year from year 1 does not stay within era or wrap to year "
1596                             + yrMax + " for " + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1597                 } else {
1598                     ucalTest.roll(Calendar.YEAR, 1); // now roll up which should wrap to beginning
1599                     yrAfter = ucalTest.get(Calendar.YEAR);
1600                     eraAfter = ucalTest.get(Calendar.ERA);
1601                     if (eraAfter != 1 || yrAfter != 1) {
1602                         errln("Fail: era 1 roll 1 year from year " + yrMax +
1603                                 " does not stay within era or wrap to year 1 for "
1604                                 + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1605                     }
1606                 }
1607                 
1608                 // if current era  > 1, try the same roll tests for current era
1609                 ucalTest.setTime(new Date());
1610                 eraNow = ucalTest.get(Calendar.ERA);
1611                 if (eraNow > 1) {
1612                     ucalTest.clear();
1613                     ucalTest.set(Calendar.YEAR, 1);
1614                     ucalTest.set(Calendar.ERA, eraNow);
1615                     yrMax = ucalTest.getActualMaximum(Calendar.YEAR); // max year value for this era
1616                     ucalTest.roll(Calendar.YEAR, -1);
1617                     yrAfter = ucalTest.get(Calendar.YEAR);
1618                     eraAfter = ucalTest.get(Calendar.ERA);
1619                     // if yrMax is reasonable we should wrap to that, else we should pin at yr 1
1620                     if (yrMax >= 32768) {
1621                         if (eraAfter != eraNow || yrAfter != 1) {
1622                             errln("Fail: era " + eraNow +
1623                                     " roll -1 year from year 1 does not stay within era or pin to year 1 for "
1624                                     + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1625                         }
1626                     } else if (eraAfter != eraNow || yrAfter != yrMax) {
1627                         errln("Fail: era " + eraNow +
1628                                 " roll -1 year from year 1 does not stay within era or wrap to year " + yrMax
1629                                 + " for " + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1630                     } else {
1631                         ucalTest.roll(Calendar.YEAR, 1); // now roll up which should wrap to beginning
1632                         yrAfter = ucalTest.get(Calendar.YEAR);
1633                         eraAfter = ucalTest.get(Calendar.ERA);
1634                         if (eraAfter != eraNow || yrAfter != 1) {
1635                             errln("Fail: era " + eraNow + " roll 1 year from year " + yrMax +
1636                                     " does not stay within era or wrap to year 1 for "
1637                                     + localeID + " (get era " + eraAfter + " year " + yrAfter + ")");
1638                         }
1639                     }
1640                 }
1641             }
1642         }
1643     }
1644
1645 }