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