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