xref: /aosp_15_r20/external/icu/icu4c/source/test/intltest/caltest.cpp (revision 0e209d3975ff4a8c132096b14b0e9364a753506e)
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /************************************************************************
4  * COPYRIGHT:
5  * Copyright (c) 1997-2016, International Business Machines Corporation
6  * and others. All Rights Reserved.
7  ************************************************************************/
8 #include "unicode/utypes.h"
9 
10 #if !UCONFIG_NO_FORMATTING
11 
12 #include "caltest.h"
13 #include "unicode/dtfmtsym.h"
14 #include "unicode/gregocal.h"
15 #include "unicode/localpointer.h"
16 #include "hebrwcal.h"
17 #include "unicode/smpdtfmt.h"
18 #include "unicode/simpletz.h"
19 #include "dbgutil.h"
20 #include "unicode/udat.h"
21 #include "unicode/ustring.h"
22 #include "cstring.h"
23 #include "unicode/localpointer.h"
24 #include "chnsecal.h"
25 #include "intltest.h"
26 #include "coptccal.h"
27 #include "ethpccal.h"
28 #include "islamcal.h"
29 
30 #define mkcstr(U) u_austrcpy(calloc(8, u_strlen(U) + 1), U)
31 
32 #define TEST_CHECK_STATUS UPRV_BLOCK_MACRO_BEGIN { \
33     if (U_FAILURE(status)) { \
34         if (status == U_MISSING_RESOURCE_ERROR) { \
35             dataerrln("%s:%d: Test failure.  status=%s", __FILE__, __LINE__, u_errorName(status)); \
36         } else { \
37             errln("%s:%d: Test failure.  status=%s", __FILE__, __LINE__, u_errorName(status)); \
38         } \
39         return; \
40     } \
41 } UPRV_BLOCK_MACRO_END
42 
43 #define TEST_CHECK_STATUS_LOCALE(testlocale) UPRV_BLOCK_MACRO_BEGIN { \
44     if (U_FAILURE(status)) { \
45         if (status == U_MISSING_RESOURCE_ERROR) { \
46             dataerrln("%s:%d: Test failure, locale %s.  status=%s", __FILE__, __LINE__, testlocale, u_errorName(status)); \
47         } else { \
48             errln("%s:%d: Test failure, locale %s.  status=%s", __FILE__, __LINE__, testlocale, u_errorName(status)); \
49         } \
50         return; \
51     } \
52 } UPRV_BLOCK_MACRO_END
53 
54 #define TEST_ASSERT(expr) UPRV_BLOCK_MACRO_BEGIN { \
55     if ((expr)==false) { \
56         errln("%s:%d: Test failure \n", __FILE__, __LINE__); \
57     } \
58 } UPRV_BLOCK_MACRO_END
59 
60 // *****************************************************************************
61 // class CalendarTest
62 // *****************************************************************************
63 
calToStr(const Calendar & cal)64 UnicodeString CalendarTest::calToStr(const Calendar & cal)
65 {
66   UnicodeString out;
67   UErrorCode status = U_ZERO_ERROR;
68   int i;
69   UDate d;
70   for(i = 0;i<UCAL_FIELD_COUNT;i++) {
71     out += (UnicodeString("") + fieldName((UCalendarDateFields)i) + "=" +  cal.get((UCalendarDateFields)i, status) + UnicodeString(" "));
72   }
73   out += "[" + UnicodeString(cal.getType()) + "]";
74 
75   if(cal.inDaylightTime(status)) {
76     out += UnicodeString(" (in DST), zone=");
77   }
78   else {
79     out += UnicodeString(", zone=");
80   }
81 
82   UnicodeString str2;
83   out += cal.getTimeZone().getDisplayName(str2);
84   d = cal.getTime(status);
85   out += UnicodeString(" :","") + d;
86 
87   return out;
88 }
89 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)90 void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
91 {
92     if (exec) logln("TestSuite TestCalendar");
93 
94     TESTCASE_AUTO_BEGIN;
95     TESTCASE_AUTO(TestDOW943);
96     TESTCASE_AUTO(TestClonesUnique908);
97     TESTCASE_AUTO(TestGregorianChange768);
98     TESTCASE_AUTO(TestDisambiguation765);
99     TESTCASE_AUTO(TestGMTvsLocal4064654);
100     TESTCASE_AUTO(TestAddSetOrder621);
101     TESTCASE_AUTO(TestAdd520);
102     TESTCASE_AUTO(TestFieldSet4781);
103     //  TESTCASE_AUTO(TestSerialize337);
104     TESTCASE_AUTO(TestSecondsZero121);
105     TESTCASE_AUTO(TestAddSetGet0610);
106     TESTCASE_AUTO(TestFields060);
107     TESTCASE_AUTO(TestEpochStartFields);
108     TESTCASE_AUTO(TestDOWProgression);
109     TESTCASE_AUTO(TestGenericAPI);
110     TESTCASE_AUTO(TestAddRollExtensive);
111     TESTCASE_AUTO(TestDOW_LOCALandYEAR_WOY);
112     TESTCASE_AUTO(TestWOY);
113     TESTCASE_AUTO(TestRog);
114     TESTCASE_AUTO(TestYWOY);
115     TESTCASE_AUTO(TestJD);
116     TESTCASE_AUTO(TestDebug);
117     TESTCASE_AUTO(Test6703);
118     TESTCASE_AUTO(Test3785);
119     TESTCASE_AUTO(Test1624);
120     TESTCASE_AUTO(TestTimeStamp);
121     TESTCASE_AUTO(TestISO8601);
122     TESTCASE_AUTO(TestAmbiguousWallTimeAPIs);
123     TESTCASE_AUTO(TestRepeatedWallTime);
124     TESTCASE_AUTO(TestSkippedWallTime);
125     TESTCASE_AUTO(TestCloneLocale);
126     TESTCASE_AUTO(TestIslamicUmAlQura);
127     TESTCASE_AUTO(TestIslamicTabularDates);
128     TESTCASE_AUTO(TestHebrewMonthValidation);
129     TESTCASE_AUTO(TestWeekData);
130     TESTCASE_AUTO(TestAddAcrossZoneTransition);
131     TESTCASE_AUTO(TestChineseCalendarMapping);
132     TESTCASE_AUTO(TestTimeZoneInLocale);
133     TESTCASE_AUTO(TestBasicConversionISO8601);
134     TESTCASE_AUTO(TestBasicConversionJapanese);
135     TESTCASE_AUTO(TestBasicConversionBuddhist);
136     TESTCASE_AUTO(TestBasicConversionTaiwan);
137     TESTCASE_AUTO(TestBasicConversionPersian);
138     TESTCASE_AUTO(TestBasicConversionIslamic);
139     TESTCASE_AUTO(TestBasicConversionIslamicTBLA);
140     TESTCASE_AUTO(TestBasicConversionIslamicCivil);
141     TESTCASE_AUTO(TestBasicConversionIslamicRGSA);
142     TESTCASE_AUTO(TestBasicConversionIslamicUmalqura);
143     TESTCASE_AUTO(TestBasicConversionHebrew);
144     TESTCASE_AUTO(TestBasicConversionChinese);
145     TESTCASE_AUTO(TestBasicConversionDangi);
146     TESTCASE_AUTO(TestBasicConversionIndian);
147     TESTCASE_AUTO(TestBasicConversionCoptic);
148     TESTCASE_AUTO(TestBasicConversionEthiopic);
149     TESTCASE_AUTO(TestBasicConversionEthiopicAmeteAlem);
150     TESTCASE_AUTO(TestGregorianCalendarInTemporalLeapYear);
151     TESTCASE_AUTO(TestChineseCalendarInTemporalLeapYear);
152     TESTCASE_AUTO(TestDangiCalendarInTemporalLeapYear);
153     TESTCASE_AUTO(TestHebrewCalendarInTemporalLeapYear);
154     TESTCASE_AUTO(TestIslamicCalendarInTemporalLeapYear);
155     TESTCASE_AUTO(TestIslamicCivilCalendarInTemporalLeapYear);
156     TESTCASE_AUTO(TestIslamicUmalquraCalendarInTemporalLeapYear);
157     TESTCASE_AUTO(TestIslamicRGSACalendarInTemporalLeapYear);
158     TESTCASE_AUTO(TestIslamicTBLACalendarInTemporalLeapYear);
159     TESTCASE_AUTO(TestPersianCalendarInTemporalLeapYear);
160     TESTCASE_AUTO(TestIndianCalendarInTemporalLeapYear);
161     TESTCASE_AUTO(TestTaiwanCalendarInTemporalLeapYear);
162     TESTCASE_AUTO(TestJapaneseCalendarInTemporalLeapYear);
163     TESTCASE_AUTO(TestBuddhistCalendarInTemporalLeapYear);
164     TESTCASE_AUTO(TestCopticCalendarInTemporalLeapYear);
165     TESTCASE_AUTO(TestEthiopicCalendarInTemporalLeapYear);
166     TESTCASE_AUTO(TestEthiopicAmeteAlemCalendarInTemporalLeapYear);
167     TESTCASE_AUTO(TestChineseCalendarGetTemporalMonthCode);
168     TESTCASE_AUTO(TestDangiCalendarGetTemporalMonthCode);
169     TESTCASE_AUTO(TestHebrewCalendarGetTemporalMonthCode);
170     TESTCASE_AUTO(TestCopticCalendarGetTemporalMonthCode);
171     TESTCASE_AUTO(TestEthiopicCalendarGetTemporalMonthCode);
172     TESTCASE_AUTO(TestEthiopicAmeteAlemCalendarGetTemporalMonthCode);
173     TESTCASE_AUTO(TestGregorianCalendarSetTemporalMonthCode);
174     TESTCASE_AUTO(TestChineseCalendarSetTemporalMonthCode);
175     TESTCASE_AUTO(TestHebrewCalendarSetTemporalMonthCode);
176     TESTCASE_AUTO(TestCopticCalendarSetTemporalMonthCode);
177     TESTCASE_AUTO(TestEthiopicCalendarSetTemporalMonthCode);
178     TESTCASE_AUTO(TestMostCalendarsOrdinalMonthSet);
179     TESTCASE_AUTO(TestChineseCalendarOrdinalMonthSet);
180     TESTCASE_AUTO(TestDangiCalendarOrdinalMonthSet);
181     TESTCASE_AUTO(TestHebrewCalendarOrdinalMonthSet);
182     TESTCASE_AUTO(TestCalendarAddOrdinalMonth);
183     TESTCASE_AUTO(TestCalendarRollOrdinalMonth);
184     TESTCASE_AUTO(TestLimitsOrdinalMonth);
185     TESTCASE_AUTO(TestActualLimitsOrdinalMonth);
186     TESTCASE_AUTO(TestChineseCalendarMonthInSpecialYear);
187     TESTCASE_AUTO(TestClearMonth);
188 
189     TESTCASE_AUTO(TestFWWithISO8601);
190     TESTCASE_AUTO(TestDangiOverflowIsLeapMonthBetween22507);
191     TESTCASE_AUTO(TestRollWeekOfYear);
192     TESTCASE_AUTO(TestFirstDayOfWeek);
193 
194     TESTCASE_AUTO(Test22633ChineseOverflow);
195     TESTCASE_AUTO(Test22633IndianOverflow);
196     TESTCASE_AUTO(Test22633IslamicUmalquraOverflow);
197     TESTCASE_AUTO(Test22633PersianOverflow);
198     TESTCASE_AUTO(Test22633HebrewOverflow);
199     TESTCASE_AUTO(Test22633AMPMOverflow);
200     TESTCASE_AUTO(Test22633SetGetTimeOverflow);
201     TESTCASE_AUTO(Test22633Set2FieldsGetTimeOverflow);
202     TESTCASE_AUTO(Test22633SetAddGetTimeOverflow);
203     TESTCASE_AUTO(Test22633SetRollGetTimeOverflow);
204     TESTCASE_AUTO(Test22633AddTwiceGetTimeOverflow);
205     TESTCASE_AUTO(Test22633RollTwiceGetTimeOverflow);
206 
207     TESTCASE_AUTO(Test22633HebrewLargeNegativeDay);
208 
209     TESTCASE_AUTO(TestAddOverflow);
210 
211 
212     TESTCASE_AUTO(TestChineseCalendarComputeMonthStart);
213 
214     TESTCASE_AUTO_END;
215 }
216 
217 // ---------------------------------------------------------------------------------
218 
fieldName(UCalendarDateFields f)219 UnicodeString CalendarTest::fieldName(UCalendarDateFields f) {
220     switch (f) {
221 #define FIELD_NAME_STR(x) case x: return (#x)+5
222       FIELD_NAME_STR( UCAL_ERA );
223       FIELD_NAME_STR( UCAL_YEAR );
224       FIELD_NAME_STR( UCAL_MONTH );
225       FIELD_NAME_STR( UCAL_WEEK_OF_YEAR );
226       FIELD_NAME_STR( UCAL_WEEK_OF_MONTH );
227       FIELD_NAME_STR( UCAL_DATE );
228       FIELD_NAME_STR( UCAL_DAY_OF_YEAR );
229       FIELD_NAME_STR( UCAL_DAY_OF_WEEK );
230       FIELD_NAME_STR( UCAL_DAY_OF_WEEK_IN_MONTH );
231       FIELD_NAME_STR( UCAL_AM_PM );
232       FIELD_NAME_STR( UCAL_HOUR );
233       FIELD_NAME_STR( UCAL_HOUR_OF_DAY );
234       FIELD_NAME_STR( UCAL_MINUTE );
235       FIELD_NAME_STR( UCAL_SECOND );
236       FIELD_NAME_STR( UCAL_MILLISECOND );
237       FIELD_NAME_STR( UCAL_ZONE_OFFSET );
238       FIELD_NAME_STR( UCAL_DST_OFFSET );
239       FIELD_NAME_STR( UCAL_YEAR_WOY );
240       FIELD_NAME_STR( UCAL_DOW_LOCAL );
241       FIELD_NAME_STR( UCAL_EXTENDED_YEAR );
242       FIELD_NAME_STR( UCAL_JULIAN_DAY );
243       FIELD_NAME_STR( UCAL_MILLISECONDS_IN_DAY );
244 #undef FIELD_NAME_STR
245     default:
246         return UnicodeString("") + ((int32_t)f);
247     }
248 }
249 
250 /**
251  * Test various API methods for API completeness.
252  */
253 void
TestGenericAPI()254 CalendarTest::TestGenericAPI()
255 {
256     UErrorCode status = U_ZERO_ERROR;
257     UDate d;
258     UnicodeString str;
259     UBool eq = false,b4 = false,af = false;
260 
261     UDate when = date(90, UCAL_APRIL, 15);
262 
263     UnicodeString tzid("TestZone");
264     int32_t tzoffset = 123400;
265 
266     SimpleTimeZone *zone = new SimpleTimeZone(tzoffset, tzid);
267     Calendar *cal = Calendar::createInstance(zone->clone(), status);
268     if (failure(status, "Calendar::createInstance #1", true)) return;
269 
270     if (*zone != cal->getTimeZone()) errln("FAIL: Calendar::getTimeZone failed");
271 
272     Calendar *cal2 = Calendar::createInstance(cal->getTimeZone(), status);
273     if (failure(status, "Calendar::createInstance #2")) return;
274     cal->setTime(when, status);
275     cal2->setTime(when, status);
276     if (failure(status, "Calendar::setTime")) return;
277 
278     if (!(*cal == *cal2)) errln("FAIL: Calendar::operator== failed");
279     if ((*cal != *cal2))  errln("FAIL: Calendar::operator!= failed");
280     if (!cal->equals(*cal2, status) ||
281         cal->before(*cal2, status) ||
282         cal->after(*cal2, status) ||
283         U_FAILURE(status)) errln("FAIL: equals/before/after failed");
284 
285     logln(UnicodeString("cal=")  +cal->getTime(status)  + UnicodeString(calToStr(*cal)));
286     logln(UnicodeString("cal2=")  +cal2->getTime(status)  + UnicodeString(calToStr(*cal2)));
287     logln("cal2->setTime(when+1000)");
288     cal2->setTime(when + 1000, status);
289     logln(UnicodeString("cal2=")  +cal2->getTime(status)  + UnicodeString(calToStr(*cal2)));
290 
291     if (failure(status, "Calendar::setTime")) return;
292     if (cal->equals(*cal2, status) ||
293         cal2->before(*cal, status) ||
294         cal->after(*cal2, status) ||
295         U_FAILURE(status)) errln("FAIL: equals/before/after failed after setTime(+1000)");
296 
297     logln("cal->roll(UCAL_SECOND)");
298     cal->roll(UCAL_SECOND, (UBool) true, status);
299     logln(UnicodeString("cal=")  +cal->getTime(status)  + UnicodeString(calToStr(*cal)));
300     cal->roll(UCAL_SECOND, (int32_t)0, status);
301     logln(UnicodeString("cal=")  +cal->getTime(status)  + UnicodeString(calToStr(*cal)));
302     if (failure(status, "Calendar::roll")) return;
303 
304     if (!(eq=cal->equals(*cal2, status)) ||
305         (b4=cal->before(*cal2, status)) ||
306         (af=cal->after(*cal2, status)) ||
307         U_FAILURE(status)) {
308       errln("FAIL: equals[%c]/before[%c]/after[%c] failed after roll 1 second [should be T/F/F]",
309             eq?'T':'F',
310             b4?'T':'F',
311             af?'T':'F');
312       logln(UnicodeString("cal=")  +cal->getTime(status)  + UnicodeString(calToStr(*cal)));
313       logln(UnicodeString("cal2=")  +cal2->getTime(status)  + UnicodeString(calToStr(*cal2)));
314     }
315 
316     // Roll back to January
317     cal->roll(UCAL_MONTH, (int32_t)(1 + UCAL_DECEMBER - cal->get(UCAL_MONTH, status)), status);
318     if (failure(status, "Calendar::roll")) return;
319     if (cal->equals(*cal2, status) ||
320         cal2->before(*cal, status) ||
321         cal->after(*cal2, status) ||
322         U_FAILURE(status)) errln("FAIL: equals/before/after failed after rollback to January");
323 
324     TimeZone *z = cal->orphanTimeZone();
325     if (z->getID(str) != tzid ||
326         z->getRawOffset() != tzoffset)
327         errln("FAIL: orphanTimeZone failed");
328 
329     int32_t i;
330     for (i=0; i<2; ++i)
331     {
332         UBool lenient = ( i > 0 );
333         cal->setLenient(lenient);
334         if (lenient != cal->isLenient()) errln("FAIL: setLenient/isLenient failed");
335         // Later: Check for lenient behavior
336     }
337 
338     for (i=UCAL_SUNDAY; i<=UCAL_SATURDAY; ++i)
339     {
340         cal->setFirstDayOfWeek((UCalendarDaysOfWeek)i);
341         if (cal->getFirstDayOfWeek() != i) errln("FAIL: set/getFirstDayOfWeek failed");
342         UErrorCode aStatus = U_ZERO_ERROR;
343         if (cal->getFirstDayOfWeek(aStatus) != i || U_FAILURE(aStatus)) errln("FAIL: getFirstDayOfWeek(status) failed");
344     }
345 
346     for (i=1; i<=7; ++i)
347     {
348         cal->setMinimalDaysInFirstWeek((uint8_t)i);
349         if (cal->getMinimalDaysInFirstWeek() != i) errln("FAIL: set/getFirstDayOfWeek failed");
350     }
351 
352     for (i=0; i<UCAL_FIELD_COUNT; ++i)
353     {
354         if (cal->getMinimum((UCalendarDateFields)i) > cal->getGreatestMinimum((UCalendarDateFields)i))
355             errln(UnicodeString("FAIL: getMinimum larger than getGreatestMinimum for field ") + i);
356         if (cal->getLeastMaximum((UCalendarDateFields)i) > cal->getMaximum((UCalendarDateFields)i))
357             errln(UnicodeString("FAIL: getLeastMaximum larger than getMaximum for field ") + i);
358         if (cal->getMinimum((UCalendarDateFields)i) >= cal->getMaximum((UCalendarDateFields)i))
359             errln(UnicodeString("FAIL: getMinimum not less than getMaximum for field ") + i);
360     }
361 
362     cal->adoptTimeZone(TimeZone::createDefault());
363     cal->clear();
364     cal->set(1984, 5, 24);
365     if (cal->getTime(status) != date(84, 5, 24) || U_FAILURE(status))
366         errln("FAIL: Calendar::set(3 args) failed");
367 
368     cal->clear();
369     cal->set(1985, 3, 2, 11, 49);
370     if (cal->getTime(status) != date(85, 3, 2, 11, 49) || U_FAILURE(status))
371         errln("FAIL: Calendar::set(5 args) failed");
372 
373     cal->clear();
374     cal->set(1995, 9, 12, 1, 39, 55);
375     if (cal->getTime(status) != date(95, 9, 12, 1, 39, 55) || U_FAILURE(status))
376         errln("FAIL: Calendar::set(6 args) failed");
377 
378     cal->getTime(status);
379     if (failure(status, "Calendar::getTime")) return;
380     for (i=0; i<UCAL_FIELD_COUNT; ++i)
381     {
382         switch(i) {
383             case UCAL_YEAR: case UCAL_MONTH: case UCAL_DATE:
384             case UCAL_HOUR_OF_DAY: case UCAL_MINUTE: case UCAL_SECOND:
385             case UCAL_EXTENDED_YEAR:
386               if (!cal->isSet((UCalendarDateFields)i)) errln("FAIL: Calendar::isSet F, should be T " + fieldName((UCalendarDateFields)i));
387                 break;
388             default:
389               if (cal->isSet((UCalendarDateFields)i)) errln("FAIL: Calendar::isSet = T, should be F  " + fieldName((UCalendarDateFields)i));
390         }
391         cal->clear((UCalendarDateFields)i);
392         if (cal->isSet((UCalendarDateFields)i)) errln("FAIL: Calendar::clear/isSet failed " + fieldName((UCalendarDateFields)i));
393     }
394 
395     if(cal->getActualMinimum(Calendar::SECOND, status) != 0){
396         errln("Calendar is suppose to return 0 for getActualMinimum");
397     }
398 
399     Calendar *cal3 = Calendar::createInstance(status);
400     cal3->roll(Calendar::SECOND, (int32_t)0, status);
401     if (failure(status, "Calendar::roll(EDateFields, int32_t, UErrorCode)")) return;
402 
403     delete cal;
404     delete cal2;
405     delete cal3;
406 
407     int32_t count;
408     const Locale* loc = Calendar::getAvailableLocales(count);
409     if (count < 1 || loc == nullptr)
410     {
411         dataerrln("FAIL: getAvailableLocales failed");
412     }
413     else
414     {
415         for (i=0; i<count; ++i)
416         {
417             cal = Calendar::createInstance(loc[i], status);
418             if (U_FAILURE(status)) {
419                 errcheckln(status, UnicodeString("FAIL: Calendar::createInstance #3, locale ") +  loc[i].getName() + " , error " + u_errorName(status));
420                 return;
421             }
422             delete cal;
423         }
424     }
425 
426     cal = Calendar::createInstance(TimeZone::createDefault(), Locale::getEnglish(), status);
427     if (failure(status, "Calendar::createInstance #4")) return;
428     delete cal;
429 
430     cal = Calendar::createInstance(*zone, Locale::getEnglish(), status);
431     if (failure(status, "Calendar::createInstance #5")) return;
432     delete cal;
433 
434     GregorianCalendar *gc = new GregorianCalendar(*zone, status);
435     if (failure(status, "new GregorianCalendar")) return;
436     delete gc;
437 
438     gc = new GregorianCalendar(Locale::getEnglish(), status);
439     if (failure(status, "new GregorianCalendar")) return;
440     delete gc;
441 
442     gc = new GregorianCalendar(Locale::getEnglish(), status);
443     delete gc;
444 
445     gc = new GregorianCalendar(*zone, Locale::getEnglish(), status);
446     if (failure(status, "new GregorianCalendar")) return;
447     delete gc;
448 
449     gc = new GregorianCalendar(zone, status);
450     if (failure(status, "new GregorianCalendar")) return;
451     delete gc;
452 
453     gc = new GregorianCalendar(1998, 10, 14, 21, 43, status);
454     if (gc->getTime(status) != (d =date(98, 10, 14, 21, 43) )|| U_FAILURE(status))
455       errln("FAIL: new GregorianCalendar(ymdhm) failed with " + UnicodeString(u_errorName(status)) + ",  cal="  + gc->getTime(status)  + UnicodeString(calToStr(*gc)) + ", d=" + d);
456     else
457       logln(UnicodeString("GOOD: cal=")  +gc->getTime(status)  + UnicodeString(calToStr(*gc)) + ", d=" + d);
458     delete gc;
459 
460     gc = new GregorianCalendar(1998, 10, 14, 21, 43, 55, status);
461     if (gc->getTime(status) != (d=date(98, 10, 14, 21, 43, 55)) || U_FAILURE(status))
462       errln("FAIL: new GregorianCalendar(ymdhms) failed with " + UnicodeString(u_errorName(status)));
463 
464     GregorianCalendar gc2(Locale::getEnglish(), status);
465     if (failure(status, "new GregorianCalendar")) return;
466     gc2 = *gc;
467     if (gc2 != *gc || !(gc2 == *gc)) errln("FAIL: GregorianCalendar assignment/operator==/operator!= failed");
468     delete gc;
469     delete z;
470 
471     /* Code coverage for Calendar class. */
472     cal = Calendar::createInstance(status);
473     if (failure(status, "Calendar::createInstance #6")) {
474         return;
475     }else {
476         cal->roll(UCAL_HOUR, (int32_t)100, status);
477         cal->clear(UCAL_HOUR);
478 #if !UCONFIG_NO_SERVICE
479         URegistryKey key = cal->registerFactory(nullptr, status);
480         cal->unregister(key, status);
481 #endif
482     }
483     delete cal;
484 
485     status = U_ZERO_ERROR;
486     cal = Calendar::createInstance(Locale("he_IL@calendar=hebrew"), status);
487     if (failure(status, "Calendar::createInstance #7")) {
488         return;
489     } else {
490         cal->roll(Calendar::MONTH, (int32_t)100, status);
491     }
492 
493     LocalPointer<StringEnumeration> values(
494         Calendar::getKeywordValuesForLocale("calendar", Locale("he"), false, status));
495     if (values.isNull() || U_FAILURE(status)) {
496         dataerrln("FAIL: Calendar::getKeywordValuesForLocale(he): %s", u_errorName(status));
497     } else {
498         UBool containsHebrew = false;
499         const char *charValue;
500         int32_t valueLength;
501         while ((charValue = values->next(&valueLength, status)) != nullptr) {
502             if (valueLength == 6 && uprv_strcmp(charValue, "hebrew") == 0) {
503                 containsHebrew = true;
504             }
505         }
506         if (!containsHebrew) {
507             errln("Calendar::getKeywordValuesForLocale(he)->next() does not contain \"hebrew\"");
508         }
509 
510         values->reset(status);
511         containsHebrew = false;
512         UnicodeString hebrew = UNICODE_STRING_SIMPLE("hebrew");
513         const char16_t *ucharValue;
514         while ((ucharValue = values->unext(&valueLength, status)) != nullptr) {
515             UnicodeString value(false, ucharValue, valueLength);
516             if (value == hebrew) {
517                 containsHebrew = true;
518             }
519         }
520         if (!containsHebrew) {
521             errln("Calendar::getKeywordValuesForLocale(he)->unext() does not contain \"hebrew\"");
522         }
523 
524         values->reset(status);
525         containsHebrew = false;
526         const UnicodeString *stringValue;
527         while ((stringValue = values->snext(status)) != nullptr) {
528             if (*stringValue == hebrew) {
529                 containsHebrew = true;
530             }
531         }
532         if (!containsHebrew) {
533             errln("Calendar::getKeywordValuesForLocale(he)->snext() does not contain \"hebrew\"");
534         }
535     }
536     delete cal;
537 }
538 
539 // -------------------------------------
540 
541 /**
542  * This test confirms the correct behavior of add when incrementing
543  * through subsequent days.
544  */
545 void
TestRog()546 CalendarTest::TestRog()
547 {
548     UErrorCode status = U_ZERO_ERROR;
549     GregorianCalendar* gc = new GregorianCalendar(status);
550     if (failure(status, "new GregorianCalendar", true)) return;
551     int32_t year = 1997, month = UCAL_APRIL, date = 1;
552     gc->set(year, month, date);
553     gc->set(UCAL_HOUR_OF_DAY, 23);
554     gc->set(UCAL_MINUTE, 0);
555     gc->set(UCAL_SECOND, 0);
556     gc->set(UCAL_MILLISECOND, 0);
557     for (int32_t i = 0; i < 9; i++, gc->add(UCAL_DATE, 1, status)) {
558         if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
559         if (gc->get(UCAL_YEAR, status) != year ||
560             gc->get(UCAL_MONTH, status) != month ||
561             gc->get(UCAL_DATE, status) != (date + i)) errln("FAIL: Date wrong");
562         if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
563     }
564     delete gc;
565 }
566 
567 // -------------------------------------
568 
569 /**
570  * Test the handling of the day of the week, checking for correctness and
571  * for correct minimum and maximum values.
572  */
573 void
TestDOW943()574 CalendarTest::TestDOW943()
575 {
576     dowTest(false);
577     dowTest(true);
578 }
579 
dowTest(UBool lenient)580 void CalendarTest::dowTest(UBool lenient)
581 {
582     UErrorCode status = U_ZERO_ERROR;
583     GregorianCalendar* cal = new GregorianCalendar(status);
584     if (failure(status, "new GregorianCalendar", true)) return;
585     logln("cal - Aug 12, 1997\n");
586     cal->set(1997, UCAL_AUGUST, 12);
587     cal->getTime(status);
588     if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
589     logln((lenient?UnicodeString("LENIENT0: "):UnicodeString("nonlenient0: ")) + UnicodeString(calToStr(*cal)));
590     cal->setLenient(lenient);
591     logln("cal - Dec 1, 1996\n");
592     cal->set(1996, UCAL_DECEMBER, 1);
593     logln((lenient?UnicodeString("LENIENT: "):UnicodeString("nonlenient: ")) + UnicodeString(calToStr(*cal)));
594     int32_t dow = cal->get(UCAL_DAY_OF_WEEK, status);
595     if (U_FAILURE(status)) { errln("Calendar::get failed [%s]", u_errorName(status)); return; }
596     int32_t min = cal->getMinimum(UCAL_DAY_OF_WEEK);
597     int32_t max = cal->getMaximum(UCAL_DAY_OF_WEEK);
598     if (dow < min ||
599         dow > max) errln(UnicodeString("FAIL: Day of week ") + (int32_t)dow + " out of range");
600     if (dow != UCAL_SUNDAY) errln("FAIL: Day of week should be SUNDAY[%d] not %d", UCAL_SUNDAY, dow);
601     if (min != UCAL_SUNDAY ||
602         max != UCAL_SATURDAY) errln("FAIL: Min/max bad");
603     delete cal;
604 }
605 
606 // -------------------------------------
607 
608 /**
609  * Confirm that cloned Calendar objects do not inadvertently share substructures.
610  */
611 void
TestClonesUnique908()612 CalendarTest::TestClonesUnique908()
613 {
614     UErrorCode status = U_ZERO_ERROR;
615     Calendar *c = Calendar::createInstance(status);
616     if (failure(status, "Calendar::createInstance", true)) return;
617     Calendar *d = c->clone();
618     c->set(UCAL_MILLISECOND, 123);
619     d->set(UCAL_MILLISECOND, 456);
620     if (c->get(UCAL_MILLISECOND, status) != 123 ||
621         d->get(UCAL_MILLISECOND, status) != 456) {
622         errln("FAIL: Clones share fields");
623     }
624     if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
625     delete c;
626     delete d;
627 }
628 
629 // -------------------------------------
630 
631 /**
632  * Confirm that the Gregorian cutoff value works as advertised.
633  */
634 void
TestGregorianChange768()635 CalendarTest::TestGregorianChange768()
636 {
637     UBool b;
638     UErrorCode status = U_ZERO_ERROR;
639     UnicodeString str;
640     GregorianCalendar* c = new GregorianCalendar(status);
641     if (failure(status, "new GregorianCalendar", true)) return;
642     logln(UnicodeString("With cutoff ") + dateToString(c->getGregorianChange(), str));
643     b = c->isLeapYear(1800);
644     logln(UnicodeString(" isLeapYear(1800) = ") + (b ? "true" : "false"));
645     logln(UnicodeString(" (should be false)"));
646     if (b) errln("FAIL");
647     c->setGregorianChange(date(0, 0, 1), status);
648     if (U_FAILURE(status)) { errln("GregorianCalendar::setGregorianChange failed"); return; }
649     logln(UnicodeString("With cutoff ") + dateToString(c->getGregorianChange(), str));
650     b = c->isLeapYear(1800);
651     logln(UnicodeString(" isLeapYear(1800) = ") + (b ? "true" : "false"));
652     logln(UnicodeString(" (should be true)"));
653     if (!b) errln("FAIL");
654     delete c;
655 }
656 
657 // -------------------------------------
658 
659 /**
660  * Confirm the functioning of the field disambiguation algorithm.
661  */
662 void
TestDisambiguation765()663 CalendarTest::TestDisambiguation765()
664 {
665     UErrorCode status = U_ZERO_ERROR;
666     Calendar *c = Calendar::createInstance("en_US", status);
667     if (failure(status, "Calendar::createInstance", true)) return;
668     c->setLenient(false);
669     c->clear();
670     c->set(UCAL_YEAR, 1997);
671     c->set(UCAL_MONTH, UCAL_JUNE);
672     c->set(UCAL_DATE, 3);
673     verify765("1997 third day of June = ", c, 1997, UCAL_JUNE, 3);
674     c->clear();
675     c->set(UCAL_YEAR, 1997);
676     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
677     c->set(UCAL_MONTH, UCAL_JUNE);
678     c->set(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
679     verify765("1997 first Tuesday in June = ", c, 1997, UCAL_JUNE, 3);
680     c->clear();
681     c->set(UCAL_YEAR, 1997);
682     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
683     c->set(UCAL_MONTH, UCAL_JUNE);
684     c->set(UCAL_DAY_OF_WEEK_IN_MONTH, - 1);
685     verify765("1997 last Tuesday in June = ", c, 1997, UCAL_JUNE, 24);
686 
687     status = U_ZERO_ERROR;
688     c->clear();
689     c->set(UCAL_YEAR, 1997);
690     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
691     c->set(UCAL_MONTH, UCAL_JUNE);
692     c->set(UCAL_DAY_OF_WEEK_IN_MONTH, 0);
693     c->getTime(status);
694     verify765("1997 zero-th Tuesday in June = ", status);
695 
696     c->clear();
697     c->set(UCAL_YEAR, 1997);
698     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
699     c->set(UCAL_MONTH, UCAL_JUNE);
700     c->set(UCAL_WEEK_OF_MONTH, 1);
701     verify765("1997 Tuesday in week 1 of June = ", c, 1997, UCAL_JUNE, 3);
702     c->clear();
703     c->set(UCAL_YEAR, 1997);
704     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
705     c->set(UCAL_MONTH, UCAL_JUNE);
706     c->set(UCAL_WEEK_OF_MONTH, 5);
707     verify765("1997 Tuesday in week 5 of June = ", c, 1997, UCAL_JULY, 1);
708 
709     status = U_ZERO_ERROR;
710     c->clear();
711     c->set(UCAL_YEAR, 1997);
712     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
713     c->set(UCAL_MONTH, UCAL_JUNE);
714     c->set(UCAL_WEEK_OF_MONTH, 0);
715     c->setMinimalDaysInFirstWeek(1);
716     c->getTime(status);
717     verify765("1997 Tuesday in week 0 of June = ", status);
718 
719     /* Note: The following test used to expect YEAR 1997, WOY 1 to
720      * resolve to a date in Dec 1996; that is, to behave as if
721      * YEAR_WOY were 1997.  With the addition of a new explicit
722      * YEAR_WOY field, YEAR_WOY must itself be set if that is what is
723      * desired.  Using YEAR in combination with WOY is ambiguous, and
724      * results in the first WOY/DOW day of the year satisfying the
725      * given fields (there may be up to two such days). In this case,
726      * it properly resolves to Tue Dec 30 1997, which has a WOY value
727      * of 1 (for YEAR_WOY 1998) and a DOW of Tuesday, and falls in the
728      * _calendar_ year 1997, as specified. - aliu */
729     c->clear();
730     c->set(UCAL_YEAR_WOY, 1997); // aliu
731     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
732     c->set(UCAL_WEEK_OF_YEAR, 1);
733     verify765("1997 Tuesday in week 1 of yearWOY = ", c, 1996, UCAL_DECEMBER, 31);
734     c->clear(); // - add test for YEAR
735     c->setMinimalDaysInFirstWeek(1);
736     c->set(UCAL_YEAR, 1997);
737     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
738     c->set(UCAL_WEEK_OF_YEAR, 1);
739     verify765("1997 Tuesday in week 1 of year = ", c, 1997, UCAL_DECEMBER, 30);
740     c->clear();
741     c->set(UCAL_YEAR, 1997);
742     c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
743     c->set(UCAL_WEEK_OF_YEAR, 10);
744     verify765("1997 Tuesday in week 10 of year = ", c, 1997, UCAL_MARCH, 4);
745     //try {
746 
747     // {sfb} week 0 is no longer a valid week of year
748     /*c->clear();
749     c->set(Calendar::YEAR, 1997);
750     c->set(Calendar::DAY_OF_WEEK, Calendar::TUESDAY);
751     //c->set(Calendar::WEEK_OF_YEAR, 0);
752     c->set(Calendar::WEEK_OF_YEAR, 1);
753     verify765("1997 Tuesday in week 0 of year = ", c, 1996, Calendar::DECEMBER, 24);*/
754 
755     //}
756     //catch(IllegalArgumentException ex) {
757     //    errln("FAIL: Exception seen:");
758     //    ex.printStackTrace(log);
759     //}
760     delete c;
761 }
762 
763 // -------------------------------------
764 
765 void
verify765(const UnicodeString & msg,Calendar * c,int32_t year,int32_t month,int32_t day)766 CalendarTest::verify765(const UnicodeString& msg, Calendar* c, int32_t year, int32_t month, int32_t day)
767 {
768     UnicodeString str;
769     UErrorCode status = U_ZERO_ERROR;
770     int32_t y = c->get(UCAL_YEAR, status);
771     int32_t m = c->get(UCAL_MONTH, status);
772     int32_t d = c->get(UCAL_DATE, status);
773     if ( y == year &&
774          m == month &&
775          d == day) {
776         if (U_FAILURE(status)) { errln("FAIL: Calendar::get failed"); return; }
777         logln("PASS: " + msg + dateToString(c->getTime(status), str));
778         if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
779     }
780     else {
781         errln("FAIL: " + msg + dateToString(c->getTime(status), str) + "; expected " + (int32_t)year + "/" + (int32_t)(month + 1) + "/" + (int32_t)day +
782             "; got " + (int32_t)y + "/" + (int32_t)(m + 1) + "/" + (int32_t)d + " for Locale: " + c->getLocaleID(ULOC_ACTUAL_LOCALE,status));
783         if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
784     }
785 }
786 
787 // -------------------------------------
788 
789 void
verify765(const UnicodeString & msg,UErrorCode status)790 CalendarTest::verify765(const UnicodeString& msg/*, IllegalArgumentException e*/, UErrorCode status)
791 {
792     if (status != U_ILLEGAL_ARGUMENT_ERROR) errln("FAIL: No IllegalArgumentException for " + msg);
793     else logln("PASS: " + msg + "IllegalArgument as expected");
794 }
795 
796 // -------------------------------------
797 
798 /**
799  * Confirm that the offset between local time and GMT behaves as expected.
800  */
801 void
TestGMTvsLocal4064654()802 CalendarTest::TestGMTvsLocal4064654()
803 {
804     test4064654(1997, 1, 1, 12, 0, 0);
805     test4064654(1997, 4, 16, 18, 30, 0);
806 }
807 
808 // -------------------------------------
809 
810 void
test4064654(int32_t yr,int32_t mo,int32_t dt,int32_t hr,int32_t mn,int32_t sc)811 CalendarTest::test4064654(int32_t yr, int32_t mo, int32_t dt, int32_t hr, int32_t mn, int32_t sc)
812 {
813     UDate date;
814     UErrorCode status = U_ZERO_ERROR;
815     UnicodeString str;
816     Calendar *gmtcal = Calendar::createInstance(status);
817     if (failure(status, "Calendar::createInstance", true)) return;
818     gmtcal->adoptTimeZone(TimeZone::createTimeZone("Africa/Casablanca"));
819     gmtcal->set(yr, mo - 1, dt, hr, mn, sc);
820     gmtcal->set(UCAL_MILLISECOND, 0);
821     date = gmtcal->getTime(status);
822     if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
823     logln("date = " + dateToString(date, str));
824     Calendar *cal = Calendar::createInstance(status);
825     if (U_FAILURE(status)) { errln("Calendar::createInstance failed"); return; }
826     cal->setTime(date, status);
827     if (U_FAILURE(status)) { errln("Calendar::setTime failed"); return; }
828     int32_t offset = cal->getTimeZone().getOffset((uint8_t)cal->get(UCAL_ERA, status),
829                                                   cal->get(UCAL_YEAR, status),
830                                                   cal->get(UCAL_MONTH, status),
831                                                   cal->get(UCAL_DATE, status),
832                                                   (uint8_t)cal->get(UCAL_DAY_OF_WEEK, status),
833                                                   cal->get(UCAL_MILLISECOND, status), status);
834     if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
835     logln("offset for " + dateToString(date, str) + "= " + (offset / 1000 / 60 / 60.0) + "hr");
836     int32_t utc = ((cal->get(UCAL_HOUR_OF_DAY, status) * 60 +
837                     cal->get(UCAL_MINUTE, status)) * 60 +
838                    cal->get(UCAL_SECOND, status)) * 1000 +
839         cal->get(UCAL_MILLISECOND, status) - offset;
840     if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
841     int32_t expected = ((hr * 60 + mn) * 60 + sc) * 1000;
842     if (utc != expected) errln(UnicodeString("FAIL: Discrepancy of ") + (utc - expected) +
843                                " millis = " + ((utc - expected) / 1000 / 60 / 60.0) + " hr");
844     delete gmtcal;
845     delete cal;
846 }
847 
848 // -------------------------------------
849 
850 /**
851  * The operations of adding and setting should not exhibit pathological
852  * dependence on the order of operations.  This test checks for this.
853  */
854 void
TestAddSetOrder621()855 CalendarTest::TestAddSetOrder621()
856 {
857     UDate d = date(97, 4, 14, 13, 23, 45);
858     UErrorCode status = U_ZERO_ERROR;
859     Calendar *cal = Calendar::createInstance(status);
860     if (failure(status, "Calendar::createInstance", true)) return;
861 
862     cal->setTime(d, status);
863     if (U_FAILURE(status)) {
864         errln("Calendar::setTime failed");
865         delete cal;
866         return;
867     }
868     cal->add(UCAL_DATE, - 5, status);
869     if (U_FAILURE(status)) {
870         errln("Calendar::add failed");
871         delete cal;
872         return;
873     }
874     cal->set(UCAL_HOUR_OF_DAY, 0);
875     cal->set(UCAL_MINUTE, 0);
876     cal->set(UCAL_SECOND, 0);
877     UnicodeString s;
878     dateToString(cal->getTime(status), s);
879     if (U_FAILURE(status)) {
880         errln("Calendar::getTime failed");
881         delete cal;
882         return;
883     }
884     delete cal;
885 
886     cal = Calendar::createInstance(status);
887     if (U_FAILURE(status)) {
888         errln("Calendar::createInstance failed");
889         delete cal;
890         return;
891     }
892     cal->setTime(d, status);
893     if (U_FAILURE(status)) {
894         errln("Calendar::setTime failed");
895         delete cal;
896         return;
897     }
898     cal->set(UCAL_HOUR_OF_DAY, 0);
899     cal->set(UCAL_MINUTE, 0);
900     cal->set(UCAL_SECOND, 0);
901     cal->add(UCAL_DATE, - 5, status);
902     if (U_FAILURE(status)) {
903         errln("Calendar::add failed");
904         delete cal;
905         return;
906     }
907     UnicodeString s2;
908     dateToString(cal->getTime(status), s2);
909     if (U_FAILURE(status)) {
910         errln("Calendar::getTime failed");
911         delete cal;
912         return;
913     }
914     if (s == s2)
915         logln("Pass: " + s + " == " + s2);
916     else
917         errln("FAIL: " + s + " != " + s2);
918     delete cal;
919 }
920 
921 // -------------------------------------
922 
923 /**
924  * Confirm that adding to various fields works.
925  */
926 void
TestAdd520()927 CalendarTest::TestAdd520()
928 {
929     int32_t y = 1997, m = UCAL_FEBRUARY, d = 1;
930     UErrorCode status = U_ZERO_ERROR;
931     GregorianCalendar *temp = new GregorianCalendar(y, m, d, status);
932     if (failure(status, "new GregorianCalendar", true)) return;
933     check520(temp, y, m, d);
934     temp->add(UCAL_YEAR, 1, status);
935     if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
936     y++;
937     check520(temp, y, m, d);
938     temp->add(UCAL_MONTH, 1, status);
939     if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
940     m++;
941     check520(temp, y, m, d);
942     temp->add(UCAL_DATE, 1, status);
943     if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
944     d++;
945     check520(temp, y, m, d);
946     temp->add(UCAL_DATE, 2, status);
947     if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
948     d += 2;
949     check520(temp, y, m, d);
950     temp->add(UCAL_DATE, 28, status);
951     if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
952     d = 1;++m;
953     check520(temp, y, m, d);
954     delete temp;
955 }
956 
957 // -------------------------------------
958 
959 /**
960  * Execute adding and rolling in GregorianCalendar extensively,
961  */
962 void
TestAddRollExtensive()963 CalendarTest::TestAddRollExtensive()
964 {
965     int32_t maxlimit = 40;
966     int32_t y = 1997, m = UCAL_FEBRUARY, d = 1, hr = 1, min = 1, sec = 0, ms = 0;
967     UErrorCode status = U_ZERO_ERROR;
968     GregorianCalendar *temp = new GregorianCalendar(y, m, d, status);
969     if (failure(status, "new GregorianCalendar", true)) return;
970 
971     temp->set(UCAL_HOUR, hr);
972     temp->set(UCAL_MINUTE, min);
973     temp->set(UCAL_SECOND, sec);
974     temp->set(UCAL_MILLISECOND, ms);
975     temp->setMinimalDaysInFirstWeek(1);
976 
977     UCalendarDateFields e;
978 
979     logln("Testing GregorianCalendar add...");
980     e = UCAL_YEAR;
981     while (e < UCAL_FIELD_COUNT) {
982         int32_t i;
983         int32_t limit = maxlimit;
984         status = U_ZERO_ERROR;
985         for (i = 0; i < limit; i++) {
986             temp->add(e, 1, status);
987             if (U_FAILURE(status)) {
988                 limit = i;
989                 status = U_ZERO_ERROR;
990                 break;      // Suppress compile warning. Shouldn't be necessary, but it is.
991             }
992         }
993         for (i = 0; i < limit; i++) {
994             temp->add(e, -1, status);
995             if (U_FAILURE(status)) { errln("GregorianCalendar::add -1 failed"); return; }
996         }
997         check520(temp, y, m, d, hr, min, sec, ms, e);
998 
999         e = (UCalendarDateFields) ((int32_t) e + 1);
1000     }
1001 
1002     logln("Testing GregorianCalendar roll...");
1003     e = UCAL_YEAR;
1004     while (e < UCAL_FIELD_COUNT) {
1005         int32_t i;
1006         int32_t limit = maxlimit;
1007         status = U_ZERO_ERROR;
1008         for (i = 0; i < limit; i++) {
1009             logln(calToStr(*temp) + UnicodeString("  " ) + fieldName(e) + UnicodeString("++") );
1010             temp->roll(e, 1, status);
1011             if (U_FAILURE(status)) {
1012               logln("caltest.cpp:%d e=%d, i=%d - roll(+) err %s\n",  __LINE__, (int) e, (int) i, u_errorName(status));
1013               logln(calToStr(*temp));
1014               limit = i; status = U_ZERO_ERROR;
1015             }
1016         }
1017         for (i = 0; i < limit; i++) {
1018             logln("caltest.cpp:%d e=%d, i=%d\n",  __LINE__, (int) e, (int) i);
1019             logln(calToStr(*temp) + UnicodeString("  " ) + fieldName(e) + UnicodeString("--") );
1020             temp->roll(e, -1, status);
1021             if (U_FAILURE(status)) { errln(UnicodeString("GregorianCalendar::roll ") + CalendarTest::fieldName(e) + " count=" + UnicodeString('@'+i) + " by -1 failed with " + u_errorName(status) ); return; }
1022         }
1023         check520(temp, y, m, d, hr, min, sec, ms, e);
1024 
1025         e = (UCalendarDateFields) ((int32_t) e + 1);
1026     }
1027 
1028     delete temp;
1029 }
1030 
1031 // -------------------------------------
1032 void
check520(Calendar * c,int32_t y,int32_t m,int32_t d,int32_t hr,int32_t min,int32_t sec,int32_t ms,UCalendarDateFields field)1033 CalendarTest::check520(Calendar* c,
1034                         int32_t y, int32_t m, int32_t d,
1035                         int32_t hr, int32_t min, int32_t sec,
1036                         int32_t ms, UCalendarDateFields field)
1037 
1038 {
1039     UErrorCode status = U_ZERO_ERROR;
1040     if (c->get(UCAL_YEAR, status) != y ||
1041         c->get(UCAL_MONTH, status) != m ||
1042         c->get(UCAL_DATE, status) != d ||
1043         c->get(UCAL_HOUR, status) != hr ||
1044         c->get(UCAL_MINUTE, status) != min ||
1045         c->get(UCAL_SECOND, status) != sec ||
1046         c->get(UCAL_MILLISECOND, status) != ms) {
1047         errln(UnicodeString("U_FAILURE for field ") + (int32_t)field +
1048                 ": Expected y/m/d h:m:s:ms of " +
1049                 y + "/" + (m + 1) + "/" + d + " " +
1050               hr + ":" + min + ":" + sec + ":" + ms +
1051               "; got " + c->get(UCAL_YEAR, status) +
1052               "/" + (c->get(UCAL_MONTH, status) + 1) +
1053               "/" + c->get(UCAL_DATE, status) +
1054               " " + c->get(UCAL_HOUR, status) + ":" +
1055               c->get(UCAL_MINUTE, status) + ":" +
1056               c->get(UCAL_SECOND, status) + ":" +
1057               c->get(UCAL_MILLISECOND, status)
1058               );
1059 
1060         if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1061     }
1062     else
1063         logln(UnicodeString("Confirmed: ") + y + "/" +
1064                 (m + 1) + "/" + d + " " +
1065                 hr + ":" + min + ":" + sec + ":" + ms);
1066 }
1067 
1068 // -------------------------------------
1069 void
check520(Calendar * c,int32_t y,int32_t m,int32_t d)1070 CalendarTest::check520(Calendar* c,
1071                         int32_t y, int32_t m, int32_t d)
1072 
1073 {
1074     UErrorCode status = U_ZERO_ERROR;
1075     if (c->get(UCAL_YEAR, status) != y ||
1076         c->get(UCAL_MONTH, status) != m ||
1077         c->get(UCAL_DATE, status) != d) {
1078         errln(UnicodeString("FAILURE: Expected y/m/d of ") +
1079               y + "/" + (m + 1) + "/" + d + " " +
1080               "; got " + c->get(UCAL_YEAR, status) +
1081               "/" + (c->get(UCAL_MONTH, status) + 1) +
1082               "/" + c->get(UCAL_DATE, status)
1083               );
1084 
1085         if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1086     }
1087     else
1088         logln(UnicodeString("Confirmed: ") + y + "/" +
1089                 (m + 1) + "/" + d);
1090 }
1091 
1092 // -------------------------------------
1093 
1094 /**
1095  * Test that setting of fields works.  In particular, make sure that all instances
1096  * of GregorianCalendar don't share a static instance of the fields array.
1097  */
1098 void
TestFieldSet4781()1099 CalendarTest::TestFieldSet4781()
1100 {
1101     // try {
1102         UErrorCode status = U_ZERO_ERROR;
1103         GregorianCalendar *g = new GregorianCalendar(status);
1104         if (failure(status, "new GregorianCalendar", true)) return;
1105         GregorianCalendar *g2 = new GregorianCalendar(status);
1106         if (U_FAILURE(status)) { errln("Couldn't create GregorianCalendar"); return; }
1107         g2->set(UCAL_HOUR, 12, status);
1108         g2->set(UCAL_MINUTE, 0, status);
1109         g2->set(UCAL_SECOND, 0, status);
1110         if (U_FAILURE(status)) { errln("Calendar::set failed"); return; }
1111         if (*g == *g2) logln("Same");
1112         else logln("Different");
1113     //}
1114         //catch(IllegalArgumentException e) {
1115         //errln("Unexpected exception seen: " + e);
1116     //}
1117         delete g;
1118         delete g2;
1119 }
1120 
1121 // -------------------------------------
1122 
1123 /* We don't support serialization on C++
1124 void
1125 CalendarTest::TestSerialize337()
1126 {
1127     Calendar cal = Calendar::getInstance();
1128     UBool ok = false;
1129     try {
1130         FileOutputStream f = new FileOutputStream(FILENAME);
1131         ObjectOutput s = new ObjectOutputStream(f);
1132         s.writeObject(PREFIX);
1133         s.writeObject(cal);
1134         s.writeObject(POSTFIX);
1135         f.close();
1136         FileInputStream in = new FileInputStream(FILENAME);
1137         ObjectInputStream t = new ObjectInputStream(in);
1138         UnicodeString& pre = (UnicodeString&) t.readObject();
1139         Calendar c = (Calendar) t.readObject();
1140         UnicodeString& post = (UnicodeString&) t.readObject();
1141         in.close();
1142         ok = pre.equals(PREFIX) &&
1143             post.equals(POSTFIX) &&
1144             cal->equals(c);
1145         File fl = new File(FILENAME);
1146         fl.delete();
1147     }
1148     catch(IOException e) {
1149         errln("FAIL: Exception received:");
1150         e.printStackTrace(log);
1151     }
1152     catch(ClassNotFoundException e) {
1153         errln("FAIL: Exception received:");
1154         e.printStackTrace(log);
1155     }
1156     if (!ok) errln("Serialization of Calendar object failed.");
1157 }
1158 
1159 UnicodeString& CalendarTest::PREFIX = "abc";
1160 
1161 UnicodeString& CalendarTest::POSTFIX = "def";
1162 
1163 UnicodeString& CalendarTest::FILENAME = "tmp337.bin";
1164  */
1165 
1166 // -------------------------------------
1167 
1168 /**
1169  * Verify that the seconds of a Calendar can be zeroed out through the
1170  * expected sequence of operations.
1171  */
1172 void
TestSecondsZero121()1173 CalendarTest::TestSecondsZero121()
1174 {
1175     UErrorCode status = U_ZERO_ERROR;
1176     Calendar *cal = new GregorianCalendar(status);
1177     if (failure(status, "new GregorianCalendar", true)) return;
1178     cal->setTime(Calendar::getNow(), status);
1179     if (U_FAILURE(status)) { errln("Calendar::setTime failed"); return; }
1180     cal->set(UCAL_SECOND, 0);
1181     if (U_FAILURE(status)) { errln("Calendar::set failed"); return; }
1182     UDate d = cal->getTime(status);
1183     if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
1184     UnicodeString s;
1185     dateToString(d, s);
1186     if (s.indexOf("DATE_FORMAT_FAILURE") >= 0) {
1187         dataerrln("Got: \"DATE_FORMAT_FAILURE\".");
1188     } else if (s.indexOf(":00 ") < 0) {
1189         errln("Expected to see :00 in " + s);
1190     }
1191     delete cal;
1192 }
1193 
1194 // -------------------------------------
1195 
1196 /**
1197  * Verify that a specific sequence of adding and setting works as expected;
1198  * it should not vary depending on when and whether the get method is
1199  * called.
1200  */
1201 void
TestAddSetGet0610()1202 CalendarTest::TestAddSetGet0610()
1203 {
1204     UnicodeString EXPECTED_0610("1993/0/5", "");
1205     UErrorCode status = U_ZERO_ERROR;
1206     {
1207         Calendar *calendar = new GregorianCalendar(status);
1208         if (failure(status, "new GregorianCalendar", true)) return;
1209         calendar->set(1993, UCAL_JANUARY, 4);
1210         logln("1A) " + value(calendar));
1211         calendar->add(UCAL_DATE, 1, status);
1212         if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
1213         UnicodeString v = value(calendar);
1214         logln("1B) " + v);
1215         logln("--) 1993/0/5");
1216         if (!(v == EXPECTED_0610)) errln("Expected " + EXPECTED_0610 + "; saw " + v);
1217         delete calendar;
1218     }
1219     {
1220         Calendar *calendar = new GregorianCalendar(1993, UCAL_JANUARY, 4, status);
1221         if (U_FAILURE(status)) { errln("Couldn't create GregorianCalendar"); return; }
1222         logln("2A) " + value(calendar));
1223         calendar->add(UCAL_DATE, 1, status);
1224         if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
1225         UnicodeString v = value(calendar);
1226         logln("2B) " + v);
1227         logln("--) 1993/0/5");
1228         if (!(v == EXPECTED_0610)) errln("Expected " + EXPECTED_0610 + "; saw " + v);
1229         delete calendar;
1230     }
1231     {
1232         Calendar *calendar = new GregorianCalendar(1993, UCAL_JANUARY, 4, status);
1233         if (U_FAILURE(status)) { errln("Couldn't create GregorianCalendar"); return; }
1234         logln("3A) " + value(calendar));
1235         calendar->getTime(status);
1236         if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
1237         calendar->add(UCAL_DATE, 1, status);
1238         if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
1239         UnicodeString v = value(calendar);
1240         logln("3B) " + v);
1241         logln("--) 1993/0/5");
1242         if (!(v == EXPECTED_0610)) errln("Expected " + EXPECTED_0610 + "; saw " + v);
1243         delete calendar;
1244     }
1245 }
1246 
1247 // -------------------------------------
1248 
1249 UnicodeString
value(Calendar * calendar)1250 CalendarTest::value(Calendar* calendar)
1251 {
1252     UErrorCode status = U_ZERO_ERROR;
1253     return UnicodeString("") + (int32_t)calendar->get(UCAL_YEAR, status) +
1254         "/" + (int32_t)calendar->get(UCAL_MONTH, status) +
1255         "/" + (int32_t)calendar->get(UCAL_DATE, status) +
1256         (U_FAILURE(status) ? " FAIL: Calendar::get failed" : "");
1257 }
1258 
1259 
1260 // -------------------------------------
1261 
1262 /**
1263  * Verify that various fields on a known date are set correctly.
1264  */
1265 void
TestFields060()1266 CalendarTest::TestFields060()
1267 {
1268     UErrorCode status = U_ZERO_ERROR;
1269     int32_t year = 1997;
1270     int32_t month = UCAL_OCTOBER;
1271     int32_t dDate = 22;
1272     GregorianCalendar* calendar = nullptr;
1273     calendar = new GregorianCalendar(year, month, dDate, status);
1274     if (failure(status, "new GregorianCalendar", true)) return;
1275     for (int32_t i = 0; i < EXPECTED_FIELDS_length;) {
1276         UCalendarDateFields field = (UCalendarDateFields)EXPECTED_FIELDS[i++];
1277         int32_t expected = EXPECTED_FIELDS[i++];
1278         if (calendar->get(field, status) != expected) {
1279             errln(UnicodeString("Expected field ") + (int32_t)field + " to have value " + (int32_t)expected +
1280                   "; received " + (int32_t)calendar->get(field, status) + " instead");
1281             if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1282         }
1283     }
1284     delete calendar;
1285 }
1286 
1287 int32_t CalendarTest::EXPECTED_FIELDS[] = {
1288     UCAL_YEAR, 1997,
1289     UCAL_MONTH, UCAL_OCTOBER,
1290     UCAL_DATE, 22,
1291     UCAL_DAY_OF_WEEK, UCAL_WEDNESDAY,
1292     UCAL_DAY_OF_WEEK_IN_MONTH, 4,
1293     UCAL_DAY_OF_YEAR, 295
1294 };
1295 
1296 const int32_t CalendarTest::EXPECTED_FIELDS_length = (int32_t)(sizeof(CalendarTest::EXPECTED_FIELDS) /
1297     sizeof(CalendarTest::EXPECTED_FIELDS[0]));
1298 
1299 // -------------------------------------
1300 
1301 /**
1302  * Verify that various fields on a known date are set correctly.  In this
1303  * case, the start of the epoch (January 1 1970).
1304  */
1305 void
TestEpochStartFields()1306 CalendarTest::TestEpochStartFields()
1307 {
1308     UErrorCode status = U_ZERO_ERROR;
1309     TimeZone *z = TimeZone::createDefault();
1310     Calendar *c = Calendar::createInstance(status);
1311     if (failure(status, "Calendar::createInstance", true)) return;
1312     UDate d = - z->getRawOffset();
1313     GregorianCalendar *gc = new GregorianCalendar(status);
1314     if (U_FAILURE(status)) { errln("Couldn't create GregorianCalendar"); return; }
1315     gc->setTimeZone(*z);
1316     gc->setTime(d, status);
1317     if (U_FAILURE(status)) { errln("Calendar::setTime failed"); return; }
1318     UBool idt = gc->inDaylightTime(status);
1319     if (U_FAILURE(status)) { errln("GregorianCalendar::inDaylightTime failed"); return; }
1320     if (idt) {
1321         UnicodeString str;
1322         logln("Warning: Skipping test because " + dateToString(d, str) + " is in DST.");
1323     }
1324     else {
1325         c->setTime(d, status);
1326         if (U_FAILURE(status)) { errln("Calendar::setTime failed"); return; }
1327         for (int32_t i = 0; i < UCAL_ZONE_OFFSET;++i) {
1328             if (c->get((UCalendarDateFields)i, status) != EPOCH_FIELDS[i])
1329                 dataerrln(UnicodeString("Expected field ") + i + " to have value " + EPOCH_FIELDS[i] +
1330                       "; saw " + c->get((UCalendarDateFields)i, status) + " instead");
1331             if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1332         }
1333         if (c->get(UCAL_ZONE_OFFSET, status) != z->getRawOffset())
1334         {
1335             errln(UnicodeString("Expected field ZONE_OFFSET to have value ") + z->getRawOffset() +
1336                   "; saw " + c->get(UCAL_ZONE_OFFSET, status) + " instead");
1337             if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1338         }
1339         if (c->get(UCAL_DST_OFFSET, status) != 0)
1340         {
1341             errln(UnicodeString("Expected field DST_OFFSET to have value 0") +
1342                   "; saw " + c->get(UCAL_DST_OFFSET, status) + " instead");
1343             if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1344         }
1345     }
1346     delete c;
1347     delete z;
1348     delete gc;
1349 }
1350 
1351 int32_t CalendarTest::EPOCH_FIELDS[] = {
1352     1, 1970, 0, 1, 1, 1, 1, 5, 1, 0, 0, 0, 0, 0, 0, - 28800000, 0
1353 };
1354 
1355 // -------------------------------------
1356 
1357 /**
1358  * Test that the days of the week progress properly when add is called repeatedly
1359  * for increments of 24 days.
1360  */
1361 void
TestDOWProgression()1362 CalendarTest::TestDOWProgression()
1363 {
1364     UErrorCode status = U_ZERO_ERROR;
1365     Calendar *cal = new GregorianCalendar(1972, UCAL_OCTOBER, 26, status);
1366     if (failure(status, "new GregorianCalendar", true)) return;
1367     marchByDelta(cal, 24);
1368     delete cal;
1369 }
1370 
1371 // -------------------------------------
1372 
1373 void
TestDOW_LOCALandYEAR_WOY()1374 CalendarTest::TestDOW_LOCALandYEAR_WOY()
1375 {
1376     /* Note: I've commented out the loop_addroll tests for YEAR and
1377      * YEAR_WOY below because these two fields should NOT behave
1378      * identically when adding.  YEAR should keep the month/dom
1379      * invariant.  YEAR_WOY should keep the woy/dow invariant.  I've
1380      * added a new test that checks for this in place of the old call
1381      * to loop_addroll. - aliu */
1382     UErrorCode status = U_ZERO_ERROR;
1383     int32_t times = 20;
1384     Calendar *cal=Calendar::createInstance(Locale::getGermany(), status);
1385     if (failure(status, "Calendar::createInstance", true)) return;
1386     SimpleDateFormat *sdf=new SimpleDateFormat(UnicodeString("YYYY'-W'ww-ee"), Locale::getGermany(), status);
1387     if (U_FAILURE(status)) { dataerrln("Couldn't create SimpleDateFormat - %s", u_errorName(status)); return; }
1388 
1389     // ICU no longer use localized date-time pattern characters by default.
1390     // So we set pattern chars using 'J' instead of 'Y'.
1391     DateFormatSymbols *dfs = new DateFormatSymbols(Locale::getGermany(), status);
1392     dfs->setLocalPatternChars(UnicodeString("GyMdkHmsSEDFwWahKzJeugAZvcLQq"));
1393     sdf->adoptDateFormatSymbols(dfs);
1394     sdf->applyLocalizedPattern(UnicodeString("JJJJ'-W'ww-ee"), status);
1395     if (U_FAILURE(status)) { errln("Couldn't apply localized pattern"); return; }
1396 
1397     cal->clear();
1398     cal->set(1997, UCAL_DECEMBER, 25);
1399     doYEAR_WOYLoop(cal, sdf, times, status);
1400     //loop_addroll(cal, /*sdf,*/ times, UCAL_YEAR_WOY, UCAL_YEAR,  status);
1401     yearAddTest(*cal, status); // aliu
1402     loop_addroll(cal, /*sdf,*/ times, UCAL_DOW_LOCAL, UCAL_DAY_OF_WEEK, status);
1403     if (U_FAILURE(status)) { errln("Error in parse/calculate test for 1997"); return; }
1404 
1405     cal->clear();
1406     cal->set(1998, UCAL_DECEMBER, 25);
1407     doYEAR_WOYLoop(cal, sdf, times, status);
1408     //loop_addroll(cal, /*sdf,*/ times, UCAL_YEAR_WOY, UCAL_YEAR,  status);
1409     yearAddTest(*cal, status); // aliu
1410     loop_addroll(cal, /*sdf,*/ times, UCAL_DOW_LOCAL, UCAL_DAY_OF_WEEK, status);
1411     if (U_FAILURE(status)) { errln("Error in parse/calculate test for 1998"); return; }
1412 
1413     cal->clear();
1414     cal->set(1582, UCAL_OCTOBER, 1);
1415     doYEAR_WOYLoop(cal, sdf, times, status);
1416     //loop_addroll(cal, /*sdf,*/ times, Calendar::YEAR_WOY, Calendar::YEAR,  status);
1417     yearAddTest(*cal, status); // aliu
1418     loop_addroll(cal, /*sdf,*/ times, UCAL_DOW_LOCAL, UCAL_DAY_OF_WEEK, status);
1419     if (U_FAILURE(status)) { errln("Error in parse/calculate test for 1582"); return; }
1420     delete sdf;
1421     delete cal;
1422 }
1423 
1424 /**
1425  * Confirm that adding a YEAR and adding a YEAR_WOY work properly for
1426  * the given Calendar at its current setting.
1427  */
yearAddTest(Calendar & cal,UErrorCode & status)1428 void CalendarTest::yearAddTest(Calendar& cal, UErrorCode& status) {
1429     /**
1430      * When adding the YEAR, the month and day should remain constant.
1431      * When adding the YEAR_WOY, the WOY and DOW should remain constant. - aliu
1432      * Examples:
1433      *  Wed Jan 14 1998 / 1998-W03-03 Add(YEAR_WOY, 1) -> Wed Jan 20 1999 / 1999-W03-03
1434      *                                Add(YEAR, 1)     -> Thu Jan 14 1999 / 1999-W02-04
1435      *  Thu Jan 14 1999 / 1999-W02-04 Add(YEAR_WOY, 1) -> Thu Jan 13 2000 / 2000-W02-04
1436      *                                Add(YEAR, 1)     -> Fri Jan 14 2000 / 2000-W02-05
1437      *  Sun Oct 31 1582 / 1582-W42-07 Add(YEAR_WOY, 1) -> Sun Oct 23 1583 / 1583-W42-07
1438      *                                Add(YEAR, 1)     -> Mon Oct 31 1583 / 1583-W44-01
1439      */
1440     int32_t y   = cal.get(UCAL_YEAR, status);
1441     int32_t mon = cal.get(UCAL_MONTH, status);
1442     int32_t day = cal.get(UCAL_DATE, status);
1443     int32_t ywy = cal.get(UCAL_YEAR_WOY, status);
1444     int32_t woy = cal.get(UCAL_WEEK_OF_YEAR, status);
1445     int32_t dow = cal.get(UCAL_DOW_LOCAL, status);
1446     UDate t = cal.getTime(status);
1447 
1448     if(U_FAILURE(status)){
1449         errln(UnicodeString("Failed to create Calendar for locale. Error: ") + UnicodeString(u_errorName(status)));
1450         return;
1451     }
1452     UnicodeString str, str2;
1453     SimpleDateFormat fmt(UnicodeString("EEE MMM dd yyyy / YYYY'-W'ww-ee"), status);
1454     fmt.setCalendar(cal);
1455 
1456     fmt.format(t, str.remove());
1457     str += ".add(YEAR, 1)    =>";
1458     cal.add(UCAL_YEAR, 1, status);
1459     int32_t y2   = cal.get(UCAL_YEAR, status);
1460     int32_t mon2 = cal.get(UCAL_MONTH, status);
1461     int32_t day2 = cal.get(UCAL_DATE, status);
1462     fmt.format(cal.getTime(status), str);
1463     if (y2 != (y+1) || mon2 != mon || day2 != day) {
1464         str += (UnicodeString)", expected year " +
1465             (y+1) + ", month " + (mon+1) + ", day " + day;
1466         errln((UnicodeString)"FAIL: " + str);
1467         logln( UnicodeString(" -> ") + CalendarTest::calToStr(cal) );
1468     } else {
1469         logln(str);
1470     }
1471 
1472     fmt.format(t, str.remove());
1473     str += ".add(YEAR_WOY, 1)=>";
1474     cal.setTime(t, status);
1475     logln( UnicodeString(" <- ") + CalendarTest::calToStr(cal) );
1476     cal.add(UCAL_YEAR_WOY, 1, status);
1477     int32_t ywy2 = cal.get(UCAL_YEAR_WOY, status);
1478     int32_t woy2 = cal.get(UCAL_WEEK_OF_YEAR, status);
1479     int32_t dow2 = cal.get(UCAL_DOW_LOCAL, status);
1480     fmt.format(cal.getTime(status), str);
1481     if (ywy2 != (ywy+1) || woy2 != woy || dow2 != dow) {
1482         str += (UnicodeString)", expected yearWOY " +
1483             (ywy+1) + ", woy " + woy + ", dowLocal " + dow;
1484         errln((UnicodeString)"FAIL: " + str);
1485         logln( UnicodeString(" -> ") + CalendarTest::calToStr(cal) );
1486     } else {
1487         logln(str);
1488     }
1489 }
1490 
1491 // -------------------------------------
1492 
loop_addroll(Calendar * cal,int times,UCalendarDateFields field,UCalendarDateFields field2,UErrorCode & errorCode)1493 void CalendarTest::loop_addroll(Calendar *cal, /*SimpleDateFormat *sdf,*/ int times, UCalendarDateFields field, UCalendarDateFields field2, UErrorCode& errorCode) {
1494     Calendar *calclone;
1495     SimpleDateFormat fmt(UnicodeString("EEE MMM dd yyyy / YYYY'-W'ww-ee"), errorCode);
1496     fmt.setCalendar(*cal);
1497     int i;
1498 
1499     for(i = 0; i<times; i++) {
1500         calclone = cal->clone();
1501         UDate start = cal->getTime(errorCode);
1502         cal->add(field,1,errorCode);
1503         if (U_FAILURE(errorCode)) { errln("Error in add"); delete calclone; return; }
1504         calclone->add(field2,1,errorCode);
1505         if (U_FAILURE(errorCode)) { errln("Error in add"); delete calclone; return; }
1506         if(cal->getTime(errorCode) != calclone->getTime(errorCode)) {
1507             UnicodeString str("FAIL: Results of add differ. "), str2;
1508             str += fmt.format(start, str2) + " ";
1509             str += UnicodeString("Add(") + fieldName(field) + ", 1) -> " +
1510                 fmt.format(cal->getTime(errorCode), str2.remove()) + "; ";
1511             str += UnicodeString("Add(") + fieldName(field2) + ", 1) -> " +
1512                 fmt.format(calclone->getTime(errorCode), str2.remove());
1513             errln(str);
1514             delete calclone;
1515             return;
1516         }
1517         delete calclone;
1518     }
1519 
1520     for(i = 0; i<times; i++) {
1521         calclone = cal->clone();
1522         cal->roll(field,(int32_t)1,errorCode);
1523         if (U_FAILURE(errorCode)) { errln("Error in roll"); delete calclone; return; }
1524         calclone->roll(field2,(int32_t)1,errorCode);
1525         if (U_FAILURE(errorCode)) { errln("Error in roll"); delete calclone; return; }
1526         if(cal->getTime(errorCode) != calclone->getTime(errorCode)) {
1527             delete calclone;
1528             errln("Results of roll differ!");
1529             return;
1530         }
1531         delete calclone;
1532     }
1533 }
1534 
1535 // -------------------------------------
1536 
1537 void
doYEAR_WOYLoop(Calendar * cal,SimpleDateFormat * sdf,int32_t times,UErrorCode & errorCode)1538 CalendarTest::doYEAR_WOYLoop(Calendar *cal, SimpleDateFormat *sdf,
1539                                     int32_t times, UErrorCode& errorCode) {
1540 
1541     UnicodeString us;
1542     UDate tst, original;
1543     Calendar *tstres = new GregorianCalendar(Locale::getGermany(), errorCode);
1544     for(int i=0; i<times; ++i) {
1545         sdf->format(Formattable(cal->getTime(errorCode),Formattable::kIsDate), us, errorCode);
1546         //logln("expected: "+us);
1547         if (U_FAILURE(errorCode)) { errln("Format error"); return; }
1548         tst=sdf->parse(us,errorCode);
1549         if (U_FAILURE(errorCode)) { errln("Parse error"); return; }
1550         tstres->clear();
1551         tstres->setTime(tst, errorCode);
1552         //logln((UnicodeString)"Parsed week of year is "+tstres->get(UCAL_WEEK_OF_YEAR, errorCode));
1553         if (U_FAILURE(errorCode)) { errln("Set time error"); return; }
1554         original = cal->getTime(errorCode);
1555         us.remove();
1556         sdf->format(Formattable(tst,Formattable::kIsDate), us, errorCode);
1557         //logln("got: "+us);
1558         if (U_FAILURE(errorCode)) { errln("Get time error"); return; }
1559         if(original!=tst) {
1560             us.remove();
1561             sdf->format(Formattable(original, Formattable::kIsDate), us, errorCode);
1562             errln("FAIL: Parsed time doesn't match with regular");
1563             logln("expected "+us + " " + calToStr(*cal));
1564             us.remove();
1565             sdf->format(Formattable(tst, Formattable::kIsDate), us, errorCode);
1566             logln("got "+us + " " + calToStr(*tstres));
1567         }
1568         tstres->clear();
1569         tstres->set(UCAL_YEAR_WOY, cal->get(UCAL_YEAR_WOY, errorCode));
1570         tstres->set(UCAL_WEEK_OF_YEAR, cal->get(UCAL_WEEK_OF_YEAR, errorCode));
1571         tstres->set(UCAL_DOW_LOCAL, cal->get(UCAL_DOW_LOCAL, errorCode));
1572         if(cal->get(UCAL_YEAR, errorCode) != tstres->get(UCAL_YEAR, errorCode)) {
1573             errln("FAIL: Different Year!");
1574             logln((UnicodeString)"Expected "+cal->get(UCAL_YEAR, errorCode));
1575             logln((UnicodeString)"Got "+tstres->get(UCAL_YEAR, errorCode));
1576             return;
1577         }
1578         if(cal->get(UCAL_DAY_OF_YEAR, errorCode) != tstres->get(UCAL_DAY_OF_YEAR, errorCode)) {
1579             errln("FAIL: Different Day Of Year!");
1580             logln((UnicodeString)"Expected "+cal->get(UCAL_DAY_OF_YEAR, errorCode));
1581             logln((UnicodeString)"Got "+tstres->get(UCAL_DAY_OF_YEAR, errorCode));
1582             return;
1583         }
1584         //logln(calToStr(*cal));
1585         cal->add(UCAL_DATE, 1, errorCode);
1586         if (U_FAILURE(errorCode)) { errln("Add error"); return; }
1587         us.remove();
1588     }
1589     delete (tstres);
1590 }
1591 // -------------------------------------
1592 
1593 void
marchByDelta(Calendar * cal,int32_t delta)1594 CalendarTest::marchByDelta(Calendar* cal, int32_t delta)
1595 {
1596     UErrorCode status = U_ZERO_ERROR;
1597     Calendar *cur = cal->clone();
1598     int32_t initialDOW = cur->get(UCAL_DAY_OF_WEEK, status);
1599     if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1600     int32_t DOW, newDOW = initialDOW;
1601     do {
1602         UnicodeString str;
1603         DOW = newDOW;
1604         logln(UnicodeString("DOW = ") + DOW + "  " + dateToString(cur->getTime(status), str));
1605         if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
1606         cur->add(UCAL_DAY_OF_WEEK, delta, status);
1607         if (U_FAILURE(status)) { errln("Calendar::add failed"); return; }
1608         newDOW = cur->get(UCAL_DAY_OF_WEEK, status);
1609         if (U_FAILURE(status)) { errln("Calendar::get failed"); return; }
1610         int32_t expectedDOW = 1 + (DOW + delta - 1) % 7;
1611         if (newDOW != expectedDOW) {
1612             errln(UnicodeString("Day of week should be ") + expectedDOW + " instead of " + newDOW +
1613                   " on " + dateToString(cur->getTime(status), str));
1614             if (U_FAILURE(status)) { errln("Calendar::getTime failed"); return; }
1615             return;
1616         }
1617     }
1618     while (newDOW != initialDOW);
1619     delete cur;
1620 }
1621 
1622 #define CHECK(status, msg) UPRV_BLOCK_MACRO_BEGIN { \
1623     if (U_FAILURE(status)) { \
1624         errcheckln(status, msg); \
1625         return; \
1626     } \
1627 } UPRV_BLOCK_MACRO_END
1628 
TestWOY()1629 void CalendarTest::TestWOY() {
1630     /*
1631       FDW = Mon, MDFW = 4:
1632          Sun Dec 26 1999, WOY 51
1633          Mon Dec 27 1999, WOY 52
1634          Tue Dec 28 1999, WOY 52
1635          Wed Dec 29 1999, WOY 52
1636          Thu Dec 30 1999, WOY 52
1637          Fri Dec 31 1999, WOY 52
1638          Sat Jan 01 2000, WOY 52 ***
1639          Sun Jan 02 2000, WOY 52 ***
1640          Mon Jan 03 2000, WOY 1
1641          Tue Jan 04 2000, WOY 1
1642          Wed Jan 05 2000, WOY 1
1643          Thu Jan 06 2000, WOY 1
1644          Fri Jan 07 2000, WOY 1
1645          Sat Jan 08 2000, WOY 1
1646          Sun Jan 09 2000, WOY 1
1647          Mon Jan 10 2000, WOY 2
1648 
1649       FDW = Mon, MDFW = 2:
1650          Sun Dec 26 1999, WOY 52
1651          Mon Dec 27 1999, WOY 1  ***
1652          Tue Dec 28 1999, WOY 1  ***
1653          Wed Dec 29 1999, WOY 1  ***
1654          Thu Dec 30 1999, WOY 1  ***
1655          Fri Dec 31 1999, WOY 1  ***
1656          Sat Jan 01 2000, WOY 1
1657          Sun Jan 02 2000, WOY 1
1658          Mon Jan 03 2000, WOY 2
1659          Tue Jan 04 2000, WOY 2
1660          Wed Jan 05 2000, WOY 2
1661          Thu Jan 06 2000, WOY 2
1662          Fri Jan 07 2000, WOY 2
1663          Sat Jan 08 2000, WOY 2
1664          Sun Jan 09 2000, WOY 2
1665          Mon Jan 10 2000, WOY 3
1666     */
1667 
1668     UnicodeString str;
1669     UErrorCode status = U_ZERO_ERROR;
1670     int32_t i;
1671 
1672     GregorianCalendar cal(status);
1673     SimpleDateFormat fmt(UnicodeString("EEE MMM dd yyyy', WOY' w"), status);
1674     if (failure(status, "Cannot construct calendar/format", true)) return;
1675 
1676     UCalendarDaysOfWeek fdw = (UCalendarDaysOfWeek) 0;
1677 
1678     //for (int8_t pass=2; pass<=2; ++pass) {
1679     for (int8_t pass=1; pass<=2; ++pass) {
1680         switch (pass) {
1681         case 1:
1682             fdw = UCAL_MONDAY;
1683             cal.setFirstDayOfWeek(fdw);
1684             cal.setMinimalDaysInFirstWeek(4);
1685             fmt.adoptCalendar(cal.clone());
1686             break;
1687         case 2:
1688             fdw = UCAL_MONDAY;
1689             cal.setFirstDayOfWeek(fdw);
1690             cal.setMinimalDaysInFirstWeek(2);
1691             fmt.adoptCalendar(cal.clone());
1692             break;
1693         }
1694 
1695         //for (i=2; i<=6; ++i) {
1696         for (i=0; i<16; ++i) {
1697         UDate t, t2;
1698         int32_t t_y, t_woy, t_dow;
1699         cal.clear();
1700         cal.set(1999, UCAL_DECEMBER, 26 + i);
1701         fmt.format(t = cal.getTime(status), str.remove());
1702         CHECK(status, "Fail: getTime failed");
1703         logln(UnicodeString("* ") + str);
1704         int32_t dow = cal.get(UCAL_DAY_OF_WEEK, status);
1705         int32_t woy = cal.get(UCAL_WEEK_OF_YEAR, status);
1706         int32_t year = cal.get(UCAL_YEAR, status);
1707         int32_t mon = cal.get(UCAL_MONTH, status);
1708         logln(calToStr(cal));
1709         CHECK(status, "Fail: get failed");
1710         int32_t dowLocal = dow - fdw;
1711         if (dowLocal < 0) dowLocal += 7;
1712         dowLocal++;
1713         int32_t yearWoy = year;
1714         if (mon == UCAL_JANUARY) {
1715             if (woy >= 52) --yearWoy;
1716         } else {
1717             if (woy == 1) ++yearWoy;
1718         }
1719 
1720         // Basic fields->time check y/woy/dow
1721         // Since Y/WOY is ambiguous, we do a check of the fields,
1722         // not of the specific time.
1723         cal.clear();
1724         cal.set(UCAL_YEAR, year);
1725         cal.set(UCAL_WEEK_OF_YEAR, woy);
1726         cal.set(UCAL_DAY_OF_WEEK, dow);
1727         t_y = cal.get(UCAL_YEAR, status);
1728         t_woy = cal.get(UCAL_WEEK_OF_YEAR, status);
1729         t_dow = cal.get(UCAL_DAY_OF_WEEK, status);
1730         CHECK(status, "Fail: get failed");
1731         if (t_y != year || t_woy != woy || t_dow != dow) {
1732             str = "Fail: y/woy/dow fields->time => ";
1733             fmt.format(cal.getTime(status), str);
1734             errln(str);
1735             logln(calToStr(cal));
1736             logln("[get!=set] Y%d!=%d || woy%d!=%d || dow%d!=%d\n",
1737                   t_y, year, t_woy, woy, t_dow, dow);
1738         } else {
1739           logln("y/woy/dow fields->time OK");
1740         }
1741 
1742         // Basic fields->time check y/woy/dow_local
1743         // Since Y/WOY is ambiguous, we do a check of the fields,
1744         // not of the specific time.
1745         cal.clear();
1746         cal.set(UCAL_YEAR, year);
1747         cal.set(UCAL_WEEK_OF_YEAR, woy);
1748         cal.set(UCAL_DOW_LOCAL, dowLocal);
1749         t_y = cal.get(UCAL_YEAR, status);
1750         t_woy = cal.get(UCAL_WEEK_OF_YEAR, status);
1751         t_dow = cal.get(UCAL_DOW_LOCAL, status);
1752         CHECK(status, "Fail: get failed");
1753         if (t_y != year || t_woy != woy || t_dow != dowLocal) {
1754             str = "Fail: y/woy/dow_local fields->time => ";
1755             fmt.format(cal.getTime(status), str);
1756             errln(str);
1757         }
1758 
1759         // Basic fields->time check y_woy/woy/dow
1760         cal.clear();
1761         cal.set(UCAL_YEAR_WOY, yearWoy);
1762         cal.set(UCAL_WEEK_OF_YEAR, woy);
1763         cal.set(UCAL_DAY_OF_WEEK, dow);
1764         t2 = cal.getTime(status);
1765         CHECK(status, "Fail: getTime failed");
1766         if (t != t2) {
1767             str = "Fail: y_woy/woy/dow fields->time => ";
1768             fmt.format(t2, str);
1769             errln(str);
1770             logln(calToStr(cal));
1771             logln("%.f != %.f\n", t, t2);
1772         } else {
1773           logln("y_woy/woy/dow OK");
1774         }
1775 
1776         // Basic fields->time check y_woy/woy/dow_local
1777         cal.clear();
1778         cal.set(UCAL_YEAR_WOY, yearWoy);
1779         cal.set(UCAL_WEEK_OF_YEAR, woy);
1780         cal.set(UCAL_DOW_LOCAL, dowLocal);
1781         t2 = cal.getTime(status);
1782         CHECK(status, "Fail: getTime failed");
1783         if (t != t2) {
1784             str = "Fail: y_woy/woy/dow_local fields->time => ";
1785             fmt.format(t2, str);
1786             errln(str);
1787         }
1788 
1789         logln("Testing DOW_LOCAL.. dow%d\n", dow);
1790         // Make sure DOW_LOCAL disambiguates over DOW
1791         int32_t wrongDow = dow - 3;
1792         if (wrongDow < 1) wrongDow += 7;
1793         cal.setTime(t, status);
1794         cal.set(UCAL_DAY_OF_WEEK, wrongDow);
1795         cal.set(UCAL_DOW_LOCAL, dowLocal);
1796         t2 = cal.getTime(status);
1797         CHECK(status, "Fail: set/getTime failed");
1798         if (t != t2) {
1799             str = "Fail: DOW_LOCAL fields->time => ";
1800             fmt.format(t2, str);
1801             errln(str);
1802             logln(calToStr(cal));
1803             logln("%.f :   DOW%d, DOW_LOCAL%d -> %.f\n",
1804                   t, wrongDow, dowLocal, t2);
1805         }
1806 
1807         // Make sure DOW disambiguates over DOW_LOCAL
1808         int32_t wrongDowLocal = dowLocal - 3;
1809         if (wrongDowLocal < 1) wrongDowLocal += 7;
1810         cal.setTime(t, status);
1811         cal.set(UCAL_DOW_LOCAL, wrongDowLocal);
1812         cal.set(UCAL_DAY_OF_WEEK, dow);
1813         t2 = cal.getTime(status);
1814         CHECK(status, "Fail: set/getTime failed");
1815         if (t != t2) {
1816             str = "Fail: DOW       fields->time => ";
1817             fmt.format(t2, str);
1818             errln(str);
1819         }
1820 
1821         // Make sure YEAR_WOY disambiguates over YEAR
1822         cal.setTime(t, status);
1823         cal.set(UCAL_YEAR, year - 2);
1824         cal.set(UCAL_YEAR_WOY, yearWoy);
1825         t2 = cal.getTime(status);
1826         CHECK(status, "Fail: set/getTime failed");
1827         if (t != t2) {
1828             str = "Fail: YEAR_WOY  fields->time => ";
1829             fmt.format(t2, str);
1830             errln(str);
1831         }
1832 
1833         // Make sure YEAR disambiguates over YEAR_WOY
1834         cal.setTime(t, status);
1835         cal.set(UCAL_YEAR_WOY, yearWoy - 2);
1836         cal.set(UCAL_YEAR, year);
1837         t2 = cal.getTime(status);
1838         CHECK(status, "Fail: set/getTime failed");
1839         if (t != t2) {
1840             str = "Fail: YEAR      fields->time => ";
1841             fmt.format(t2, str);
1842             errln(str);
1843         }
1844     }
1845     }
1846 
1847     /*
1848       FDW = Mon, MDFW = 4:
1849          Sun Dec 26 1999, WOY 51
1850          Mon Dec 27 1999, WOY 52
1851          Tue Dec 28 1999, WOY 52
1852          Wed Dec 29 1999, WOY 52
1853          Thu Dec 30 1999, WOY 52
1854          Fri Dec 31 1999, WOY 52
1855          Sat Jan 01 2000, WOY 52
1856          Sun Jan 02 2000, WOY 52
1857     */
1858 
1859     // Roll the DOW_LOCAL within week 52
1860     for (i=27; i<=33; ++i) {
1861         int32_t amount;
1862         for (amount=-7; amount<=7; ++amount) {
1863             str = "roll(";
1864             cal.set(1999, UCAL_DECEMBER, i);
1865             UDate t, t2;
1866             fmt.format(cal.getTime(status), str);
1867             CHECK(status, "Fail: getTime failed");
1868             str += UnicodeString(", ") + amount + ") = ";
1869 
1870             cal.roll(UCAL_DOW_LOCAL, amount, status);
1871             CHECK(status, "Fail: roll failed");
1872 
1873             t = cal.getTime(status);
1874             int32_t newDom = i + amount;
1875             while (newDom < 27) newDom += 7;
1876             while (newDom > 33) newDom -= 7;
1877             cal.set(1999, UCAL_DECEMBER, newDom);
1878             t2 = cal.getTime(status);
1879             CHECK(status, "Fail: getTime failed");
1880             fmt.format(t, str);
1881 
1882             if (t != t2) {
1883                 str.append(", exp ");
1884                 fmt.format(t2, str);
1885                 errln(str);
1886             } else {
1887                 logln(str);
1888             }
1889         }
1890     }
1891 }
1892 
TestYWOY()1893 void CalendarTest::TestYWOY()
1894 {
1895    UnicodeString str;
1896    UErrorCode status = U_ZERO_ERROR;
1897 
1898    GregorianCalendar cal(status);
1899    if (failure(status, "construct GregorianCalendar", true)) return;
1900 
1901    cal.setFirstDayOfWeek(UCAL_SUNDAY);
1902    cal.setMinimalDaysInFirstWeek(1);
1903 
1904    logln("Setting:  ywoy=2004, woy=1, dow=MONDAY");
1905    cal.clear();
1906    cal.set(UCAL_YEAR_WOY,2004);
1907    cal.set(UCAL_WEEK_OF_YEAR,1);
1908    cal.set(UCAL_DAY_OF_WEEK, UCAL_MONDAY);
1909 
1910    logln(calToStr(cal));
1911    if(cal.get(UCAL_YEAR, status) != 2003) {
1912      errln("year not 2003");
1913    }
1914 
1915    logln("+ setting DOW to THURSDAY");
1916    cal.clear();
1917    cal.set(UCAL_YEAR_WOY,2004);
1918    cal.set(UCAL_WEEK_OF_YEAR,1);
1919    cal.set(UCAL_DAY_OF_WEEK, UCAL_THURSDAY);
1920 
1921    logln(calToStr(cal));
1922    if(cal.get(UCAL_YEAR, status) != 2004) {
1923      errln("year not 2004");
1924    }
1925 
1926    logln("+ setting DOW_LOCAL to 1");
1927    cal.clear();
1928    cal.set(UCAL_YEAR_WOY,2004);
1929    cal.set(UCAL_WEEK_OF_YEAR,1);
1930    cal.set(UCAL_DAY_OF_WEEK, UCAL_THURSDAY);
1931    cal.set(UCAL_DOW_LOCAL, 1);
1932 
1933    logln(calToStr(cal));
1934    if(cal.get(UCAL_YEAR, status) != 2003) {
1935      errln("year not 2003");
1936    }
1937 
1938    cal.setFirstDayOfWeek(UCAL_MONDAY);
1939    cal.setMinimalDaysInFirstWeek(4);
1940    UDate t = 946713600000.;
1941    cal.setTime(t, status);
1942    cal.set(UCAL_DAY_OF_WEEK, 4);
1943    cal.set(UCAL_DOW_LOCAL, 6);
1944    if(cal.getTime(status) != t) {
1945      logln(calToStr(cal));
1946      errln("FAIL:  DOW_LOCAL did not take precedence");
1947    }
1948 
1949 }
1950 
TestJD()1951 void CalendarTest::TestJD()
1952 {
1953   int32_t jd;
1954   static const int32_t kEpochStartAsJulianDay = 2440588;
1955   UErrorCode status = U_ZERO_ERROR;
1956   GregorianCalendar cal(status);
1957   if (failure(status, "construct GregorianCalendar", true)) return;
1958   cal.setTimeZone(*TimeZone::getGMT());
1959   cal.clear();
1960   jd = cal.get(UCAL_JULIAN_DAY, status);
1961   if(jd != kEpochStartAsJulianDay) {
1962     errln("Wanted JD of %d at time=0, [epoch 1970] but got %d\n", kEpochStartAsJulianDay, jd);
1963   } else {
1964     logln("Wanted JD of %d at time=0, [epoch 1970], got %d\n", kEpochStartAsJulianDay, jd);
1965   }
1966 
1967   cal.setTime(Calendar::getNow(), status);
1968   cal.clear();
1969   cal.set(UCAL_JULIAN_DAY, kEpochStartAsJulianDay);
1970   UDate epochTime = cal.getTime(status);
1971   if(epochTime != 0) {
1972     errln("Wanted time of 0 at jd=%d, got %.1lf\n", kEpochStartAsJulianDay, epochTime);
1973   } else {
1974     logln("Wanted time of 0 at jd=%d, got %.1lf\n", kEpochStartAsJulianDay, epochTime);
1975   }
1976 
1977 }
1978 
1979 // make sure the ctestfw utilities are in sync with the Calendar
TestDebug()1980 void CalendarTest::TestDebug()
1981 {
1982     for(int32_t  t=0;t<=UDBG_ENUM_COUNT;t++) {
1983         int32_t count = udbg_enumCount((UDebugEnumType)t);
1984         if(count == -1) {
1985             logln("enumCount(%d) returned -1", count);
1986             continue;
1987         }
1988         for(int32_t i=0;i<=count;i++) {
1989             if(t<=UDBG_HIGHEST_CONTIGUOUS_ENUM && i<count) {
1990                 if( i!=udbg_enumArrayValue((UDebugEnumType)t, i)) {
1991                     errln("FAIL: udbg_enumArrayValue(%d,%d) returned %d, expected %d", t, i, udbg_enumArrayValue((UDebugEnumType)t,i), i);
1992                 }
1993             } else {
1994                 logln("Testing count+1:");
1995             }
1996                   const char *name = udbg_enumName((UDebugEnumType)t,i);
1997                   if(name==nullptr) {
1998                           if(i==count || t>UDBG_HIGHEST_CONTIGUOUS_ENUM  ) {
1999                                 logln(" null name - expected.\n");
2000                           } else {
2001                                 errln("FAIL: udbg_enumName(%d,%d) returned nullptr", t, i);
2002                           }
2003                           name = "(null)";
2004                   }
2005           logln("udbg_enumArrayValue(%d,%d) = %s, returned %d", t, i,
2006                       name, udbg_enumArrayValue((UDebugEnumType)t,i));
2007             logln("udbg_enumString = " + udbg_enumString((UDebugEnumType)t,i));
2008         }
2009         if(udbg_enumExpectedCount((UDebugEnumType)t) != count && t<=UDBG_HIGHEST_CONTIGUOUS_ENUM) {
2010             errln("FAIL: udbg_enumExpectedCount(%d): %d, != UCAL_FIELD_COUNT=%d ", t, udbg_enumExpectedCount((UDebugEnumType)t), count);
2011         } else {
2012             logln("udbg_ucal_fieldCount: %d, UCAL_FIELD_COUNT=udbg_enumCount %d ", udbg_enumExpectedCount((UDebugEnumType)t), count);
2013         }
2014     }
2015 }
2016 
2017 
2018 #undef CHECK
2019 
2020 // List of interesting locales
testLocaleID(int32_t i)2021 const char *CalendarTest::testLocaleID(int32_t i)
2022 {
2023   switch(i) {
2024   case 0: return "he_IL@calendar=hebrew";
2025   case 1: return "en_US@calendar=hebrew";
2026   case 2: return "fr_FR@calendar=hebrew";
2027   case 3: return "fi_FI@calendar=hebrew";
2028   case 4: return "nl_NL@calendar=hebrew";
2029   case 5: return "hu_HU@calendar=hebrew";
2030   case 6: return "nl_BE@currency=MTL;calendar=islamic";
2031   case 7: return "th_TH_TRADITIONAL@calendar=gregorian";
2032   case 8: return "ar_JO@calendar=islamic-civil";
2033   case 9: return "fi_FI@calendar=islamic";
2034   case 10: return "fr_CH@calendar=islamic-civil";
2035   case 11: return "he_IL@calendar=islamic-civil";
2036   case 12: return "hu_HU@calendar=buddhist";
2037   case 13: return "hu_HU@calendar=islamic";
2038   case 14: return "en_US@calendar=japanese";
2039   default: return nullptr;
2040   }
2041 }
2042 
testLocaleCount()2043 int32_t CalendarTest::testLocaleCount()
2044 {
2045   static int32_t gLocaleCount = -1;
2046   if(gLocaleCount < 0) {
2047     int32_t i;
2048     for(i=0;testLocaleID(i) != nullptr;i++) {
2049       // do nothing
2050     }
2051     gLocaleCount = i;
2052   }
2053   return gLocaleCount;
2054 }
2055 
doMinDateOfCalendar(Calendar * adopt,UBool & isGregorian,UErrorCode & status)2056 static UDate doMinDateOfCalendar(Calendar* adopt, UBool &isGregorian, UErrorCode& status) {
2057   if(U_FAILURE(status)) return 0.0;
2058 
2059   adopt->clear();
2060   adopt->set(UCAL_EXTENDED_YEAR, adopt->getActualMinimum(UCAL_EXTENDED_YEAR, status));
2061   UDate ret = adopt->getTime(status);
2062   isGregorian = dynamic_cast<GregorianCalendar*>(adopt) != nullptr;
2063   delete adopt;
2064   return ret;
2065 }
2066 
minDateOfCalendar(const Locale & locale,UBool & isGregorian,UErrorCode & status)2067 UDate CalendarTest::minDateOfCalendar(const Locale& locale, UBool &isGregorian, UErrorCode& status) {
2068   if(U_FAILURE(status)) return 0.0;
2069   return doMinDateOfCalendar(Calendar::createInstance(locale, status), isGregorian, status);
2070 }
2071 
minDateOfCalendar(const Calendar & cal,UBool & isGregorian,UErrorCode & status)2072 UDate CalendarTest::minDateOfCalendar(const Calendar& cal, UBool &isGregorian, UErrorCode& status) {
2073   if(U_FAILURE(status)) return 0.0;
2074   return doMinDateOfCalendar(cal.clone(), isGregorian, status);
2075 }
2076 
Test6703()2077 void CalendarTest::Test6703()
2078 {
2079     UErrorCode status = U_ZERO_ERROR;
2080     Calendar *cal;
2081 
2082     Locale loc1("en@calendar=fubar");
2083     cal = Calendar::createInstance(loc1, status);
2084     if (failure(status, "Calendar::createInstance", true)) return;
2085     delete cal;
2086 
2087     status = U_ZERO_ERROR;
2088     Locale loc2("en");
2089     cal = Calendar::createInstance(loc2, status);
2090     if (failure(status, "Calendar::createInstance")) return;
2091     delete cal;
2092 
2093     status = U_ZERO_ERROR;
2094     Locale loc3("en@calendar=roc");
2095     cal = Calendar::createInstance(loc3, status);
2096     if (failure(status, "Calendar::createInstance")) return;
2097     delete cal;
2098 }
2099 
Test3785()2100 void CalendarTest::Test3785()
2101 {
2102     UErrorCode status = U_ZERO_ERROR;
2103     UnicodeString uzone = UNICODE_STRING_SIMPLE("Europe/Paris");
2104     UnicodeString exp1 = UNICODE_STRING_SIMPLE("Mon 30 Jumada II 1433 AH, 01:47:03");
2105     UnicodeString exp2 = UNICODE_STRING_SIMPLE("Mon 1 Rajab 1433 AH, 01:47:04");
2106 
2107     LocalUDateFormatPointer df(udat_open(UDAT_NONE, UDAT_NONE, "en@calendar=islamic", uzone.getTerminatedBuffer(),
2108                                          uzone.length(), nullptr, 0, &status));
2109     if (df.isNull() || U_FAILURE(status)) return;
2110 
2111     char16_t upattern[64];
2112     u_uastrcpy(upattern, "EEE d MMMM y G, HH:mm:ss");
2113     udat_applyPattern(df.getAlias(), false, upattern, u_strlen(upattern));
2114 
2115     char16_t ubuffer[1024];
2116     UDate ud0 = 1337557623000.0;
2117 
2118     status = U_ZERO_ERROR;
2119     udat_format(df.getAlias(), ud0, ubuffer, 1024, nullptr, &status);
2120     if (U_FAILURE(status)) {
2121         errln("Error formatting date 1\n");
2122         return;
2123     }
2124     //printf("formatted: '%s'\n", mkcstr(ubuffer));
2125 
2126     UnicodeString act1(ubuffer);
2127     if ( act1 != exp1 ) {
2128         errln("Unexpected result from date 1 format\n");
2129     }
2130     ud0 += 1000.0; // add one second
2131 
2132     status = U_ZERO_ERROR;
2133     udat_format(df.getAlias(), ud0, ubuffer, 1024, nullptr, &status);
2134     if (U_FAILURE(status)) {
2135         errln("Error formatting date 2\n");
2136         return;
2137     }
2138     //printf("formatted: '%s'\n", mkcstr(ubuffer));
2139     UnicodeString act2(ubuffer);
2140     if ( act2 != exp2 ) {
2141         errln("Unexpected result from date 2 format\n");
2142     }
2143 }
2144 
Test1624()2145 void CalendarTest::Test1624() {
2146     UErrorCode status = U_ZERO_ERROR;
2147     Locale loc("he_IL@calendar=hebrew");
2148     HebrewCalendar hc(loc,status);
2149 
2150     for (int32_t year = 5600; year < 5800; year++ ) {
2151 
2152         for (int32_t month = HebrewCalendar::TISHRI; month <= HebrewCalendar::ELUL; month++) {
2153             // skip the adar 1 month if year is not a leap year
2154             if (HebrewCalendar::isLeapYear(year) == false && month == HebrewCalendar::ADAR_1) {
2155                 continue;
2156             }
2157             int32_t day = 15;
2158             hc.set(year,month,day);
2159             int32_t dayHC = hc.get(UCAL_DATE,status);
2160             int32_t monthHC = hc.get(UCAL_MONTH,status);
2161             int32_t yearHC = hc.get(UCAL_YEAR,status);
2162 
2163             if (failure(status, "HebrewCalendar.get()", true)) continue;
2164 
2165             if (dayHC != day) {
2166                 errln(" ==> day %d incorrect, should be: %d\n",dayHC,day);
2167                 break;
2168             }
2169             if (monthHC != month) {
2170                 errln(" ==> month %d incorrect, should be: %d\n",monthHC,month);
2171                 break;
2172             }
2173             if (yearHC != year) {
2174                 errln(" ==> day %d incorrect, should be: %d\n",yearHC,year);
2175                 break;
2176             }
2177         }
2178     }
2179 }
2180 
TestTimeStamp()2181 void CalendarTest::TestTimeStamp() {
2182     UErrorCode status = U_ZERO_ERROR;
2183     UDate start = 0.0, time;
2184     Calendar *cal;
2185 
2186     // Create a new Gregorian Calendar.
2187     cal = Calendar::createInstance("en_US@calendar=gregorian", status);
2188     if (U_FAILURE(status)) {
2189         dataerrln("Error creating Gregorian calendar.");
2190         return;
2191     }
2192 
2193     for (int i = 0; i < 20000; i++) {
2194         // Set the Gregorian Calendar to a specific date for testing.
2195         cal->set(2009, UCAL_JULY, 3, 0, 49, 46);
2196 
2197         time = cal->getTime(status);
2198         if (U_FAILURE(status)) {
2199             errln("Error calling getTime()");
2200             break;
2201         }
2202 
2203         if (i == 0) {
2204             start = time;
2205         } else {
2206             if (start != time) {
2207                 errln("start and time not equal.");
2208                 break;
2209             }
2210         }
2211     }
2212 
2213     delete cal;
2214 }
2215 
TestISO8601()2216 void CalendarTest::TestISO8601() {
2217     const char* TEST_LOCALES[] = {
2218         "en_US@calendar=iso8601",
2219         "en_US@calendar=Iso8601",
2220         "th_TH@calendar=iso8601",
2221         "ar_EG@calendar=iso8601",
2222         nullptr
2223     };
2224 
2225     int32_t TEST_DATA[][3] = {
2226         {2008, 1, 2008},
2227         {2009, 1, 2009},
2228         {2010, 53, 2009},
2229         {2011, 52, 2010},
2230         {2012, 52, 2011},
2231         {2013, 1, 2013},
2232         {2014, 1, 2014},
2233         {0, 0, 0},
2234     };
2235 
2236     for (int i = 0; TEST_LOCALES[i] != nullptr; i++) {
2237         UErrorCode status = U_ZERO_ERROR;
2238         Calendar *cal = Calendar::createInstance(TEST_LOCALES[i], status);
2239         if (U_FAILURE(status)) {
2240             errln("Error: Failed to create a calendar for locale: %s", TEST_LOCALES[i]);
2241             continue;
2242         }
2243         if (uprv_strcmp(cal->getType(), "iso8601") != 0) {
2244             errln("Error: iso8601 calendar is not used for locale: %s", TEST_LOCALES[i]);
2245             continue;
2246         }
2247         for (int j = 0; TEST_DATA[j][0] != 0; j++) {
2248             cal->set(TEST_DATA[j][0], UCAL_JANUARY, 1);
2249             int32_t weekNum = cal->get(UCAL_WEEK_OF_YEAR, status);
2250             int32_t weekYear = cal->get(UCAL_YEAR_WOY, status);
2251             if (U_FAILURE(status)) {
2252                 errln("Error: Failed to get week of year");
2253                 break;
2254             }
2255             if (weekNum != TEST_DATA[j][1] || weekYear != TEST_DATA[j][2]) {
2256                 errln("Error: Incorrect week of year on January 1st, %d for locale %s: Returned [weekNum=%d, weekYear=%d], Expected [weekNum=%d, weekYear=%d]",
2257                     TEST_DATA[j][0], TEST_LOCALES[i], weekNum, weekYear, TEST_DATA[j][1], TEST_DATA[j][2]);
2258             }
2259         }
2260         delete cal;
2261     }
2262 
2263 }
2264 
2265 void
TestAmbiguousWallTimeAPIs()2266 CalendarTest::TestAmbiguousWallTimeAPIs() {
2267     UErrorCode status = U_ZERO_ERROR;
2268     Calendar* cal = Calendar::createInstance(status);
2269     if (U_FAILURE(status)) {
2270         errln("Fail: Error creating a calendar instance.");
2271         return;
2272     }
2273 
2274     if (cal->getRepeatedWallTimeOption() != UCAL_WALLTIME_LAST) {
2275         errln("Fail: Default repeted time option is not UCAL_WALLTIME_LAST");
2276     }
2277     if (cal->getSkippedWallTimeOption() != UCAL_WALLTIME_LAST) {
2278         errln("Fail: Default skipped time option is not UCAL_WALLTIME_LAST");
2279     }
2280 
2281     Calendar* cal2 = cal->clone();
2282 
2283     if (*cal != *cal2) {
2284         errln("Fail: Cloned calendar != the original");
2285     }
2286     if (!cal->equals(*cal2, status)) {
2287         errln("Fail: The time of cloned calendar is not equal to the original");
2288     } else if (U_FAILURE(status)) {
2289         errln("Fail: Error equals");
2290     }
2291     status = U_ZERO_ERROR;
2292 
2293     cal2->setRepeatedWallTimeOption(UCAL_WALLTIME_FIRST);
2294     cal2->setSkippedWallTimeOption(UCAL_WALLTIME_FIRST);
2295 
2296     if (*cal == *cal2) {
2297         errln("Fail: Cloned and modified calendar == the original");
2298     }
2299     if (!cal->equals(*cal2, status)) {
2300         errln("Fail: The time of cloned calendar is not equal to the original after changing wall time options");
2301     } else if (U_FAILURE(status)) {
2302         errln("Fail: Error equals after changing wall time options");
2303     }
2304     status = U_ZERO_ERROR;
2305 
2306     if (cal2->getRepeatedWallTimeOption() != UCAL_WALLTIME_FIRST) {
2307         errln("Fail: Repeted time option is not UCAL_WALLTIME_FIRST");
2308     }
2309     if (cal2->getSkippedWallTimeOption() != UCAL_WALLTIME_FIRST) {
2310         errln("Fail: Skipped time option is not UCAL_WALLTIME_FIRST");
2311     }
2312 
2313     cal2->setRepeatedWallTimeOption(UCAL_WALLTIME_NEXT_VALID);
2314     if (cal2->getRepeatedWallTimeOption() != UCAL_WALLTIME_FIRST) {
2315         errln("Fail: Repeated wall time option was updated other than UCAL_WALLTIME_FIRST");
2316     }
2317 
2318     delete cal;
2319     delete cal2;
2320 }
2321 
2322 class CalFields {
2323 public:
2324     CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec, int32_t ms = 0);
2325     CalFields(const Calendar& cal, UErrorCode& status);
2326     void setTo(Calendar& cal) const;
2327     char* toString(char* buf, int32_t len) const;
2328     bool operator==(const CalFields& rhs) const;
2329     bool operator!=(const CalFields& rhs) const;
2330     UBool isEquivalentTo(const Calendar& cal, UErrorCode& status) const;
2331 
2332 private:
2333     int32_t year;
2334     int32_t month;
2335     int32_t day;
2336     int32_t hour;
2337     int32_t min;
2338     int32_t sec;
2339     int32_t ms;
2340 };
2341 
CalFields(int32_t year,int32_t month,int32_t day,int32_t hour,int32_t min,int32_t sec,int32_t ms)2342 CalFields::CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec, int32_t ms)
2343     : year(year), month(month), day(day), hour(hour), min(min), sec(sec), ms(ms) {
2344 }
2345 
CalFields(const Calendar & cal,UErrorCode & status)2346 CalFields::CalFields(const Calendar& cal, UErrorCode& status) {
2347     year = cal.get(UCAL_YEAR, status);
2348     month = cal.get(UCAL_MONTH, status) + 1;
2349     day = cal.get(UCAL_DAY_OF_MONTH, status);
2350     hour = cal.get(UCAL_HOUR_OF_DAY, status);
2351     min = cal.get(UCAL_MINUTE, status);
2352     sec = cal.get(UCAL_SECOND, status);
2353     ms = cal.get(UCAL_MILLISECOND, status);
2354 }
2355 
2356 void
setTo(Calendar & cal) const2357 CalFields::setTo(Calendar& cal) const {
2358     cal.clear();
2359     cal.set(year, month - 1, day, hour, min, sec);
2360     cal.set(UCAL_MILLISECOND, ms);
2361 }
2362 
2363 char*
toString(char * buf,int32_t len) const2364 CalFields::toString(char* buf, int32_t len) const {
2365     char local[32];
2366     snprintf(local, sizeof(local), "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, min, sec, ms);
2367     uprv_strncpy(buf, local, len - 1);
2368     buf[len - 1] = 0;
2369     return buf;
2370 }
2371 
2372 bool
operator ==(const CalFields & rhs) const2373 CalFields::operator==(const CalFields& rhs) const {
2374     return year == rhs.year
2375         && month == rhs.month
2376         && day == rhs.day
2377         && hour == rhs.hour
2378         && min == rhs.min
2379         && sec == rhs.sec
2380         && ms == rhs.ms;
2381 }
2382 
2383 bool
operator !=(const CalFields & rhs) const2384 CalFields::operator!=(const CalFields& rhs) const {
2385     return !(*this == rhs);
2386 }
2387 
2388 UBool
isEquivalentTo(const Calendar & cal,UErrorCode & status) const2389 CalFields::isEquivalentTo(const Calendar& cal, UErrorCode& status) const {
2390     return year == cal.get(UCAL_YEAR, status)
2391         && month == cal.get(UCAL_MONTH, status) + 1
2392         && day == cal.get(UCAL_DAY_OF_MONTH, status)
2393         && hour == cal.get(UCAL_HOUR_OF_DAY, status)
2394         && min == cal.get(UCAL_MINUTE, status)
2395         && sec == cal.get(UCAL_SECOND, status)
2396         && ms == cal.get(UCAL_MILLISECOND, status);
2397 }
2398 
2399 typedef struct {
2400     const char*     tzid;
2401     const CalFields in;
2402     const CalFields expLastGMT;
2403     const CalFields expFirstGMT;
2404 } RepeatedWallTimeTestData;
2405 
2406 static const RepeatedWallTimeTestData RPDATA[] =
2407 {
2408     // Time zone            Input wall time                 WALLTIME_LAST in GMT            WALLTIME_FIRST in GMT
2409     {"America/New_York",    CalFields(2011,11,6,0,59,59),   CalFields(2011,11,6,4,59,59),   CalFields(2011,11,6,4,59,59)},
2410     {"America/New_York",    CalFields(2011,11,6,1,0,0),     CalFields(2011,11,6,6,0,0),     CalFields(2011,11,6,5,0,0)},
2411     {"America/New_York",    CalFields(2011,11,6,1,0,1),     CalFields(2011,11,6,6,0,1),     CalFields(2011,11,6,5,0,1)},
2412     {"America/New_York",    CalFields(2011,11,6,1,30,0),    CalFields(2011,11,6,6,30,0),    CalFields(2011,11,6,5,30,0)},
2413     {"America/New_York",    CalFields(2011,11,6,1,59,59),   CalFields(2011,11,6,6,59,59),   CalFields(2011,11,6,5,59,59)},
2414     {"America/New_York",    CalFields(2011,11,6,2,0,0),     CalFields(2011,11,6,7,0,0),     CalFields(2011,11,6,7,0,0)},
2415     {"America/New_York",    CalFields(2011,11,6,2,0,1),     CalFields(2011,11,6,7,0,1),     CalFields(2011,11,6,7,0,1)},
2416 
2417     {"Australia/Lord_Howe", CalFields(2011,4,3,1,29,59),    CalFields(2011,4,2,14,29,59),   CalFields(2011,4,2,14,29,59)},
2418     {"Australia/Lord_Howe", CalFields(2011,4,3,1,30,0),     CalFields(2011,4,2,15,0,0),     CalFields(2011,4,2,14,30,0)},
2419     {"Australia/Lord_Howe", CalFields(2011,4,3,1,45,0),     CalFields(2011,4,2,15,15,0),    CalFields(2011,4,2,14,45,0)},
2420     {"Australia/Lord_Howe", CalFields(2011,4,3,1,59,59),    CalFields(2011,4,2,15,29,59),   CalFields(2011,4,2,14,59,59)},
2421     {"Australia/Lord_Howe", CalFields(2011,4,3,2,0,0),      CalFields(2011,4,2,15,30,0),    CalFields(2011,4,2,15,30,0)},
2422     {"Australia/Lord_Howe", CalFields(2011,4,3,2,0,1),      CalFields(2011,4,2,15,30,1),    CalFields(2011,4,2,15,30,1)},
2423 
2424     {nullptr,                  CalFields(0,0,0,0,0,0),         CalFields(0,0,0,0,0,0),          CalFields(0,0,0,0,0,0)}
2425 };
2426 
TestRepeatedWallTime()2427 void CalendarTest::TestRepeatedWallTime() {
2428     UErrorCode status = U_ZERO_ERROR;
2429     GregorianCalendar calGMT((const TimeZone&)*TimeZone::getGMT(), status);
2430     GregorianCalendar calDefault(status);
2431     GregorianCalendar calLast(status);
2432     GregorianCalendar calFirst(status);
2433 
2434     if (U_FAILURE(status)) {
2435         errln("Fail: Failed to create a calendar object.");
2436         return;
2437     }
2438 
2439     calLast.setRepeatedWallTimeOption(UCAL_WALLTIME_LAST);
2440     calFirst.setRepeatedWallTimeOption(UCAL_WALLTIME_FIRST);
2441 
2442     for (int32_t i = 0; RPDATA[i].tzid != nullptr; i++) {
2443         char buf[32];
2444         TimeZone *tz = TimeZone::createTimeZone(RPDATA[i].tzid);
2445 
2446         // UCAL_WALLTIME_LAST
2447         status = U_ZERO_ERROR;
2448         calLast.setTimeZone(*tz);
2449         RPDATA[i].in.setTo(calLast);
2450         calGMT.setTime(calLast.getTime(status), status);
2451         CalFields outLastGMT(calGMT, status);
2452         if (U_FAILURE(status)) {
2453             errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (UCAL_WALLTIME_LAST) - ")
2454                 + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "]");
2455         } else {
2456             if (outLastGMT != RPDATA[i].expLastGMT) {
2457                 dataerrln(UnicodeString("Fail: UCAL_WALLTIME_LAST ") + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "] is parsed as "
2458                     + outLastGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + RPDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
2459             }
2460         }
2461 
2462         // default
2463         status = U_ZERO_ERROR;
2464         calDefault.setTimeZone(*tz);
2465         RPDATA[i].in.setTo(calDefault);
2466         calGMT.setTime(calDefault.getTime(status), status);
2467         CalFields outDefGMT(calGMT, status);
2468         if (U_FAILURE(status)) {
2469             errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (default) - ")
2470                 + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "]");
2471         } else {
2472             if (outDefGMT != RPDATA[i].expLastGMT) {
2473                 dataerrln(UnicodeString("Fail: (default) ") + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "] is parsed as "
2474                     + outDefGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + RPDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
2475             }
2476         }
2477 
2478         // UCAL_WALLTIME_FIRST
2479         status = U_ZERO_ERROR;
2480         calFirst.setTimeZone(*tz);
2481         RPDATA[i].in.setTo(calFirst);
2482         calGMT.setTime(calFirst.getTime(status), status);
2483         CalFields outFirstGMT(calGMT, status);
2484         if (U_FAILURE(status)) {
2485             errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (UCAL_WALLTIME_FIRST) - ")
2486                 + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "]");
2487         } else {
2488             if (outFirstGMT != RPDATA[i].expFirstGMT) {
2489                 dataerrln(UnicodeString("Fail: UCAL_WALLTIME_FIRST ") + RPDATA[i].in.toString(buf, sizeof(buf)) + "[" + RPDATA[i].tzid + "] is parsed as "
2490                     + outFirstGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + RPDATA[i].expFirstGMT.toString(buf, sizeof(buf)) + "[GMT]");
2491             }
2492         }
2493         delete tz;
2494     }
2495 }
2496 
2497 typedef struct {
2498     const char*     tzid;
2499     const CalFields in;
2500     UBool           isValid;
2501     const CalFields expLastGMT;
2502     const CalFields expFirstGMT;
2503     const CalFields expNextAvailGMT;
2504 } SkippedWallTimeTestData;
2505 
2506 static SkippedWallTimeTestData SKDATA[] =
2507 {
2508      // Time zone           Input wall time                 valid?  WALLTIME_LAST in GMT            WALLTIME_FIRST in GMT           WALLTIME_NEXT_VALID in GMT
2509     {"America/New_York",    CalFields(2011,3,13,1,59,59),   true,   CalFields(2011,3,13,6,59,59),   CalFields(2011,3,13,6,59,59),   CalFields(2011,3,13,6,59,59)},
2510     {"America/New_York",    CalFields(2011,3,13,2,0,0),     false,  CalFields(2011,3,13,7,0,0),     CalFields(2011,3,13,6,0,0),     CalFields(2011,3,13,7,0,0)},
2511     {"America/New_York",    CalFields(2011,3,13,2,1,0),     false,  CalFields(2011,3,13,7,1,0),     CalFields(2011,3,13,6,1,0),     CalFields(2011,3,13,7,0,0)},
2512     {"America/New_York",    CalFields(2011,3,13,2,30,0),    false,  CalFields(2011,3,13,7,30,0),    CalFields(2011,3,13,6,30,0),    CalFields(2011,3,13,7,0,0)},
2513     {"America/New_York",    CalFields(2011,3,13,2,59,59),   false,  CalFields(2011,3,13,7,59,59),   CalFields(2011,3,13,6,59,59),   CalFields(2011,3,13,7,0,0)},
2514     {"America/New_York",    CalFields(2011,3,13,3,0,0),     true,   CalFields(2011,3,13,7,0,0),     CalFields(2011,3,13,7,0,0),     CalFields(2011,3,13,7,0,0)},
2515 
2516     {"Pacific/Apia",        CalFields(2011,12,29,23,59,59), true,   CalFields(2011,12,30,9,59,59),  CalFields(2011,12,30,9,59,59),  CalFields(2011,12,30,9,59,59)},
2517     {"Pacific/Apia",        CalFields(2011,12,30,0,0,0),    false,  CalFields(2011,12,30,10,0,0),   CalFields(2011,12,29,10,0,0),   CalFields(2011,12,30,10,0,0)},
2518     {"Pacific/Apia",        CalFields(2011,12,30,12,0,0),   false,  CalFields(2011,12,30,22,0,0),   CalFields(2011,12,29,22,0,0),   CalFields(2011,12,30,10,0,0)},
2519     {"Pacific/Apia",        CalFields(2011,12,30,23,59,59), false,  CalFields(2011,12,31,9,59,59),  CalFields(2011,12,30,9,59,59),  CalFields(2011,12,30,10,0,0)},
2520     {"Pacific/Apia",        CalFields(2011,12,31,0,0,0),    true,   CalFields(2011,12,30,10,0,0),   CalFields(2011,12,30,10,0,0),   CalFields(2011,12,30,10,0,0)},
2521 
2522     {nullptr,                  CalFields(0,0,0,0,0,0),         true,   CalFields(0,0,0,0,0,0),         CalFields(0,0,0,0,0,0),         CalFields(0,0,0,0,0,0)}
2523 };
2524 
2525 
TestSkippedWallTime()2526 void CalendarTest::TestSkippedWallTime() {
2527     UErrorCode status = U_ZERO_ERROR;
2528     GregorianCalendar calGMT((const TimeZone&)*TimeZone::getGMT(), status);
2529     GregorianCalendar calDefault(status);
2530     GregorianCalendar calLast(status);
2531     GregorianCalendar calFirst(status);
2532     GregorianCalendar calNextAvail(status);
2533 
2534     if (U_FAILURE(status)) {
2535         errln("Fail: Failed to create a calendar object.");
2536         return;
2537     }
2538 
2539     calLast.setSkippedWallTimeOption(UCAL_WALLTIME_LAST);
2540     calFirst.setSkippedWallTimeOption(UCAL_WALLTIME_FIRST);
2541     calNextAvail.setSkippedWallTimeOption(UCAL_WALLTIME_NEXT_VALID);
2542 
2543     for (int32_t i = 0; SKDATA[i].tzid != nullptr; i++) {
2544         UDate d;
2545         char buf[32];
2546         TimeZone *tz = TimeZone::createTimeZone(SKDATA[i].tzid);
2547 
2548         for (int32_t j = 0; j < 2; j++) {
2549             UBool bLenient = (j == 0);
2550 
2551             // UCAL_WALLTIME_LAST
2552             status = U_ZERO_ERROR;
2553             calLast.setLenient(bLenient);
2554             calLast.setTimeZone(*tz);
2555             SKDATA[i].in.setTo(calLast);
2556             d = calLast.getTime(status);
2557             if (bLenient || SKDATA[i].isValid) {
2558                 calGMT.setTime(d, status);
2559                 CalFields outLastGMT(calGMT, status);
2560                 if (U_FAILURE(status)) {
2561                     errln(UnicodeString("Fail: Failed to get/set time calLast/calGMT (UCAL_WALLTIME_LAST) - ")
2562                         + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2563                 } else {
2564                     if (outLastGMT != SKDATA[i].expLastGMT) {
2565                         dataerrln(UnicodeString("Fail: UCAL_WALLTIME_LAST ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
2566                             + outLastGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
2567                     }
2568                 }
2569             } else if (U_SUCCESS(status)) {
2570                 // strict, invalid wall time - must report an error
2571                 dataerrln(UnicodeString("Fail: An error expected (UCAL_WALLTIME_LAST)") +
2572                     + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2573             }
2574 
2575             // default
2576             status = U_ZERO_ERROR;
2577             calDefault.setLenient(bLenient);
2578             calDefault.setTimeZone(*tz);
2579             SKDATA[i].in.setTo(calDefault);
2580             d = calDefault.getTime(status);
2581             if (bLenient || SKDATA[i].isValid) {
2582                 calGMT.setTime(d, status);
2583                 CalFields outDefGMT(calGMT, status);
2584                 if (U_FAILURE(status)) {
2585                     errln(UnicodeString("Fail: Failed to get/set time calDefault/calGMT (default) - ")
2586                         + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2587                 } else {
2588                     if (outDefGMT != SKDATA[i].expLastGMT) {
2589                         dataerrln(UnicodeString("Fail: (default) ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
2590                             + outDefGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expLastGMT.toString(buf, sizeof(buf)) + "[GMT]");
2591                     }
2592                 }
2593             } else if (U_SUCCESS(status)) {
2594                 // strict, invalid wall time - must report an error
2595                 dataerrln(UnicodeString("Fail: An error expected (default)") +
2596                     + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2597             }
2598 
2599             // UCAL_WALLTIME_FIRST
2600             status = U_ZERO_ERROR;
2601             calFirst.setLenient(bLenient);
2602             calFirst.setTimeZone(*tz);
2603             SKDATA[i].in.setTo(calFirst);
2604             d = calFirst.getTime(status);
2605             if (bLenient || SKDATA[i].isValid) {
2606                 calGMT.setTime(d, status);
2607                 CalFields outFirstGMT(calGMT, status);
2608                 if (U_FAILURE(status)) {
2609                     errln(UnicodeString("Fail: Failed to get/set time calFirst/calGMT (UCAL_WALLTIME_FIRST) - ")
2610                         + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2611                 } else {
2612                     if (outFirstGMT != SKDATA[i].expFirstGMT) {
2613                         dataerrln(UnicodeString("Fail: UCAL_WALLTIME_FIRST ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
2614                             + outFirstGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expFirstGMT.toString(buf, sizeof(buf)) + "[GMT]");
2615                     }
2616                 }
2617             } else if (U_SUCCESS(status)) {
2618                 // strict, invalid wall time - must report an error
2619                 dataerrln(UnicodeString("Fail: An error expected (UCAL_WALLTIME_FIRST)") +
2620                     + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2621             }
2622 
2623             // UCAL_WALLTIME_NEXT_VALID
2624             status = U_ZERO_ERROR;
2625             calNextAvail.setLenient(bLenient);
2626             calNextAvail.setTimeZone(*tz);
2627             SKDATA[i].in.setTo(calNextAvail);
2628             d = calNextAvail.getTime(status);
2629             if (bLenient || SKDATA[i].isValid) {
2630                 calGMT.setTime(d, status);
2631                 CalFields outNextAvailGMT(calGMT, status);
2632                 if (U_FAILURE(status)) {
2633                     errln(UnicodeString("Fail: Failed to get/set time calNextAvail/calGMT (UCAL_WALLTIME_NEXT_VALID) - ")
2634                         + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2635                 } else {
2636                     if (outNextAvailGMT != SKDATA[i].expNextAvailGMT) {
2637                         dataerrln(UnicodeString("Fail: UCAL_WALLTIME_NEXT_VALID ") + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "] is parsed as "
2638                             + outNextAvailGMT.toString(buf, sizeof(buf)) + "[GMT]. Expected: " + SKDATA[i].expNextAvailGMT.toString(buf, sizeof(buf)) + "[GMT]");
2639                     }
2640                 }
2641             } else if (U_SUCCESS(status)) {
2642                 // strict, invalid wall time - must report an error
2643                 dataerrln(UnicodeString("Fail: An error expected (UCAL_WALLTIME_NEXT_VALID)") +
2644                     + SKDATA[i].in.toString(buf, sizeof(buf)) + "[" + SKDATA[i].tzid + "]");
2645             }
2646         }
2647 
2648         delete tz;
2649     }
2650 }
2651 
TestCloneLocale()2652 void CalendarTest::TestCloneLocale() {
2653   UErrorCode status = U_ZERO_ERROR;
2654   LocalPointer<Calendar>  cal(Calendar::createInstance(TimeZone::getGMT()->clone(),
2655                                                        Locale::createFromName("en"), status));
2656   TEST_CHECK_STATUS;
2657   Locale l0 = cal->getLocale(ULOC_VALID_LOCALE, status);
2658   TEST_CHECK_STATUS;
2659   LocalPointer<Calendar> cal2(cal->clone());
2660   Locale l = cal2->getLocale(ULOC_VALID_LOCALE, status);
2661   if(l0!=l) {
2662     errln("Error: cloned locale %s != original locale %s, status %s\n", l0.getName(), l.getName(), u_errorName(status));
2663   }
2664   TEST_CHECK_STATUS;
2665 }
2666 
TestTimeZoneInLocale()2667 void CalendarTest::TestTimeZoneInLocale() {
2668     const char *tests[][3]  = {
2669         { "en-u-tz-usden",                     "America/Denver",             "gregorian" },
2670         { "es-u-tz-usden",                     "America/Denver",             "gregorian" },
2671         { "ms-u-tz-mykul",                     "Asia/Kuala_Lumpur",          "gregorian" },
2672         { "zh-u-tz-mykul",                     "Asia/Kuala_Lumpur",          "gregorian" },
2673         { "fr-u-ca-buddhist-tz-phmnl",         "Asia/Manila",                "buddhist" },
2674         { "th-u-ca-chinese-tz-gblon",          "Europe/London",              "chinese" },
2675         { "de-u-ca-coptic-tz-ciabj",           "Africa/Abidjan",             "coptic" },
2676         { "ja-u-ca-dangi-tz-hkhkg",            "Asia/Hong_Kong",             "dangi" },
2677         { "da-u-ca-ethioaa-tz-ruunera",        "Asia/Ust-Nera",              "ethiopic-amete-alem" },
2678         { "ko-u-ca-ethiopic-tz-cvrai",         "Atlantic/Cape_Verde",        "ethiopic" },
2679         { "fil-u-ca-gregory-tz-aubne",         "Australia/Brisbane",         "gregorian" },
2680         { "fa-u-ca-hebrew-tz-brrbr",           "America/Rio_Branco",         "hebrew" },
2681         { "gr-u-ca-indian-tz-lccas",           "America/St_Lucia",           "indian" },
2682         { "or-u-ca-islamic-tz-cayyn",          "America/Swift_Current",      "islamic" },
2683         { "my-u-ca-islamic-umalqura-tz-kzala", "Asia/Almaty",                "islamic-umalqura" },
2684         { "lo-u-ca-islamic-tbla-tz-bmbda",     "Atlantic/Bermuda",           "islamic-tbla" },
2685         { "km-u-ca-islamic-civil-tz-aqplm",    "Antarctica/Palmer",          "islamic-civil" },
2686         { "kk-u-ca-islamic-rgsa-tz-usanc",     "America/Anchorage",          "islamic-rgsa" },
2687         { "ar-u-ca-iso8601-tz-bjptn",          "Africa/Porto-Novo",          "iso8601" },
2688         { "he-u-ca-japanese-tz-tzdar",         "Africa/Dar_es_Salaam",       "japanese" },
2689         { "bs-u-ca-persian-tz-etadd",          "Africa/Addis_Ababa",         "persian" },
2690         { "it-u-ca-roc-tz-aruaq",              "America/Argentina/San_Juan", "roc" },
2691     };
2692 
2693     for (int32_t i = 0; i < UPRV_LENGTHOF(tests); ++i) {
2694         UErrorCode status = U_ZERO_ERROR;
2695         const char **testLine = tests[i];
2696         Locale locale(testLine[0]);
2697         UnicodeString expected(testLine[1], -1, US_INV);
2698         UnicodeString actual;
2699 
2700         LocalPointer<Calendar> calendar(
2701                 Calendar::createInstance(locale, status));
2702         if (failure(status, "Calendar::createInstance", true)) continue;
2703 
2704         assertEquals("TimeZone from Calendar::createInstance",
2705                      expected, calendar->getTimeZone().getID(actual));
2706 
2707         assertEquals("Calendar Type from Calendar::createInstance",
2708                      testLine[2], calendar->getType());
2709     }
2710 }
2711 
AsssertCalendarFieldValue(Calendar * cal,double time,const char * type,int32_t era,int32_t year,int32_t month,int32_t week_of_year,int32_t week_of_month,int32_t date,int32_t day_of_year,int32_t day_of_week,int32_t day_of_week_in_month,int32_t am_pm,int32_t hour,int32_t hour_of_day,int32_t minute,int32_t second,int32_t millisecond,int32_t zone_offset,int32_t dst_offset,int32_t year_woy,int32_t dow_local,int32_t extended_year,int32_t julian_day,int32_t milliseconds_in_day,int32_t is_leap_month)2712 void CalendarTest::AsssertCalendarFieldValue(
2713     Calendar* cal, double time, const char* type,
2714     int32_t era, int32_t year, int32_t month, int32_t week_of_year,
2715     int32_t week_of_month, int32_t date, int32_t day_of_year, int32_t day_of_week,
2716     int32_t day_of_week_in_month, int32_t am_pm, int32_t hour, int32_t hour_of_day,
2717     int32_t minute, int32_t second, int32_t millisecond, int32_t zone_offset,
2718     int32_t dst_offset, int32_t year_woy, int32_t dow_local, int32_t extended_year,
2719     int32_t julian_day, int32_t milliseconds_in_day, int32_t is_leap_month) {
2720 
2721     UErrorCode status = U_ZERO_ERROR;
2722     cal->setTime(time, status);
2723     assertEquals("getType", type, cal->getType());
2724 
2725     assertEquals("UCAL_ERA", era, cal->get(UCAL_ERA, status));
2726     assertEquals("UCAL_YEAR", year, cal->get(UCAL_YEAR, status));
2727     assertEquals("UCAL_MONTH", month, cal->get(UCAL_MONTH, status));
2728     assertEquals("UCAL_WEEK_OF_YEAR", week_of_year, cal->get(UCAL_WEEK_OF_YEAR, status));
2729     assertEquals("UCAL_WEEK_OF_MONTH", week_of_month, cal->get(UCAL_WEEK_OF_MONTH, status));
2730     assertEquals("UCAL_DATE", date, cal->get(UCAL_DATE, status));
2731     assertEquals("UCAL_DAY_OF_YEAR", day_of_year, cal->get(UCAL_DAY_OF_YEAR, status));
2732     assertEquals("UCAL_DAY_OF_WEEK", day_of_week, cal->get(UCAL_DAY_OF_WEEK, status));
2733     assertEquals("UCAL_DAY_OF_WEEK_IN_MONTH", day_of_week_in_month, cal->get(UCAL_DAY_OF_WEEK_IN_MONTH, status));
2734     assertEquals("UCAL_AM_PM", am_pm, cal->get(UCAL_AM_PM, status));
2735     assertEquals("UCAL_HOUR", hour, cal->get(UCAL_HOUR, status));
2736     assertEquals("UCAL_HOUR_OF_DAY", hour_of_day, cal->get(UCAL_HOUR_OF_DAY, status));
2737     assertEquals("UCAL_MINUTE", minute, cal->get(UCAL_MINUTE, status));
2738     assertEquals("UCAL_SECOND", second, cal->get(UCAL_SECOND, status));
2739     assertEquals("UCAL_MILLISECOND", millisecond, cal->get(UCAL_MILLISECOND, status));
2740     assertEquals("UCAL_ZONE_OFFSET", zone_offset, cal->get(UCAL_ZONE_OFFSET, status));
2741     assertEquals("UCAL_DST_OFFSET", dst_offset, cal->get(UCAL_DST_OFFSET, status));
2742     assertEquals("UCAL_YEAR_WOY", year_woy, cal->get(UCAL_YEAR_WOY, status));
2743     assertEquals("UCAL_DOW_LOCAL", dow_local, cal->get(UCAL_DOW_LOCAL, status));
2744     assertEquals("UCAL_EXTENDED_YEAR", extended_year, cal->get(UCAL_EXTENDED_YEAR, status));
2745     assertEquals("UCAL_JULIAN_DAY", julian_day, cal->get(UCAL_JULIAN_DAY, status));
2746     assertEquals("UCAL_MILLISECONDS_IN_DAY", milliseconds_in_day, cal->get(UCAL_MILLISECONDS_IN_DAY, status));
2747     assertEquals("UCAL_IS_LEAP_MONTH", is_leap_month, cal->get(UCAL_IS_LEAP_MONTH, status));
2748 }
2749 
2750 static constexpr double test_time = 1667277891323; // Nov 1, 2022 4:44:51 GMT
2751 
TestBasicConversionGregorian()2752 void CalendarTest::TestBasicConversionGregorian() {
2753     UErrorCode status = U_ZERO_ERROR;
2754     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2755         *TimeZone::getGMT(), Locale("en@calendar=gregorian"), status));
2756     if (U_FAILURE(status)) {
2757         errln("Fail: Cannot get Gregorian calendar");
2758         return;
2759     }
2760     AsssertCalendarFieldValue(
2761         cal.getAlias(), test_time, "gregorian",
2762         1, 2022, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51,
2763         323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0);
2764 }
TestBasicConversionISO8601()2765 void CalendarTest::TestBasicConversionISO8601() {
2766     UErrorCode status = U_ZERO_ERROR;
2767     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2768         *TimeZone::getGMT(), Locale("en@calendar=iso8601"), status));
2769     if (U_FAILURE(status)) {
2770         errln("Fail: Cannot get ISO8601 calendar");
2771         return;
2772     }
2773     AsssertCalendarFieldValue(
2774         cal.getAlias(), test_time, "iso8601",
2775         1, 2022, 10, 44, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51,
2776         323, 0, 0, 2022, 2, 2022, 2459885, 17091323, 0);
2777 }
TestBasicConversionJapanese()2778 void CalendarTest::TestBasicConversionJapanese() {
2779     UErrorCode status = U_ZERO_ERROR;
2780     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2781         *TimeZone::getGMT(), Locale("en@calendar=japanese"), status));
2782     if (U_FAILURE(status)) {
2783         errln("Fail: Cannot get Japanese calendar");
2784         return;
2785     }
2786     AsssertCalendarFieldValue(
2787         cal.getAlias(), test_time, "japanese",
2788         236, 4, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51,
2789         323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0);
2790 }
TestBasicConversionBuddhist()2791 void CalendarTest::TestBasicConversionBuddhist() {
2792     UErrorCode status = U_ZERO_ERROR;
2793     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2794         *TimeZone::getGMT(), Locale("en@calendar=buddhist"), status));
2795     if (U_FAILURE(status)) {
2796         errln("Fail: Cannot get Buddhist calendar");
2797         return;
2798     }
2799     AsssertCalendarFieldValue(
2800         cal.getAlias(), test_time, "buddhist",
2801         0, 2565, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51,
2802         323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0);
2803 }
TestBasicConversionTaiwan()2804 void CalendarTest::TestBasicConversionTaiwan() {
2805     UErrorCode status = U_ZERO_ERROR;
2806     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2807         *TimeZone::getGMT(), Locale("en@calendar=roc"), status));
2808     if (U_FAILURE(status)) {
2809         errln("Fail: Cannot get Taiwan calendar");
2810         return;
2811     }
2812     AsssertCalendarFieldValue(
2813         cal.getAlias(), test_time, "roc",
2814         1, 111, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51,
2815         323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0);
2816 
2817 }
TestBasicConversionPersian()2818 void CalendarTest::TestBasicConversionPersian() {
2819     UErrorCode status = U_ZERO_ERROR;
2820     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2821         *TimeZone::getGMT(), Locale("en@calendar=persian"), status));
2822     if (U_FAILURE(status)) {
2823         errln("Fail: Cannot get Persian calendar");
2824         return;
2825     }
2826     AsssertCalendarFieldValue(
2827         cal.getAlias(), test_time, "persian",
2828         0, 1401, 7, 33, 2, 10, 226, 3, 2, 0, 4, 4, 44, 51,
2829         323, 0, 0, 1401, 3, 1401, 2459885, 17091323, 0);
2830 }
TestBasicConversionIslamic()2831 void CalendarTest::TestBasicConversionIslamic() {
2832     UErrorCode status = U_ZERO_ERROR;
2833     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2834         *TimeZone::getGMT(), Locale("en@calendar=islamic"), status));
2835     if (U_FAILURE(status)) {
2836         errln("Fail: Cannot get Islamic calendar");
2837         return;
2838     }
2839     AsssertCalendarFieldValue(
2840         cal.getAlias(), test_time, "islamic",
2841         0, 1444, 3, 15, 2, 7, 96, 3, 1, 0, 4, 4, 44, 51,
2842         323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0);
2843 }
TestBasicConversionIslamicTBLA()2844 void CalendarTest::TestBasicConversionIslamicTBLA() {
2845     UErrorCode status = U_ZERO_ERROR;
2846     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2847         *TimeZone::getGMT(), Locale("en@calendar=islamic-tbla"), status));
2848     if (U_FAILURE(status)) {
2849         errln("Fail: Cannot get IslamicTBLA calendar");
2850         return;
2851     }
2852     AsssertCalendarFieldValue(
2853         cal.getAlias(), test_time, "islamic-tbla",
2854         0, 1444, 3, 15, 2, 7, 96, 3, 1, 0, 4, 4, 44, 51,
2855         323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0);
2856 
2857 }
TestBasicConversionIslamicCivil()2858 void CalendarTest::TestBasicConversionIslamicCivil() {
2859     UErrorCode status = U_ZERO_ERROR;
2860     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2861         *TimeZone::getGMT(), Locale("en@calendar=islamic-civil"), status));
2862     if (U_FAILURE(status)) {
2863         errln("Fail: Cannot get IslamicCivil calendar");
2864         return;
2865     }
2866     AsssertCalendarFieldValue(
2867         cal.getAlias(), test_time, "islamic-civil",
2868         0, 1444, 3, 15, 2, 6, 95, 3, 1, 0, 4, 4, 44, 51,
2869         323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0);
2870 
2871 }
TestBasicConversionIslamicRGSA()2872 void CalendarTest::TestBasicConversionIslamicRGSA() {
2873     UErrorCode status = U_ZERO_ERROR;
2874     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2875         *TimeZone::getGMT(), Locale("en@calendar=islamic-rgsa"), status));
2876     if (U_FAILURE(status)) {
2877         errln("Fail: Cannot get IslamicRGSA calendar");
2878         return;
2879     }
2880     AsssertCalendarFieldValue(
2881         cal.getAlias(), test_time, "islamic-rgsa",
2882         0, 1444, 3, 15, 2, 7, 96, 3, 1, 0, 4, 4, 44, 51,
2883         323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0);
2884 
2885 }
TestBasicConversionIslamicUmalqura()2886 void CalendarTest::TestBasicConversionIslamicUmalqura() {
2887     UErrorCode status = U_ZERO_ERROR;
2888     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2889         *TimeZone::getGMT(), Locale("en@calendar=islamic-umalqura"), status));
2890     if (U_FAILURE(status)) {
2891         errln("Fail: Cannot get IslamicUmalqura calendar");
2892         return;
2893     }
2894     AsssertCalendarFieldValue(
2895         cal.getAlias(), test_time, "islamic-umalqura",
2896         0, 1444, 3, 15, 2, 7, 95, 3, 1, 0, 4, 4, 44, 51,
2897         323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0);
2898 }
TestBasicConversionHebrew()2899 void CalendarTest::TestBasicConversionHebrew() {
2900     UErrorCode status = U_ZERO_ERROR;
2901     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2902         *TimeZone::getGMT(), Locale("en@calendar=hebrew"), status));
2903     if (U_FAILURE(status)) {
2904         errln("Fail: Cannot get Hebrew calendar");
2905         return;
2906     }
2907     AsssertCalendarFieldValue(
2908         cal.getAlias(), test_time, "hebrew",
2909         0, 5783, 1, 6, 2, 7, 37, 3, 1, 0, 4, 4, 44, 51,
2910         323, 0, 0, 5783, 3, 5783, 2459885, 17091323, 0);
2911 }
TestBasicConversionChinese()2912 void CalendarTest::TestBasicConversionChinese() {
2913     UErrorCode status = U_ZERO_ERROR;
2914     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2915         *TimeZone::getGMT(), Locale("en@calendar=chinese"), status));
2916     if (U_FAILURE(status)) {
2917         errln("Fail: Cannot get Chinese calendar");
2918         return;
2919     }
2920     AsssertCalendarFieldValue(
2921         cal.getAlias(), test_time, "chinese",
2922         78, 39, 9, 40, 2, 8, 274, 3, 2, 0, 4, 4, 44, 51,
2923         323, 0, 0, 4659, 3, 4659, 2459885, 17091323, 0);
2924 }
TestBasicConversionDangi()2925 void CalendarTest::TestBasicConversionDangi() {
2926     UErrorCode status = U_ZERO_ERROR;
2927     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2928         *TimeZone::getGMT(), Locale("en@calendar=dangi"), status));
2929     if (U_FAILURE(status)) {
2930         errln("Fail: Cannot get Dangi calendar");
2931         return;
2932     }
2933     AsssertCalendarFieldValue(
2934         cal.getAlias(), test_time, "dangi",
2935         78, 39, 9, 40, 2, 8, 274, 3, 2, 0, 4, 4, 44, 51,
2936         323, 0, 0, 4355, 3, 4355, 2459885, 17091323, 0);
2937 }
TestBasicConversionIndian()2938 void CalendarTest::TestBasicConversionIndian() {
2939     UErrorCode status = U_ZERO_ERROR;
2940     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2941         *TimeZone::getGMT(), Locale("en@calendar=indian"), status));
2942     if (U_FAILURE(status)) {
2943         errln("Fail: Cannot get Indian calendar");
2944         return;
2945     }
2946     AsssertCalendarFieldValue(
2947         cal.getAlias(), test_time, "indian",
2948         0, 1944, 7, 33, 2, 10, 225, 3, 2, 0, 4, 4, 44, 51,
2949         323, 0, 0, 1944, 3, 1944, 2459885, 17091323, 0);
2950 }
TestBasicConversionCoptic()2951 void CalendarTest::TestBasicConversionCoptic() {
2952     UErrorCode status = U_ZERO_ERROR;
2953     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2954         *TimeZone::getGMT(), Locale("en@calendar=coptic"), status));
2955     if (U_FAILURE(status)) {
2956         errln("Fail: Cannot get Coptic calendar");
2957         return;
2958     }
2959     AsssertCalendarFieldValue(
2960         cal.getAlias(), test_time, "coptic",
2961         1, 1739, 1, 8, 4, 22, 52, 3, 4, 0, 4, 4, 44, 51,
2962         323, 0, 0, 1739, 3, 1739, 2459885, 17091323, 0);
2963 }
TestBasicConversionEthiopic()2964 void CalendarTest::TestBasicConversionEthiopic() {
2965     UErrorCode status = U_ZERO_ERROR;
2966     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2967         *TimeZone::getGMT(), Locale("en@calendar=ethiopic"), status));
2968     if (U_FAILURE(status)) {
2969         errln("Fail: Cannot get Ethiopic calendar");
2970         return;
2971     }
2972     AsssertCalendarFieldValue(
2973         cal.getAlias(), test_time, "ethiopic",
2974         1, 2015, 1, 8, 4, 22, 52, 3, 4, 0, 4, 4, 44, 51,
2975         323, 0, 0, 2015, 3, 2015, 2459885, 17091323, 0);
2976 }
TestBasicConversionEthiopicAmeteAlem()2977 void CalendarTest::TestBasicConversionEthiopicAmeteAlem() {
2978     UErrorCode status = U_ZERO_ERROR;
2979     LocalPointer<Calendar> cal(icu::Calendar::createInstance(
2980         *TimeZone::getGMT(), Locale("en@calendar=ethiopic-amete-alem"), status));
2981     if (U_FAILURE(status)) {
2982         errln("Fail: Cannot get EthiopicAmeteAlem calendar");
2983         return;
2984     }
2985     AsssertCalendarFieldValue(
2986         cal.getAlias(), test_time, "ethiopic-amete-alem",
2987         0, 7515, 1, 8, 4, 22, 52, 3, 4, 0, 4, 4, 44, 51,
2988         323, 0, 0, 2015, 3, 2015, 2459885, 17091323, 0);
2989 }
2990 
2991 
setAndTestCalendar(Calendar * cal,int32_t initMonth,int32_t initDay,int32_t initYear,UErrorCode & status)2992 void CalendarTest::setAndTestCalendar(Calendar* cal, int32_t initMonth, int32_t initDay, int32_t initYear, UErrorCode& status) {
2993         cal->clear();
2994         cal->setLenient(false);
2995         cal->set(initYear, initMonth, initDay);
2996         int32_t day = cal->get(UCAL_DAY_OF_MONTH, status);
2997         int32_t month = cal->get(UCAL_MONTH, status);
2998         int32_t year = cal->get(UCAL_YEAR, status);
2999         if(U_FAILURE(status))
3000             return;
3001 
3002         if(initDay != day || initMonth != month || initYear != year)
3003         {
3004             errln(" year init values:\tmonth %i\tday %i\tyear %i", initMonth, initDay, initYear);
3005             errln("values post set():\tmonth %i\tday %i\tyear %i",month, day, year);
3006         }
3007 }
3008 
setAndTestWholeYear(Calendar * cal,int32_t startYear,UErrorCode & status)3009 void CalendarTest::setAndTestWholeYear(Calendar* cal, int32_t startYear, UErrorCode& status) {
3010         for(int32_t startMonth = 0; startMonth < 12; startMonth++) {
3011             for(int32_t startDay = 1; startDay < 31; startDay++ ) {
3012                     setAndTestCalendar(cal, startMonth, startDay, startYear, status);
3013                     if(U_FAILURE(status) && startDay == 30) {
3014                         status = U_ZERO_ERROR;
3015                         continue;
3016                     }
3017                     TEST_CHECK_STATUS;
3018             }
3019         }
3020 }
3021 
3022 // =====================================================================
3023 
3024 typedef struct {
3025     int16_t  gYear;
3026     int8_t   gMon;
3027     int8_t   gDay;
3028     int16_t  uYear;
3029     int8_t   uMon;
3030     int8_t   uDay;
3031 } GregoUmmAlQuraMap;
3032 
3033 // data from
3034 // Official Umm-al-Qura calendar of SA:
3035 // home, http://www.ummulqura.org.sa/default.aspx
3036 // converter, http://www.ummulqura.org.sa/Index.aspx
3037 static const GregoUmmAlQuraMap guMappings[] = {
3038 //  gregorian,    ummAlQura
3039 //  year mo da,   year mo da
3040 //  (using 1-based months here)
3041   { 1882,11,12,   1300, 1, 1 },
3042   { 1892, 7,25,   1310, 1, 1 },
3043   { 1896, 6,12,   1314, 1, 1 },
3044   { 1898, 5,22,   1316, 1, 1 },
3045   { 1900, 4,30,   1318, 1, 1 },
3046   { 1901, 4,20,   1319, 1, 1 },
3047   { 1902, 4,10,   1320, 1, 1 },
3048   { 1903, 3,30,   1321, 1, 1 },
3049   { 1904, 3,19,   1322, 1, 1 },
3050   { 1905, 3, 8,   1323, 1, 1 },
3051   { 1906, 2,25,   1324, 1, 1 },
3052   { 1907, 2,14,   1325, 1, 1 },
3053   { 1908, 2, 4,   1326, 1, 1 },
3054   { 1909, 1,23,   1327, 1, 1 },
3055   { 1910, 1,13,   1328, 1, 1 },
3056   { 1911, 1, 2,   1329, 1, 1 },
3057   { 1911,12,22,   1330, 1, 1 },
3058   { 1912,12,10,   1331, 1, 1 },
3059   { 1913,11,30,   1332, 1, 1 },
3060   { 1914,11,19,   1333, 1, 1 },
3061   { 1915,11, 9,   1334, 1, 1 },
3062   { 1916,10,28,   1335, 1, 1 },
3063   { 1917,10,18,   1336, 1, 1 },
3064   { 1918,10, 7,   1337, 1, 1 },
3065   { 1919, 9,26,   1338, 1, 1 },
3066   { 1920, 9,14,   1339, 1, 1 },
3067   { 1921, 9, 4,   1340, 1, 1 },
3068   { 1922, 8,24,   1341, 1, 1 },
3069   { 1923, 8,14,   1342, 1, 1 },
3070   { 1924, 8, 2,   1343, 1, 1 },
3071   { 1925, 7,22,   1344, 1, 1 },
3072   { 1926, 7,11,   1345, 1, 1 },
3073   { 1927, 6,30,   1346, 1, 1 },
3074   { 1928, 6,19,   1347, 1, 1 },
3075   { 1929, 6, 9,   1348, 1, 1 },
3076   { 1930, 5,29,   1349, 1, 1 },
3077   { 1931, 5,19,   1350, 1, 1 },
3078   { 1932, 5, 7,   1351, 1, 1 },
3079   { 1933, 4,26,   1352, 1, 1 },
3080   { 1934, 4,15,   1353, 1, 1 },
3081   { 1935, 4, 5,   1354, 1, 1 },
3082   { 1936, 3,24,   1355, 1, 1 },
3083   { 1937, 3,14,   1356, 1, 1 },
3084   { 1938, 3, 4,   1357, 1, 1 },
3085   { 1939, 2,21,   1358, 1, 1 },
3086   { 1940, 2,10,   1359, 1, 1 },
3087   { 1941, 1,29,   1360, 1, 1 },
3088   { 1942, 1,18,   1361, 1, 1 },
3089   { 1943, 1, 8,   1362, 1, 1 },
3090   { 1943,12,28,   1363, 1, 1 },
3091   { 1944,12,17,   1364, 1, 1 },
3092   { 1945,12, 6,   1365, 1, 1 },
3093   { 1946,11,25,   1366, 1, 1 },
3094   { 1947,11,14,   1367, 1, 1 },
3095   { 1948,11, 3,   1368, 1, 1 },
3096   { 1949,10,23,   1369, 1, 1 },
3097   { 1950,10,13,   1370, 1, 1 },
3098   { 1951,10, 3,   1371, 1, 1 },
3099   { 1952, 9,21,   1372, 1, 1 },
3100   { 1953, 9,10,   1373, 1, 1 },
3101   { 1954, 8,30,   1374, 1, 1 },
3102   { 1955, 8,19,   1375, 1, 1 },
3103   { 1956, 8, 8,   1376, 1, 1 },
3104   { 1957, 7,29,   1377, 1, 1 },
3105   { 1958, 7,18,   1378, 1, 1 },
3106   { 1959, 7, 8,   1379, 1, 1 },
3107   { 1960, 6,26,   1380, 1, 1 },
3108   { 1961, 6,15,   1381, 1, 1 },
3109   { 1962, 6, 4,   1382, 1, 1 },
3110   { 1963, 5,24,   1383, 1, 1 },
3111   { 1964, 5,13,   1384, 1, 1 },
3112   { 1965, 5, 3,   1385, 1, 1 },
3113   { 1966, 4,22,   1386, 1, 1 },
3114   { 1967, 4,11,   1387, 1, 1 },
3115   { 1968, 3,30,   1388, 1, 1 },
3116   { 1969, 3,19,   1389, 1, 1 },
3117   { 1970, 3, 9,   1390, 1, 1 },
3118   { 1971, 2,27,   1391, 1, 1 },
3119   { 1972, 2,16,   1392, 1, 1 },
3120   { 1973, 2, 5,   1393, 1, 1 },
3121   { 1974, 1,25,   1394, 1, 1 },
3122   { 1975, 1,14,   1395, 1, 1 },
3123   { 1976, 1, 3,   1396, 1, 1 },
3124   { 1976,12,22,   1397, 1, 1 },
3125   { 1977,12,12,   1398, 1, 1 },
3126   { 1978,12, 1,   1399, 1, 1 },
3127   { 1979,11,21,   1400, 1, 1 },
3128   { 1980,11, 9,   1401, 1, 1 },
3129   { 1981,10,29,   1402, 1, 1 },
3130   { 1982,10,18,   1403, 1, 1 },
3131   { 1983,10, 8,   1404, 1, 1 },
3132   { 1984, 9,26,   1405, 1, 1 },
3133   { 1985, 9,16,   1406, 1, 1 },
3134   { 1986, 9, 6,   1407, 1, 1 },
3135   { 1987, 8,26,   1408, 1, 1 },
3136   { 1988, 8,14,   1409, 1, 1 },
3137   { 1989, 8, 3,   1410, 1, 1 },
3138   { 1990, 7,23,   1411, 1, 1 },
3139   { 1991, 7,13,   1412, 1, 1 },
3140   { 1992, 7, 2,   1413, 1, 1 },
3141   { 1993, 6,21,   1414, 1, 1 },
3142   { 1994, 6,11,   1415, 1, 1 },
3143   { 1995, 5,31,   1416, 1, 1 },
3144   { 1996, 5,19,   1417, 1, 1 },
3145   { 1997, 5, 8,   1418, 1, 1 },
3146   { 1998, 4,28,   1419, 1, 1 },
3147   { 1999, 4,17,   1420, 1, 1 },
3148   { 1999, 5,16,   1420, 2, 1 },
3149   { 1999, 6,15,   1420, 3, 1 },
3150   { 1999, 7,14,   1420, 4, 1 },
3151   { 1999, 8,12,   1420, 5, 1 },
3152   { 1999, 9,11,   1420, 6, 1 },
3153   { 1999,10,10,   1420, 7, 1 },
3154   { 1999,11, 9,   1420, 8, 1 },
3155   { 1999,12, 9,   1420, 9, 1 },
3156   { 2000, 1, 8,   1420,10, 1 },
3157   { 2000, 2, 7,   1420,11, 1 },
3158   { 2000, 3, 7,   1420,12, 1 },
3159   { 2000, 4, 6,   1421, 1, 1 },
3160   { 2000, 5, 5,   1421, 2, 1 },
3161   { 2000, 6, 3,   1421, 3, 1 },
3162   { 2000, 7, 3,   1421, 4, 1 },
3163   { 2000, 8, 1,   1421, 5, 1 },
3164   { 2000, 8,30,   1421, 6, 1 },
3165   { 2000, 9,28,   1421, 7, 1 },
3166   { 2000,10,28,   1421, 8, 1 },
3167   { 2000,11,27,   1421, 9, 1 },
3168   { 2000,12,27,   1421,10, 1 },
3169   { 2001, 1,26,   1421,11, 1 },
3170   { 2001, 2,24,   1421,12, 1 },
3171   { 2001, 3,26,   1422, 1, 1 },
3172   { 2001, 4,25,   1422, 2, 1 },
3173   { 2001, 5,24,   1422, 3, 1 },
3174   { 2001, 6,22,   1422, 4, 1 },
3175   { 2001, 7,22,   1422, 5, 1 },
3176   { 2001, 8,20,   1422, 6, 1 },
3177   { 2001, 9,18,   1422, 7, 1 },
3178   { 2001,10,17,   1422, 8, 1 },
3179   { 2001,11,16,   1422, 9, 1 },
3180   { 2001,12,16,   1422,10, 1 },
3181   { 2002, 1,15,   1422,11, 1 },
3182   { 2002, 2,13,   1422,12, 1 },
3183   { 2002, 3,15,   1423, 1, 1 },
3184   { 2002, 4,14,   1423, 2, 1 },
3185   { 2002, 5,13,   1423, 3, 1 },
3186   { 2002, 6,12,   1423, 4, 1 },
3187   { 2002, 7,11,   1423, 5, 1 },
3188   { 2002, 8,10,   1423, 6, 1 },
3189   { 2002, 9, 8,   1423, 7, 1 },
3190   { 2002,10, 7,   1423, 8, 1 },
3191   { 2002,11, 6,   1423, 9, 1 },
3192   { 2002,12, 5,   1423,10, 1 },
3193   { 2003, 1, 4,   1423,11, 1 },
3194   { 2003, 2, 2,   1423,12, 1 },
3195   { 2003, 3, 4,   1424, 1, 1 },
3196   { 2003, 4, 3,   1424, 2, 1 },
3197   { 2003, 5, 2,   1424, 3, 1 },
3198   { 2003, 6, 1,   1424, 4, 1 },
3199   { 2003, 7, 1,   1424, 5, 1 },
3200   { 2003, 7,30,   1424, 6, 1 },
3201   { 2003, 8,29,   1424, 7, 1 },
3202   { 2003, 9,27,   1424, 8, 1 },
3203   { 2003,10,26,   1424, 9, 1 },
3204   { 2003,11,25,   1424,10, 1 },
3205   { 2003,12,24,   1424,11, 1 },
3206   { 2004, 1,23,   1424,12, 1 },
3207   { 2004, 2,21,   1425, 1, 1 },
3208   { 2004, 3,22,   1425, 2, 1 },
3209   { 2004, 4,20,   1425, 3, 1 },
3210   { 2004, 5,20,   1425, 4, 1 },
3211   { 2004, 6,19,   1425, 5, 1 },
3212   { 2004, 7,18,   1425, 6, 1 },
3213   { 2004, 8,17,   1425, 7, 1 },
3214   { 2004, 9,15,   1425, 8, 1 },
3215   { 2004,10,15,   1425, 9, 1 },
3216   { 2004,11,14,   1425,10, 1 },
3217   { 2004,12,13,   1425,11, 1 },
3218   { 2005, 1,12,   1425,12, 1 },
3219   { 2005, 2,10,   1426, 1, 1 },
3220   { 2005, 3,11,   1426, 2, 1 },
3221   { 2005, 4,10,   1426, 3, 1 },
3222   { 2005, 5, 9,   1426, 4, 1 },
3223   { 2005, 6, 8,   1426, 5, 1 },
3224   { 2005, 7, 7,   1426, 6, 1 },
3225   { 2005, 8, 6,   1426, 7, 1 },
3226   { 2005, 9, 5,   1426, 8, 1 },
3227   { 2005,10, 4,   1426, 9, 1 },
3228   { 2005,11, 3,   1426,10, 1 },
3229   { 2005,12, 3,   1426,11, 1 },
3230   { 2006, 1, 1,   1426,12, 1 },
3231   { 2006, 1,31,   1427, 1, 1 },
3232   { 2006, 3, 1,   1427, 2, 1 },
3233   { 2006, 3,30,   1427, 3, 1 },
3234   { 2006, 4,29,   1427, 4, 1 },
3235   { 2006, 5,28,   1427, 5, 1 },
3236   { 2006, 6,27,   1427, 6, 1 },
3237   { 2006, 7,26,   1427, 7, 1 },
3238   { 2006, 8,25,   1427, 8, 1 },
3239   { 2006, 9,24,   1427, 9, 1 },
3240   { 2006,10,23,   1427,10, 1 },
3241   { 2006,11,22,   1427,11, 1 },
3242   { 2006,12,22,   1427,12, 1 },
3243   { 2007, 1,20,   1428, 1, 1 },
3244   { 2007, 2,19,   1428, 2, 1 },
3245   { 2007, 3,20,   1428, 3, 1 },
3246   { 2007, 4,18,   1428, 4, 1 },
3247   { 2007, 5,18,   1428, 5, 1 },
3248   { 2007, 6,16,   1428, 6, 1 },
3249   { 2007, 7,15,   1428, 7, 1 },
3250   { 2007, 8,14,   1428, 8, 1 },
3251   { 2007, 9,13,   1428, 9, 1 },
3252   { 2007,10,13,   1428,10, 1 },
3253   { 2007,11,11,   1428,11, 1 },
3254   { 2007,12,11,   1428,12, 1 },
3255   { 2008, 1,10,   1429, 1, 1 },
3256   { 2008, 2, 8,   1429, 2, 1 },
3257   { 2008, 3, 9,   1429, 3, 1 },
3258   { 2008, 4, 7,   1429, 4, 1 },
3259   { 2008, 5, 6,   1429, 5, 1 },
3260   { 2008, 6, 5,   1429, 6, 1 },
3261   { 2008, 7, 4,   1429, 7, 1 },
3262   { 2008, 8, 2,   1429, 8, 1 },
3263   { 2008, 9, 1,   1429, 9, 1 },
3264   { 2008,10, 1,   1429,10, 1 },
3265   { 2008,10,30,   1429,11, 1 },
3266   { 2008,11,29,   1429,12, 1 },
3267   { 2008,12,29,   1430, 1, 1 },
3268   { 2009, 1,27,   1430, 2, 1 },
3269   { 2009, 2,26,   1430, 3, 1 },
3270   { 2009, 3,28,   1430, 4, 1 },
3271   { 2009, 4,26,   1430, 5, 1 },
3272   { 2009, 5,25,   1430, 6, 1 },
3273   { 2009, 6,24,   1430, 7, 1 },
3274   { 2009, 7,23,   1430, 8, 1 },
3275   { 2009, 8,22,   1430, 9, 1 },
3276   { 2009, 9,20,   1430,10, 1 },
3277   { 2009,10,20,   1430,11, 1 },
3278   { 2009,11,18,   1430,12, 1 },
3279   { 2009,12,18,   1431, 1, 1 },
3280   { 2010, 1,16,   1431, 2, 1 },
3281   { 2010, 2,15,   1431, 3, 1 },
3282   { 2010, 3,17,   1431, 4, 1 },
3283   { 2010, 4,15,   1431, 5, 1 },
3284   { 2010, 5,15,   1431, 6, 1 },
3285   { 2010, 6,13,   1431, 7, 1 },
3286   { 2010, 7,13,   1431, 8, 1 },
3287   { 2010, 8,11,   1431, 9, 1 },
3288   { 2010, 9,10,   1431,10, 1 },
3289   { 2010,10, 9,   1431,11, 1 },
3290   { 2010,11, 7,   1431,12, 1 },
3291   { 2010,12, 7,   1432, 1, 1 },
3292   { 2011, 1, 5,   1432, 2, 1 },
3293   { 2011, 2, 4,   1432, 3, 1 },
3294   { 2011, 3, 6,   1432, 4, 1 },
3295   { 2011, 4, 5,   1432, 5, 1 },
3296   { 2011, 5, 4,   1432, 6, 1 },
3297   { 2011, 6, 3,   1432, 7, 1 },
3298   { 2011, 7, 2,   1432, 8, 1 },
3299   { 2011, 8, 1,   1432, 9, 1 },
3300   { 2011, 8,30,   1432,10, 1 },
3301   { 2011, 9,29,   1432,11, 1 },
3302   { 2011,10,28,   1432,12, 1 },
3303   { 2011,11,26,   1433, 1, 1 },
3304   { 2011,12,26,   1433, 2, 1 },
3305   { 2012, 1,24,   1433, 3, 1 },
3306   { 2012, 2,23,   1433, 4, 1 },
3307   { 2012, 3,24,   1433, 5, 1 },
3308   { 2012, 4,22,   1433, 6, 1 },
3309   { 2012, 5,22,   1433, 7, 1 },
3310   { 2012, 6,21,   1433, 8, 1 },
3311   { 2012, 7,20,   1433, 9, 1 },
3312   { 2012, 8,19,   1433,10, 1 },
3313   { 2012, 9,17,   1433,11, 1 },
3314   { 2012,10,17,   1433,12, 1 },
3315   { 2012,11,15,   1434, 1, 1 },
3316   { 2012,12,14,   1434, 2, 1 },
3317   { 2013, 1,13,   1434, 3, 1 },
3318   { 2013, 2,11,   1434, 4, 1 },
3319   { 2013, 3,13,   1434, 5, 1 },
3320   { 2013, 4,11,   1434, 6, 1 },
3321   { 2013, 5,11,   1434, 7, 1 },
3322   { 2013, 6,10,   1434, 8, 1 },
3323   { 2013, 7, 9,   1434, 9, 1 },
3324   { 2013, 8, 8,   1434,10, 1 },
3325   { 2013, 9, 7,   1434,11, 1 },
3326   { 2013,10, 6,   1434,12, 1 },
3327   { 2013,11, 4,   1435, 1, 1 },
3328   { 2013,12, 4,   1435, 2, 1 },
3329   { 2014, 1, 2,   1435, 3, 1 },
3330   { 2014, 2, 1,   1435, 4, 1 },
3331   { 2014, 3, 2,   1435, 5, 1 },
3332   { 2014, 4, 1,   1435, 6, 1 },
3333   { 2014, 4,30,   1435, 7, 1 },
3334   { 2014, 5,30,   1435, 8, 1 },
3335   { 2014, 6,28,   1435, 9, 1 },
3336   { 2014, 7,28,   1435,10, 1 },
3337   { 2014, 8,27,   1435,11, 1 },
3338   { 2014, 9,25,   1435,12, 1 },
3339   { 2014,10,25,   1436, 1, 1 },
3340   { 2014,11,23,   1436, 2, 1 },
3341   { 2014,12,23,   1436, 3, 1 },
3342   { 2015, 1,21,   1436, 4, 1 },
3343   { 2015, 2,20,   1436, 5, 1 },
3344   { 2015, 3,21,   1436, 6, 1 },
3345   { 2015, 4,20,   1436, 7, 1 },
3346   { 2015, 5,19,   1436, 8, 1 },
3347   { 2015, 6,18,   1436, 9, 1 },
3348   { 2015, 7,17,   1436,10, 1 },
3349   { 2015, 8,16,   1436,11, 1 },
3350   { 2015, 9,14,   1436,12, 1 },
3351   { 2015,10,14,   1437, 1, 1 },
3352   { 2015,11,13,   1437, 2, 1 },
3353   { 2015,12,12,   1437, 3, 1 },
3354   { 2016, 1,11,   1437, 4, 1 },
3355   { 2016, 2,10,   1437, 5, 1 },
3356   { 2016, 3,10,   1437, 6, 1 },
3357   { 2016, 4, 8,   1437, 7, 1 },
3358   { 2016, 5, 8,   1437, 8, 1 },
3359   { 2016, 6, 6,   1437, 9, 1 },
3360   { 2016, 7, 6,   1437,10, 1 },
3361   { 2016, 8, 4,   1437,11, 1 },
3362   { 2016, 9, 2,   1437,12, 1 },
3363   { 2016,10, 2,   1438, 1, 1 },
3364   { 2016,11, 1,   1438, 2, 1 },
3365   { 2016,11,30,   1438, 3, 1 },
3366   { 2016,12,30,   1438, 4, 1 },
3367   { 2017, 1,29,   1438, 5, 1 },
3368   { 2017, 2,28,   1438, 6, 1 },
3369   { 2017, 3,29,   1438, 7, 1 },
3370   { 2017, 4,27,   1438, 8, 1 },
3371   { 2017, 5,27,   1438, 9, 1 },
3372   { 2017, 6,25,   1438,10, 1 },
3373   { 2017, 7,24,   1438,11, 1 },
3374   { 2017, 8,23,   1438,12, 1 },
3375   { 2017, 9,21,   1439, 1, 1 },
3376   { 2017,10,21,   1439, 2, 1 },
3377   { 2017,11,19,   1439, 3, 1 },
3378   { 2017,12,19,   1439, 4, 1 },
3379   { 2018, 1,18,   1439, 5, 1 },
3380   { 2018, 2,17,   1439, 6, 1 },
3381   { 2018, 3,18,   1439, 7, 1 },
3382   { 2018, 4,17,   1439, 8, 1 },
3383   { 2018, 5,16,   1439, 9, 1 },
3384   { 2018, 6,15,   1439,10, 1 },
3385   { 2018, 7,14,   1439,11, 1 },
3386   { 2018, 8,12,   1439,12, 1 },
3387   { 2018, 9,11,   1440, 1, 1 },
3388   { 2019, 8,31,   1441, 1, 1 },
3389   { 2020, 8,20,   1442, 1, 1 },
3390   { 2021, 8, 9,   1443, 1, 1 },
3391   { 2022, 7,30,   1444, 1, 1 },
3392   { 2023, 7,19,   1445, 1, 1 },
3393   { 2024, 7, 7,   1446, 1, 1 },
3394   { 2025, 6,26,   1447, 1, 1 },
3395   { 2026, 6,16,   1448, 1, 1 },
3396   { 2027, 6, 6,   1449, 1, 1 },
3397   { 2028, 5,25,   1450, 1, 1 },
3398   { 2029, 5,14,   1451, 1, 1 },
3399   { 2030, 5, 4,   1452, 1, 1 },
3400   { 2031, 4,23,   1453, 1, 1 },
3401   { 2032, 4,11,   1454, 1, 1 },
3402   { 2033, 4, 1,   1455, 1, 1 },
3403   { 2034, 3,22,   1456, 1, 1 },
3404   { 2035, 3,11,   1457, 1, 1 },
3405   { 2036, 2,29,   1458, 1, 1 },
3406   { 2037, 2,17,   1459, 1, 1 },
3407   { 2038, 2, 6,   1460, 1, 1 },
3408   { 2039, 1,26,   1461, 1, 1 },
3409   { 2040, 1,15,   1462, 1, 1 },
3410   { 2041, 1, 4,   1463, 1, 1 },
3411   { 2041,12,25,   1464, 1, 1 },
3412   { 2042,12,14,   1465, 1, 1 },
3413   { 2043,12, 3,   1466, 1, 1 },
3414   { 2044,11,21,   1467, 1, 1 },
3415   { 2045,11,11,   1468, 1, 1 },
3416   { 2046,10,31,   1469, 1, 1 },
3417   { 2047,10,21,   1470, 1, 1 },
3418   { 2048,10, 9,   1471, 1, 1 },
3419   { 2049, 9,29,   1472, 1, 1 },
3420   { 2050, 9,18,   1473, 1, 1 },
3421   { 2051, 9, 7,   1474, 1, 1 },
3422   { 2052, 8,26,   1475, 1, 1 },
3423   { 2053, 8,15,   1476, 1, 1 },
3424   { 2054, 8, 5,   1477, 1, 1 },
3425   { 2055, 7,26,   1478, 1, 1 },
3426   { 2056, 7,14,   1479, 1, 1 },
3427   { 2057, 7, 3,   1480, 1, 1 },
3428   { 2058, 6,22,   1481, 1, 1 },
3429   { 2059, 6,11,   1482, 1, 1 },
3430   { 2061, 5,21,   1484, 1, 1 },
3431   { 2063, 4,30,   1486, 1, 1 },
3432   { 2065, 4, 7,   1488, 1, 1 },
3433   { 2067, 3,17,   1490, 1, 1 },
3434   { 2069, 2,23,   1492, 1, 1 },
3435   { 2071, 2, 2,   1494, 1, 1 },
3436   { 2073, 1,10,   1496, 1, 1 },
3437   { 2074,12,20,   1498, 1, 1 },
3438   { 2076,11,28,   1500, 1, 1 },
3439   {    0, 0, 0,      0, 0, 0 }, // terminator
3440 };
3441 
3442 static const char16_t zoneSA[] = {0x41,0x73,0x69,0x61,0x2F,0x52,0x69,0x79,0x61,0x64,0x68,0}; // "Asia/Riyadh"
3443 
TestIslamicUmAlQura()3444 void CalendarTest::TestIslamicUmAlQura() {
3445 
3446     UErrorCode status = U_ZERO_ERROR;
3447     Locale umalquraLoc("ar_SA@calendar=islamic-umalqura");
3448     Locale gregoLoc("ar_SA@calendar=gregorian");
3449     TimeZone* tzSA = TimeZone::createTimeZone(UnicodeString(true, zoneSA, -1));
3450     Calendar* tstCal = Calendar::createInstance(*tzSA, umalquraLoc, status);
3451     Calendar* gregCal = Calendar::createInstance(*tzSA, gregoLoc, status);
3452 
3453     IslamicCalendar* iCal = dynamic_cast<IslamicCalendar*>(tstCal);
3454     if(uprv_strcmp(iCal->getType(), "islamic-umalqura") != 0) {
3455         errln("wrong type of calendar created - %s", iCal->getType());
3456     }
3457 
3458     int32_t firstYear = 1318;
3459     int32_t lastYear = 1368;    // just enough to be pretty sure
3460     //int32_t lastYear = 1480;    // the whole shootin' match
3461 
3462     tstCal->clear();
3463     tstCal->setLenient(false);
3464 
3465     int32_t day=0, month=0, year=0, initDay = 27, initMonth = IslamicCalendar::RAJAB, initYear = 1434;
3466 
3467     for( int32_t startYear = firstYear; startYear <= lastYear; startYear++) {
3468         setAndTestWholeYear(tstCal, startYear, status);
3469         status = U_ZERO_ERROR;
3470     }
3471 
3472     initMonth = IslamicCalendar::RABI_2;
3473     initDay = 5;
3474     int32_t loopCnt = 25;
3475     tstCal->clear();
3476     setAndTestCalendar( tstCal, initMonth, initDay, initYear, status);
3477     TEST_CHECK_STATUS;
3478 
3479     for(int x=1; x<=loopCnt; x++) {
3480         day = tstCal->get(UCAL_DAY_OF_MONTH,status);
3481         month = tstCal->get(UCAL_MONTH,status);
3482         year = tstCal->get(UCAL_YEAR,status);
3483         TEST_CHECK_STATUS;
3484         tstCal->roll(UCAL_DAY_OF_MONTH, (UBool)true, status);
3485         TEST_CHECK_STATUS;
3486     }
3487 
3488     if(day != (initDay + loopCnt - 1) || month != IslamicCalendar::RABI_2 || year != 1434)
3489       errln("invalid values for RABI_2 date after roll of %d", loopCnt);
3490 
3491     status = U_ZERO_ERROR;
3492     tstCal->clear();
3493     initMonth = 2;
3494     initDay = 30;
3495     setAndTestCalendar( tstCal, initMonth, initDay, initYear, status);
3496     if(U_SUCCESS(status)) {
3497         errln("error NOT detected status %i",status);
3498         errln("      init values:\tmonth %i\tday %i\tyear %i", initMonth, initDay, initYear);
3499         int32_t day = tstCal->get(UCAL_DAY_OF_MONTH, status);
3500         int32_t month = tstCal->get(UCAL_MONTH, status);
3501         int32_t year = tstCal->get(UCAL_YEAR, status);
3502         errln("values post set():\tmonth %i\tday %i\tyear %i",month, day, year);
3503     }
3504 
3505     status = U_ZERO_ERROR;
3506     tstCal->clear();
3507     initMonth = 3;
3508     initDay = 30;
3509     setAndTestCalendar( tstCal, initMonth, initDay, initYear, status);
3510     TEST_CHECK_STATUS;
3511 
3512     SimpleDateFormat* formatter = new SimpleDateFormat("yyyy-MM-dd", Locale::getUS(), status);
3513     UDate date = formatter->parse("1975-05-06", status);
3514     Calendar* is_cal = Calendar::createInstance(umalquraLoc, status);
3515     is_cal->setTime(date, status);
3516     int32_t is_day = is_cal->get(UCAL_DAY_OF_MONTH,status);
3517     int32_t is_month = is_cal->get(UCAL_MONTH,status);
3518     int32_t is_year = is_cal->get(UCAL_YEAR,status);
3519     TEST_CHECK_STATUS;
3520     if(is_day != 24 || is_month != IslamicCalendar::RABI_2 || is_year != 1395)
3521         errln("unexpected conversion date month %i not %i or day %i not 24 or year %i not 1395", is_month, IslamicCalendar::RABI_2, is_day, is_year);
3522 
3523     UDate date2 = is_cal->getTime(status);
3524     TEST_CHECK_STATUS;
3525     if(date2 != date) {
3526         errln("before(%f) and after(%f) dates don't match up!",date, date2);
3527     }
3528 
3529     // check against data
3530     const GregoUmmAlQuraMap* guMapPtr;
3531     gregCal->clear();
3532     tstCal->clear();
3533     for (guMapPtr = guMappings; guMapPtr->gYear != 0; guMapPtr++) {
3534         status = U_ZERO_ERROR;
3535         gregCal->set(guMapPtr->gYear, guMapPtr->gMon - 1, guMapPtr->gDay, 12, 0);
3536         date = gregCal->getTime(status);
3537         tstCal->setTime(date, status);
3538         int32_t uYear = tstCal->get(UCAL_YEAR, status);
3539         int32_t uMon = tstCal->get(UCAL_MONTH, status) + 1;
3540         int32_t uDay = tstCal->get(UCAL_DATE, status);
3541         if(U_FAILURE(status)) {
3542             errln("For gregorian %4d-%02d-%02d, get status %s",
3543                     guMapPtr->gYear, guMapPtr->gMon, guMapPtr->gDay, u_errorName(status) );
3544         } else if (uYear != guMapPtr->uYear || uMon != guMapPtr->uMon || uDay != guMapPtr->uDay) {
3545             errln("For gregorian %4d-%02d-%02d, expect umalqura %4d-%02d-%02d, get %4d-%02d-%02d",
3546                     guMapPtr->gYear, guMapPtr->gMon, guMapPtr->gDay,
3547                     guMapPtr->uYear, guMapPtr->uMon, guMapPtr->uDay, uYear, uMon, uDay );
3548         }
3549     }
3550 
3551     delete is_cal;
3552     delete formatter;
3553     delete gregCal;
3554     delete tstCal;
3555     delete tzSA;
3556 }
3557 
TestIslamicTabularDates()3558 void CalendarTest::TestIslamicTabularDates() {
3559     UErrorCode status = U_ZERO_ERROR;
3560     Locale islamicLoc("ar_SA@calendar=islamic-civil");
3561     Locale tblaLoc("ar_SA@calendar=islamic-tbla");
3562     SimpleDateFormat* formatter = new SimpleDateFormat("yyyy-MM-dd", Locale::getUS(), status);
3563     UDate date = formatter->parse("1975-05-06", status);
3564 
3565     Calendar* tstCal = Calendar::createInstance(islamicLoc, status);
3566     tstCal->setTime(date, status);
3567     int32_t is_day = tstCal->get(UCAL_DAY_OF_MONTH,status);
3568     int32_t is_month = tstCal->get(UCAL_MONTH,status);
3569     int32_t is_year = tstCal->get(UCAL_YEAR,status);
3570     TEST_CHECK_STATUS;
3571     delete tstCal;
3572 
3573     tstCal = Calendar::createInstance(tblaLoc, status);
3574     tstCal->setTime(date, status);
3575     int32_t tbla_day = tstCal->get(UCAL_DAY_OF_MONTH,status);
3576     int32_t tbla_month = tstCal->get(UCAL_MONTH,status);
3577     int32_t tbla_year = tstCal->get(UCAL_YEAR,status);
3578     TEST_CHECK_STATUS;
3579 
3580     if(tbla_month != is_month || tbla_year != is_year)
3581         errln("unexpected difference between islamic and tbla month %d : %d and/or year %d : %d",tbla_month,is_month,tbla_year,is_year);
3582 
3583     if(tbla_day - is_day != 1)
3584         errln("unexpected day difference between islamic and tbla: %d : %d ",tbla_day,is_day);
3585     delete tstCal;
3586     delete formatter;
3587 }
3588 
TestHebrewMonthValidation()3589 void CalendarTest::TestHebrewMonthValidation() {
3590     UErrorCode status = U_ZERO_ERROR;
3591     LocalPointer<Calendar>  cal(Calendar::createInstance(Locale::createFromName("he_IL@calendar=hebrew"), status));
3592     if (failure(status, "Calendar::createInstance, locale:he_IL@calendar=hebrew", true)) return;
3593     Calendar *pCal = cal.getAlias();
3594 
3595     UDate d;
3596     pCal->setLenient(false);
3597 
3598     // 5776 is a leap year and has month Adar I
3599     pCal->set(5776, HebrewCalendar::ADAR_1, 1);
3600     d = pCal->getTime(status);
3601     if (U_FAILURE(status)) {
3602         errln("Fail: 5776 Adar I 1 is a valid date.");
3603     }
3604     status = U_ZERO_ERROR;
3605 
3606     // 5777 is NOT a lear year and does not have month Adar I
3607     pCal->set(5777, HebrewCalendar::ADAR_1, 1);
3608     d = pCal->getTime(status);
3609     (void)d;
3610     if (status == U_ILLEGAL_ARGUMENT_ERROR) {
3611         logln("Info: U_ILLEGAL_ARGUMENT_ERROR, because 5777 Adar I 1 is not a valid date.");
3612     } else {
3613         errln("Fail: U_ILLEGAL_ARGUMENT_ERROR should be set for input date 5777 Adar I 1.");
3614     }
3615 }
3616 
TestWeekData()3617 void CalendarTest::TestWeekData() {
3618     // Each line contains two locales using the same set of week rule data.
3619     const char* LOCALE_PAIRS[] = {
3620         "en",       "en_US",
3621         "de",       "de_DE",
3622         "de_DE",    "en_DE",
3623         "en_GB",    "und_GB",
3624         "ar_EG",    "en_EG",
3625         "ar_SA",    "fr_SA",
3626         nullptr
3627     };
3628 
3629     UErrorCode status;
3630 
3631     for (int32_t i = 0; LOCALE_PAIRS[i] != nullptr; i += 2) {
3632         status = U_ZERO_ERROR;
3633         LocalPointer<Calendar>  cal1(Calendar::createInstance(LOCALE_PAIRS[i], status));
3634         LocalPointer<Calendar>  cal2(Calendar::createInstance(LOCALE_PAIRS[i + 1], status));
3635         TEST_CHECK_STATUS_LOCALE(LOCALE_PAIRS[i]);
3636 
3637         // First day of week
3638         UCalendarDaysOfWeek dow1 = cal1->getFirstDayOfWeek(status);
3639         UCalendarDaysOfWeek dow2 = cal2->getFirstDayOfWeek(status);
3640         TEST_CHECK_STATUS;
3641         TEST_ASSERT(dow1 == dow2);
3642 
3643         // Minimum days in first week
3644         uint8_t minDays1 = cal1->getMinimalDaysInFirstWeek();
3645         uint8_t minDays2 = cal2->getMinimalDaysInFirstWeek();
3646         TEST_ASSERT(minDays1 == minDays2);
3647 
3648         // Weekdays and Weekends
3649         for (int32_t d = UCAL_SUNDAY; d <= UCAL_SATURDAY; d++) {
3650             status = U_ZERO_ERROR;
3651             UCalendarWeekdayType wdt1 = cal1->getDayOfWeekType((UCalendarDaysOfWeek)d, status);
3652             UCalendarWeekdayType wdt2 = cal2->getDayOfWeekType((UCalendarDaysOfWeek)d, status);
3653             TEST_CHECK_STATUS;
3654             TEST_ASSERT(wdt1 == wdt2);
3655         }
3656     }
3657 }
3658 
3659 typedef struct {
3660     const char* zone;
3661     const CalFields base;
3662     int32_t deltaDays;
3663     UCalendarWallTimeOption skippedWTOpt;
3664     const CalFields expected;
3665 } TestAddAcrossZoneTransitionData;
3666 
3667 static const TestAddAcrossZoneTransitionData AAZTDATA[] =
3668 {
3669     // Time zone                Base wall time                      day(s)  Skipped time options
3670     //                          Expected wall time
3671 
3672     // Add 1 day, from the date before DST transition
3673     {"America/Los_Angeles",     CalFields(2014,3,8,1,59,59,999),    1,      UCAL_WALLTIME_FIRST,
3674                                 CalFields(2014,3,9,1,59,59,999)},
3675 
3676     {"America/Los_Angeles",     CalFields(2014,3,8,1,59,59,999),    1,      UCAL_WALLTIME_LAST,
3677                                 CalFields(2014,3,9,1,59,59,999)},
3678 
3679     {"America/Los_Angeles",     CalFields(2014,3,8,1,59,59,999),    1,      UCAL_WALLTIME_NEXT_VALID,
3680                                 CalFields(2014,3,9,1,59,59,999)},
3681 
3682 
3683     {"America/Los_Angeles",     CalFields(2014,3,8,2,0,0,0),        1,      UCAL_WALLTIME_FIRST,
3684                                 CalFields(2014,3,9,1,0,0,0)},
3685 
3686     {"America/Los_Angeles",     CalFields(2014,3,8,2,0,0,0),        1,      UCAL_WALLTIME_LAST,
3687                                 CalFields(2014,3,9,3,0,0,0)},
3688 
3689     {"America/Los_Angeles",     CalFields(2014,3,8,2,0,0,0),        1,      UCAL_WALLTIME_NEXT_VALID,
3690                                 CalFields(2014,3,9,3,0,0,0)},
3691 
3692 
3693     {"America/Los_Angeles",     CalFields(2014,3,8,2,30,0,0),       1,      UCAL_WALLTIME_FIRST,
3694                                 CalFields(2014,3,9,1,30,0,0)},
3695 
3696     {"America/Los_Angeles",     CalFields(2014,3,8,2,30,0,0),       1,      UCAL_WALLTIME_LAST,
3697                                 CalFields(2014,3,9,3,30,0,0)},
3698 
3699     {"America/Los_Angeles",     CalFields(2014,3,8,2,30,0,0),       1,      UCAL_WALLTIME_NEXT_VALID,
3700                                 CalFields(2014,3,9,3,0,0,0)},
3701 
3702 
3703     {"America/Los_Angeles",     CalFields(2014,3,8,3,0,0,0),        1,      UCAL_WALLTIME_FIRST,
3704                                 CalFields(2014,3,9,3,0,0,0)},
3705 
3706     {"America/Los_Angeles",     CalFields(2014,3,8,3,0,0,0),        1,      UCAL_WALLTIME_LAST,
3707                                 CalFields(2014,3,9,3,0,0,0)},
3708 
3709     {"America/Los_Angeles",     CalFields(2014,3,8,3,0,0,0),        1,      UCAL_WALLTIME_NEXT_VALID,
3710                                 CalFields(2014,3,9,3,0,0,0)},
3711 
3712     // Subtract 1 day, from one day after DST transition
3713     {"America/Los_Angeles",     CalFields(2014,3,10,1,59,59,999),   -1,     UCAL_WALLTIME_FIRST,
3714                                 CalFields(2014,3,9,1,59,59,999)},
3715 
3716     {"America/Los_Angeles",     CalFields(2014,3,10,1,59,59,999),   -1,     UCAL_WALLTIME_LAST,
3717                                 CalFields(2014,3,9,1,59,59,999)},
3718 
3719     {"America/Los_Angeles",     CalFields(2014,3,10,1,59,59,999),   -1,     UCAL_WALLTIME_NEXT_VALID,
3720                                 CalFields(2014,3,9,1,59,59,999)},
3721 
3722 
3723     {"America/Los_Angeles",     CalFields(2014,3,10,2,0,0,0),       -1,     UCAL_WALLTIME_FIRST,
3724                                 CalFields(2014,3,9,1,0,0,0)},
3725 
3726     {"America/Los_Angeles",     CalFields(2014,3,10,2,0,0,0),       -1,     UCAL_WALLTIME_LAST,
3727                                 CalFields(2014,3,9,3,0,0,0)},
3728 
3729     {"America/Los_Angeles",     CalFields(2014,3,10,2,0,0,0),       -1,     UCAL_WALLTIME_NEXT_VALID,
3730                                 CalFields(2014,3,9,3,0,0,0)},
3731 
3732 
3733     {"America/Los_Angeles",     CalFields(2014,3,10,2,30,0,0),      -1,     UCAL_WALLTIME_FIRST,
3734                                 CalFields(2014,3,9,1,30,0,0)},
3735 
3736     {"America/Los_Angeles",     CalFields(2014,3,10,2,30,0,0),      -1,     UCAL_WALLTIME_LAST,
3737                                 CalFields(2014,3,9,3,30,0,0)},
3738 
3739     {"America/Los_Angeles",     CalFields(2014,3,10,2,30,0,0),      -1,     UCAL_WALLTIME_NEXT_VALID,
3740                                 CalFields(2014,3,9,3,0,0,0)},
3741 
3742 
3743     {"America/Los_Angeles",     CalFields(2014,3,10,3,0,0,0),       -1,     UCAL_WALLTIME_FIRST,
3744                                 CalFields(2014,3,9,3,0,0,0)},
3745 
3746     {"America/Los_Angeles",     CalFields(2014,3,10,3,0,0,0),       -1,     UCAL_WALLTIME_LAST,
3747                                 CalFields(2014,3,9,3,0,0,0)},
3748 
3749     {"America/Los_Angeles",     CalFields(2014,3,10,3,0,0,0),       -1,     UCAL_WALLTIME_NEXT_VALID,
3750                                 CalFields(2014,3,9,3,0,0,0)},
3751 
3752 
3753     // Test case for ticket#10544
3754     {"America/Santiago",        CalFields(2013,4,27,0,0,0,0),       134,    UCAL_WALLTIME_FIRST,
3755                                 CalFields(2013,9,7,23,0,0,0)},
3756 
3757     {"America/Santiago",        CalFields(2013,4,27,0,0,0,0),       134,    UCAL_WALLTIME_LAST,
3758                                 CalFields(2013,9,8,1,0,0,0)},
3759 
3760     {"America/Santiago",        CalFields(2013,4,27,0,0,0,0),       134,    UCAL_WALLTIME_NEXT_VALID,
3761                                 CalFields(2013,9,8,1,0,0,0)},
3762 
3763 
3764     {"America/Santiago",        CalFields(2013,4,27,0,30,0,0),      134,    UCAL_WALLTIME_FIRST,
3765                                 CalFields(2013,9,7,23,30,0,0)},
3766 
3767     {"America/Santiago",        CalFields(2013,4,27,0,30,0,0),      134,    UCAL_WALLTIME_LAST,
3768                                 CalFields(2013,9,8,1,30,0,0)},
3769 
3770     {"America/Santiago",        CalFields(2013,4,27,0,30,0,0),      134,    UCAL_WALLTIME_NEXT_VALID,
3771                                 CalFields(2013,9,8,1,0,0,0)},
3772 
3773 
3774     // Extreme transition - Pacific/Apia completely skips 2011-12-30
3775     {"Pacific/Apia",            CalFields(2011,12,29,0,0,0,0),      1,      UCAL_WALLTIME_FIRST,
3776                                 CalFields(2011,12,31,0,0,0,0)},
3777 
3778     {"Pacific/Apia",            CalFields(2011,12,29,0,0,0,0),      1,      UCAL_WALLTIME_LAST,
3779                                 CalFields(2011,12,31,0,0,0,0)},
3780 
3781     {"Pacific/Apia",            CalFields(2011,12,29,0,0,0,0),      1,      UCAL_WALLTIME_NEXT_VALID,
3782                                 CalFields(2011,12,31,0,0,0,0)},
3783 
3784 
3785     {"Pacific/Apia",            CalFields(2011,12,31,12,0,0,0),     -1,     UCAL_WALLTIME_FIRST,
3786                                 CalFields(2011,12,29,12,0,0,0)},
3787 
3788     {"Pacific/Apia",            CalFields(2011,12,31,12,0,0,0),     -1,     UCAL_WALLTIME_LAST,
3789                                 CalFields(2011,12,29,12,0,0,0)},
3790 
3791     {"Pacific/Apia",            CalFields(2011,12,31,12,0,0,0),     -1,     UCAL_WALLTIME_NEXT_VALID,
3792                                 CalFields(2011,12,29,12,0,0,0)},
3793 
3794 
3795     // 30 minutes DST - Australia/Lord_Howe
3796     {"Australia/Lord_Howe",     CalFields(2013,10,5,2,15,0,0),      1,      UCAL_WALLTIME_FIRST,
3797                                 CalFields(2013,10,6,1,45,0,0)},
3798 
3799     {"Australia/Lord_Howe",     CalFields(2013,10,5,2,15,0,0),      1,      UCAL_WALLTIME_LAST,
3800                                 CalFields(2013,10,6,2,45,0,0)},
3801 
3802     {"Australia/Lord_Howe",     CalFields(2013,10,5,2,15,0,0),      1,      UCAL_WALLTIME_NEXT_VALID,
3803                                 CalFields(2013,10,6,2,30,0,0)},
3804 
3805     {nullptr, CalFields(0,0,0,0,0,0,0), 0, UCAL_WALLTIME_LAST, CalFields(0,0,0,0,0,0,0)}
3806 };
3807 
TestAddAcrossZoneTransition()3808 void CalendarTest::TestAddAcrossZoneTransition() {
3809     UErrorCode status = U_ZERO_ERROR;
3810     GregorianCalendar cal(status);
3811     TEST_CHECK_STATUS;
3812 
3813     for (int32_t i = 0; AAZTDATA[i].zone; i++) {
3814         status = U_ZERO_ERROR;
3815         TimeZone *tz = TimeZone::createTimeZone(AAZTDATA[i].zone);
3816         cal.adoptTimeZone(tz);
3817         cal.setSkippedWallTimeOption(AAZTDATA[i].skippedWTOpt);
3818         AAZTDATA[i].base.setTo(cal);
3819         cal.add(UCAL_DATE, AAZTDATA[i].deltaDays, status);
3820         TEST_CHECK_STATUS;
3821 
3822         if (!AAZTDATA[i].expected.isEquivalentTo(cal, status)) {
3823             CalFields res(cal, status);
3824             TEST_CHECK_STATUS;
3825             char buf[32];
3826             const char *optDisp = AAZTDATA[i].skippedWTOpt == UCAL_WALLTIME_FIRST ? "FIRST" :
3827                     AAZTDATA[i].skippedWTOpt == UCAL_WALLTIME_LAST ? "LAST" : "NEXT_VALID";
3828             dataerrln(UnicodeString("Error: base:") + AAZTDATA[i].base.toString(buf, sizeof(buf)) + ", tz:" + AAZTDATA[i].zone
3829                         + ", delta:" + AAZTDATA[i].deltaDays + " day(s), opt:" + optDisp
3830                         + ", result:" + res.toString(buf, sizeof(buf))
3831                         + " - expected:" + AAZTDATA[i].expected.toString(buf, sizeof(buf)));
3832         }
3833     }
3834 }
3835 
3836 // Data in a separate file (Gregorian to Chinese lunar map)
3837 #define INCLUDED_FROM_CALTEST_CPP
3838 #include "caltestdata.h"
3839 
TestChineseCalendarMapping()3840 void CalendarTest::TestChineseCalendarMapping() {
3841     UErrorCode status = U_ZERO_ERROR;
3842     LocalPointer<TimeZone> zone(TimeZone::createTimeZone(UnicodeString("China")));
3843     Locale locEnCalGregory = Locale::createFromName("en@calendar=gregorian");
3844     Locale locEnCalChinese = Locale::createFromName("en@calendar=chinese");
3845     LocalPointer<Calendar>  calGregory(Calendar::createInstance(zone->clone(), locEnCalGregory, status));
3846     LocalPointer<Calendar>  calChinese(Calendar::createInstance(zone.orphan(), locEnCalChinese, status));
3847     if ( U_FAILURE(status) ) {
3848         errln("Fail: Calendar::createInstance fails for en with calendar=gregorian or calendar=chinese: %s", u_errorName(status));
3849     } else {
3850         const GregoToLunar * mapPtr = gregoToLunar; // in "caltestdata.h" included above
3851         calGregory->clear();
3852         calChinese->clear();
3853         for (; mapPtr->gyr != 0; mapPtr++) {
3854             status = U_ZERO_ERROR;
3855             calGregory->set(mapPtr->gyr, mapPtr->gmo - 1, mapPtr->gda, 8, 0);
3856             UDate date = calGregory->getTime(status);
3857             calChinese->setTime(date, status);
3858             if ( U_FAILURE(status) ) {
3859                 errln("Fail: for Gregorian %4d-%02d-%02d, calGregory->getTime or calChinese->setTime reports: %s",
3860                         mapPtr->gyr, mapPtr->gmo, mapPtr->gda, u_errorName(status));
3861                 continue;
3862             }
3863             int32_t era = calChinese->get(UCAL_ERA, status);
3864             int32_t yr  = calChinese->get(UCAL_YEAR, status);
3865             int32_t mo  = calChinese->get(UCAL_MONTH, status) + 1;
3866             int32_t lp  = calChinese->get(UCAL_IS_LEAP_MONTH, status);
3867             int32_t da  = calChinese->get(UCAL_DATE, status);
3868             if ( U_FAILURE(status) ) {
3869                 errln("Fail: for Gregorian %4d-%02d-%02d, calChinese->get (for era, yr, mo, leapmo, da) reports: %s",
3870                         mapPtr->gyr, mapPtr->gmo, mapPtr->gda, u_errorName(status));
3871                 continue;
3872             }
3873             if (yr != mapPtr->cyr || mo != mapPtr->cmo || lp != mapPtr->clp || da != mapPtr->cda) {
3874                 errln("Fail: for Gregorian %4d-%02d-%02d, expected Chinese %2d-%02d(%d)-%02d, got %2d-%02d(%d)-%02d",
3875                         mapPtr->gyr, mapPtr->gmo, mapPtr->gda, mapPtr->cyr, mapPtr->cmo, mapPtr->clp, mapPtr->cda, yr, mo, lp, da);
3876                 continue;
3877             }
3878             // If Grego->Chinese worked, try reverse mapping
3879             calChinese->set(UCAL_ERA, era);
3880             calChinese->set(UCAL_YEAR, mapPtr->cyr);
3881             calChinese->set(UCAL_MONTH, mapPtr->cmo - 1);
3882             calChinese->set(UCAL_IS_LEAP_MONTH, mapPtr->clp);
3883             calChinese->set(UCAL_DATE, mapPtr->cda);
3884             calChinese->set(UCAL_HOUR_OF_DAY, 8);
3885             date = calChinese->getTime(status);
3886             calGregory->setTime(date, status);
3887             if ( U_FAILURE(status) ) {
3888                 errln("Fail: for Chinese %2d-%02d(%d)-%02d, calChinese->getTime or calGregory->setTime reports: %s",
3889                         mapPtr->cyr, mapPtr->cmo, mapPtr->clp, mapPtr->cda, u_errorName(status));
3890                 continue;
3891             }
3892             yr  = calGregory->get(UCAL_YEAR, status);
3893             mo  = calGregory->get(UCAL_MONTH, status) + 1;
3894             da  = calGregory->get(UCAL_DATE, status);
3895             if ( U_FAILURE(status) ) {
3896                 errln("Fail: for Chinese %2d-%02d(%d)-%02d, calGregory->get (for yr, mo, da) reports: %s",
3897                         mapPtr->cyr, mapPtr->cmo, mapPtr->clp, mapPtr->cda, u_errorName(status));
3898                 continue;
3899             }
3900             if (yr != mapPtr->gyr || mo != mapPtr->gmo || da != mapPtr->gda) {
3901                 errln("Fail: for Chinese %2d-%02d(%d)-%02d, Gregorian %4d-%02d-%02d, got %4d-%02d-%02d",
3902                         mapPtr->cyr, mapPtr->cmo, mapPtr->clp, mapPtr->cda, mapPtr->gyr, mapPtr->gmo, mapPtr->gda, yr, mo, da);
3903                 continue;
3904             }
3905         }
3906     }
3907 }
3908 
TestClearMonth()3909 void CalendarTest::TestClearMonth() {
3910     UErrorCode status = U_ZERO_ERROR;
3911     LocalPointer<Calendar> cal(Calendar::createInstance(Locale::getRoot(), status));
3912     if (failure(status, "construct Calendar")) return;
3913     cal->set(2023, UCAL_JUNE, 29);
3914     assertEquals("Calendar::get(UCAL_MONTH)", UCAL_JUNE, cal->get(UCAL_MONTH, status));
3915     if (failure(status, "Calendar::get(UCAL_MONTH)")) return;
3916     cal->clear(UCAL_MONTH);
3917     assertEquals("Calendar::isSet(UCAL_MONTH) after clear(UCAL_MONTH)", false, !!cal->isSet(UCAL_MONTH));
3918     assertEquals("Calendar::get(UCAL_MONTH after clear(UCAL_MONTH))", UCAL_JANUARY, !!cal->get(UCAL_MONTH, status));
3919     if (failure(status, "Calendar::get(UCAL_MONTH)")) return;
3920 
3921     cal->set(UCAL_ORDINAL_MONTH, 7);
3922     assertEquals("Calendar::get(UCAL_MONTH) after set(UCAL_ORDINAL_MONTH, 7)", UCAL_AUGUST, cal->get(UCAL_MONTH, status));
3923     if (failure(status, "Calendar::get(UCAL_MONTH) after set(UCAL_ORDINAL_MONTH, 7)")) return;
3924     assertEquals("Calendar::get(UCAL_ORDINAL_MONTH) after set(UCAL_ORDINAL_MONTH, 7)", 7, cal->get(UCAL_ORDINAL_MONTH, status));
3925     if (failure(status, "Calendar::get(UCAL_ORDINAL_MONTH) after set(UCAL_ORDINAL_MONTH, 7)")) return;
3926 
3927     cal->clear(UCAL_ORDINAL_MONTH);
3928     assertEquals("Calendar::isSet(UCAL_ORDINAL_MONTH) after clear(UCAL_ORDINAL_MONTH)", false, !!cal->isSet(UCAL_ORDINAL_MONTH));
3929     assertEquals("Calendar::get(UCAL_MONTH) after clear(UCAL_ORDINAL_MONTH)", UCAL_JANUARY, cal->get(UCAL_MONTH, status));
3930     if (failure(status, "Calendar::get(UCAL_MONTH) after clear(UCAL_ORDINAL_MONTH)")) return;
3931     assertEquals("Calendar::get(UCAL_ORDINAL_MONTH) after clear(UCAL_ORDINAL_MONTH)", 0, cal->get(UCAL_ORDINAL_MONTH, status));
3932     if (failure(status, "Calendar::get(UCAL_ORDINAL_MONTH) after clear(UCAL_ORDINAL_MONTH)")) return;
3933 
3934 }
3935 
TestGregorianCalendarInTemporalLeapYear()3936 void CalendarTest::TestGregorianCalendarInTemporalLeapYear() {
3937     // test from year 1800 to 2500
3938     UErrorCode status = U_ZERO_ERROR;
3939     GregorianCalendar gc(status);
3940     if (failure(status, "construct GregorianCalendar")) return;
3941     for (int32_t year = 1900; year < 2400; ++year) {
3942         gc.set(year, UCAL_MARCH, 7);
3943         assertEquals("Calendar::inTemporalLeapYear",
3944                  gc.isLeapYear(year) == true, gc.inTemporalLeapYear(status) == true);
3945         if (failure(status, "inTemporalLeapYear")) return;
3946     }
3947 }
3948 
RunChineseCalendarInTemporalLeapYearTest(Calendar * cal)3949 void CalendarTest::RunChineseCalendarInTemporalLeapYearTest(Calendar* cal) {
3950     UErrorCode status = U_ZERO_ERROR;
3951     GregorianCalendar gc(status);
3952     if (failure(status, "construct GregorianCalendar")) return;
3953     LocalPointer<Calendar> leapTest(cal->clone());
3954     // Start our test from 1900, Jan 1.
3955     // Check every 29 days in exhausted mode.
3956     int32_t incrementDays = 29;
3957     int32_t startYear = 1900;
3958     int32_t stopYear = 2400;
3959 
3960     if (quick) {
3961         incrementDays = 317;
3962         stopYear = 2100;
3963     }
3964     int32_t yearForHasLeapMonth = -1;
3965     bool hasLeapMonth = false;
3966     for (gc.set(startYear, UCAL_JANUARY, 1);
3967          gc.get(UCAL_YEAR, status) <= stopYear;
3968          gc.add(UCAL_DATE, incrementDays, status)) {
3969         cal->setTime(gc.getTime(status), status);
3970         if (failure(status, "add/get/set/getTime/setTime incorrect")) return;
3971 
3972         int32_t cal_year = cal->get(UCAL_EXTENDED_YEAR, status);
3973         if (yearForHasLeapMonth != cal_year) {
3974             leapTest->set(UCAL_EXTENDED_YEAR, cal_year);
3975             leapTest->set(UCAL_MONTH, 0);
3976             leapTest->set(UCAL_DATE, 1);
3977             // seek any leap month
3978             // check any leap month in the next 12 months.
3979             for (hasLeapMonth = false;
3980                  (!hasLeapMonth) && cal_year == leapTest->get(UCAL_EXTENDED_YEAR, status);
3981                  leapTest->add(UCAL_MONTH, 1, status)) {
3982                 hasLeapMonth = leapTest->get(UCAL_IS_LEAP_MONTH, status) != 0;
3983             }
3984             yearForHasLeapMonth = cal_year;
3985         }
3986         if (failure(status, "error while figure out expectation")) return;
3987 
3988         bool actualInLeap =  cal->inTemporalLeapYear(status);
3989         if (failure(status, "inTemporalLeapYear")) return;
3990         if (hasLeapMonth != actualInLeap) {
3991             logln("Gregorian y=%d m=%d d=%d => cal y=%d m=%s%d d=%d expected:%s actual:%s\n",
3992                    gc.get(UCAL_YEAR, status),
3993                    gc.get(UCAL_MONTH, status),
3994                    gc.get(UCAL_DATE, status),
3995                    cal->get(UCAL_EXTENDED_YEAR, status),
3996                    cal->get(UCAL_IS_LEAP_MONTH, status) == 1 ? "L" : "",
3997                    cal->get(UCAL_MONTH, status),
3998                    cal->get(UCAL_DAY_OF_MONTH, status),
3999                    hasLeapMonth ? "true" : "false",
4000                    actualInLeap ? "true" : "false");
4001         }
4002         assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
4003     }
4004 }
4005 
TestChineseCalendarInTemporalLeapYear()4006 void CalendarTest::TestChineseCalendarInTemporalLeapYear() {
4007     UErrorCode status = U_ZERO_ERROR;
4008     Locale l(Locale::getRoot());
4009     l.setKeywordValue("calendar", "chinese", status);
4010     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4011     if (failure(status, "construct ChineseCalendar")) return;
4012     RunChineseCalendarInTemporalLeapYearTest(cal.getAlias());
4013 }
4014 
TestDangiCalendarInTemporalLeapYear()4015 void CalendarTest::TestDangiCalendarInTemporalLeapYear() {
4016     UErrorCode status = U_ZERO_ERROR;
4017     Locale l(Locale::getRoot());
4018     l.setKeywordValue("calendar", "dangi", status);
4019     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4020     if (failure(status, "construct DangiCalendar")) return;
4021     RunChineseCalendarInTemporalLeapYearTest(cal.getAlias());
4022 }
4023 
TestHebrewCalendarInTemporalLeapYear()4024 void CalendarTest::TestHebrewCalendarInTemporalLeapYear() {
4025     UErrorCode status = U_ZERO_ERROR;
4026     GregorianCalendar gc(status);
4027     Locale l(Locale::getRoot());
4028     l.setKeywordValue("calendar", "hebrew", status);
4029     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4030     LocalPointer<Calendar> leapTest(Calendar::createInstance(l, status));
4031     if (failure(status, "construct HebrewCalendar")) return;
4032     // Start our test from 1900, Jan 1.
4033     // Check every 29 days in exhausted mode.
4034     int32_t incrementDays = 29;
4035     int32_t startYear = 1900;
4036     int32_t stopYear = 2400;
4037 
4038     if (quick) {
4039         incrementDays = 317;
4040         stopYear = 2100;
4041     }
4042     int32_t yearForHasLeapMonth = -1;
4043     bool hasLeapMonth = false;
4044     for (gc.set(startYear, UCAL_JANUARY, 1);
4045          gc.get(UCAL_YEAR, status) <= stopYear;
4046          gc.add(UCAL_DATE, incrementDays, status)) {
4047         cal->setTime(gc.getTime(status), status);
4048         if (failure(status, "add/get/set/getTime/setTime incorrect")) return;
4049 
4050         int32_t cal_year = cal->get(UCAL_EXTENDED_YEAR, status);
4051         if (yearForHasLeapMonth != cal_year) {
4052             leapTest->set(UCAL_EXTENDED_YEAR, cal_year);
4053             leapTest->set(UCAL_MONTH, 0);
4054             leapTest->set(UCAL_DATE, 1);
4055             // If 10 months after TISHRI is TAMUZ, then it is a leap year.
4056             leapTest->add(UCAL_MONTH, 10, status);
4057             hasLeapMonth = leapTest->get(UCAL_MONTH, status) == icu::HebrewCalendar::TAMUZ;
4058             yearForHasLeapMonth = cal_year;
4059         }
4060         bool actualInLeap =  cal->inTemporalLeapYear(status);
4061         if (failure(status, "inTemporalLeapYear")) return;
4062         if (hasLeapMonth != actualInLeap) {
4063             logln("Gregorian y=%d m=%d d=7 => cal y=%d m=%d d=%d expected:%s actual:%s\n",
4064                    gc.get(UCAL_YEAR, status),
4065                    gc.get(UCAL_MONTH, status),
4066                    cal->get(UCAL_EXTENDED_YEAR, status),
4067                    cal->get(UCAL_MONTH, status),
4068                    cal->get(UCAL_DAY_OF_MONTH, status),
4069                    hasLeapMonth ? "true" : "false",
4070                    actualInLeap ? "true" : "false");
4071         }
4072         assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
4073     }
4074 }
4075 
RunIslamicCalendarInTemporalLeapYearTest(Calendar * cal)4076 void CalendarTest::RunIslamicCalendarInTemporalLeapYearTest(Calendar* cal) {
4077     UErrorCode status = U_ZERO_ERROR;
4078     GregorianCalendar gc(status);
4079     if (failure(status, "construct GregorianCalendar")) return;
4080     // Start our test from 1900, Jan 1.
4081     // Check every 29 days in exhausted mode.
4082     int32_t incrementDays = 29;
4083     int32_t startYear = 1900;
4084     int32_t stopYear = 2400;
4085 
4086     if (quick) {
4087         incrementDays = 317;
4088         stopYear = 2100;
4089     }
4090     int32_t yearForHasLeapMonth = -1;
4091     bool hasLeapMonth = false;
4092     for (gc.set(startYear, UCAL_JANUARY, 1);
4093          gc.get(UCAL_YEAR, status) <= stopYear;
4094          gc.add(UCAL_DATE, incrementDays, status)) {
4095         cal->setTime(gc.getTime(status), status);
4096         if (failure(status, "set/getTime/setTime incorrect")) return;
4097         int32_t cal_year = cal->get(UCAL_EXTENDED_YEAR, status);
4098         if (yearForHasLeapMonth != cal_year) {
4099             // If that year has exactly 355 days, it is a leap year.
4100             hasLeapMonth = cal->getActualMaximum(UCAL_DAY_OF_YEAR, status) == 355;
4101             yearForHasLeapMonth = cal_year;
4102         }
4103 
4104         bool actualInLeap =  cal->inTemporalLeapYear(status);
4105         if (failure(status, "inTemporalLeapYear")) return;
4106         if (hasLeapMonth != actualInLeap) {
4107             logln("Gregorian y=%d m=%d d=%d => cal y=%d m=%s%d d=%d expected:%s actual:%s\n",
4108                    gc.get(UCAL_EXTENDED_YEAR, status),
4109                    gc.get(UCAL_MONTH, status),
4110                    gc.get(UCAL_DAY_OF_MONTH, status),
4111                    cal->get(UCAL_EXTENDED_YEAR, status),
4112                    cal->get(UCAL_IS_LEAP_MONTH, status) == 1 ? "L" : "",
4113                    cal->get(UCAL_MONTH, status),
4114                    cal->get(UCAL_DAY_OF_MONTH, status),
4115                    hasLeapMonth ? "true" : "false",
4116                    actualInLeap ? "true" : "false");
4117         }
4118         assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
4119     }
4120 }
4121 
TestIslamicCalendarInTemporalLeapYear()4122 void CalendarTest::TestIslamicCalendarInTemporalLeapYear() {
4123     UErrorCode status = U_ZERO_ERROR;
4124     Locale l(Locale::getRoot());
4125     l.setKeywordValue("calendar", "islamic", status);
4126     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4127     if (failure(status, "construct IslamicCalendar")) return;
4128     RunIslamicCalendarInTemporalLeapYearTest(cal.getAlias());
4129 }
4130 
TestIslamicCivilCalendarInTemporalLeapYear()4131 void CalendarTest::TestIslamicCivilCalendarInTemporalLeapYear() {
4132     UErrorCode status = U_ZERO_ERROR;
4133     Locale l(Locale::getRoot());
4134     l.setKeywordValue("calendar", "islamic-civil", status);
4135     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4136     if (failure(status, "construct IslamicCivilCalendar")) return;
4137     RunIslamicCalendarInTemporalLeapYearTest(cal.getAlias());
4138 }
4139 
TestIslamicUmalquraCalendarInTemporalLeapYear()4140 void CalendarTest::TestIslamicUmalquraCalendarInTemporalLeapYear() {
4141     UErrorCode status = U_ZERO_ERROR;
4142     Locale l(Locale::getRoot());
4143     l.setKeywordValue("calendar", "islamic-umalqura", status);
4144     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4145     if (failure(status, "construct IslamicUmalquraCalendar")) return;
4146     RunIslamicCalendarInTemporalLeapYearTest(cal.getAlias());
4147 }
4148 
TestIslamicRGSACalendarInTemporalLeapYear()4149 void CalendarTest::TestIslamicRGSACalendarInTemporalLeapYear() {
4150     UErrorCode status = U_ZERO_ERROR;
4151     Locale l(Locale::getRoot());
4152     l.setKeywordValue("calendar", "islamic-rgsa", status);
4153     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4154     if (failure(status, "construct IslamicRGSACalendar")) return;
4155     RunIslamicCalendarInTemporalLeapYearTest(cal.getAlias());
4156 }
4157 
TestIslamicTBLACalendarInTemporalLeapYear()4158 void CalendarTest::TestIslamicTBLACalendarInTemporalLeapYear() {
4159     UErrorCode status = U_ZERO_ERROR;
4160     Locale l(Locale::getRoot());
4161     l.setKeywordValue("calendar", "islamic-tbla", status);
4162     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4163     if (failure(status, "construct IslamicTBLACalendar")) return;
4164     RunIslamicCalendarInTemporalLeapYearTest(cal.getAlias());
4165 }
4166 
Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(Calendar * cal)4167 void CalendarTest::Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(Calendar* cal) {
4168     UErrorCode status = U_ZERO_ERROR;
4169     GregorianCalendar gc(status);
4170     if (failure(status, "construct GregorianCalendar")) return;
4171     // Start our test from 1900, Jan 1.
4172     // Check every 29 days in exhausted mode.
4173     int32_t incrementDays = 29;
4174     int32_t startYear = 1900;
4175     int32_t stopYear = 2400;
4176 
4177     if (quick) {
4178         incrementDays = 317;
4179         stopYear = 2100;
4180     }
4181     int32_t yearForHasLeapMonth = -1;
4182     bool hasLeapMonth = false;
4183     for (gc.set(startYear, UCAL_JANUARY, 1);
4184          gc.get(UCAL_YEAR, status) <= stopYear;
4185          gc.add(UCAL_DATE, incrementDays, status)) {
4186         cal->setTime(gc.getTime(status), status);
4187         if (failure(status, "set/getTime/setTime incorrect")) return;
4188         int32_t cal_year = cal->get(UCAL_EXTENDED_YEAR, status);
4189         if (yearForHasLeapMonth != cal_year) {
4190             // If that year has exactly 355 days, it is a leap year.
4191             hasLeapMonth = cal->getActualMaximum(UCAL_DAY_OF_YEAR, status) == 366;
4192             if (failure(status, "getActualMaximum incorrect")) return;
4193             yearForHasLeapMonth = cal_year;
4194         }
4195         bool actualInLeap =  cal->inTemporalLeapYear(status);
4196         if (failure(status, "inTemporalLeapYear")) return;
4197         if (hasLeapMonth != actualInLeap) {
4198             logln("Gregorian y=%d m=%d d=%d => cal y=%d m=%s%d d=%d expected:%s actual:%s\n",
4199                    gc.get(UCAL_EXTENDED_YEAR, status),
4200                    gc.get(UCAL_MONTH, status),
4201                    gc.get(UCAL_DAY_OF_MONTH, status),
4202                    cal->get(UCAL_EXTENDED_YEAR, status),
4203                    cal->get(UCAL_IS_LEAP_MONTH, status) == 1 ? "L" : "",
4204                    cal->get(UCAL_MONTH, status),
4205                    cal->get(UCAL_DAY_OF_MONTH, status),
4206                    hasLeapMonth ? "true" : "false",
4207                    actualInLeap ? "true" : "false");
4208         }
4209         assertEquals("inTemporalLeapYear", hasLeapMonth, actualInLeap);
4210     }
4211 }
4212 
TestTaiwanCalendarInTemporalLeapYear()4213 void CalendarTest::TestTaiwanCalendarInTemporalLeapYear() {
4214     UErrorCode status = U_ZERO_ERROR;
4215     Locale l(Locale::getRoot());
4216     l.setKeywordValue("calendar", "roc", status);
4217     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4218     if (failure(status, "construct TaiwanCalendar")) return;
4219     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4220 }
4221 
TestJapaneseCalendarInTemporalLeapYear()4222 void CalendarTest::TestJapaneseCalendarInTemporalLeapYear() {
4223     UErrorCode status = U_ZERO_ERROR;
4224     Locale l(Locale::getRoot());
4225     l.setKeywordValue("calendar", "japanese", status);
4226     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4227     if (failure(status, "construct JapaneseCalendar")) return;
4228     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4229 }
4230 
TestBuddhistCalendarInTemporalLeapYear()4231 void CalendarTest::TestBuddhistCalendarInTemporalLeapYear() {
4232     UErrorCode status = U_ZERO_ERROR;
4233     Locale l(Locale::getRoot());
4234     l.setKeywordValue("calendar", "buddhist", status);
4235     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4236     if (failure(status, "construct BuddhistCalendar")) return;
4237     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4238 }
4239 
TestPersianCalendarInTemporalLeapYear()4240 void CalendarTest::TestPersianCalendarInTemporalLeapYear() {
4241     UErrorCode status = U_ZERO_ERROR;
4242     Locale l(Locale::getRoot());
4243     l.setKeywordValue("calendar", "persian", status);
4244     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4245     if (failure(status, "construct PersianCalendar")) return;
4246     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4247 }
4248 
TestIndianCalendarInTemporalLeapYear()4249 void CalendarTest::TestIndianCalendarInTemporalLeapYear() {
4250     UErrorCode status = U_ZERO_ERROR;
4251     Locale l(Locale::getRoot());
4252     l.setKeywordValue("calendar", "indian", status);
4253     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4254     if (failure(status, "construct IndianCalendar")) return;
4255     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4256 }
4257 
TestCopticCalendarInTemporalLeapYear()4258 void CalendarTest::TestCopticCalendarInTemporalLeapYear() {
4259     UErrorCode status = U_ZERO_ERROR;
4260     Locale l(Locale::getRoot());
4261     l.setKeywordValue("calendar", "coptic", status);
4262     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4263     if (failure(status, "construct CopticCalendar")) return;
4264     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4265 }
4266 
TestEthiopicCalendarInTemporalLeapYear()4267 void CalendarTest::TestEthiopicCalendarInTemporalLeapYear() {
4268     UErrorCode status = U_ZERO_ERROR;
4269     Locale l(Locale::getRoot());
4270     l.setKeywordValue("calendar", "ethiopic", status);
4271     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4272     if (failure(status, "construct EthiopicCalendar")) return;
4273     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4274 }
4275 
TestEthiopicAmeteAlemCalendarInTemporalLeapYear()4276 void CalendarTest::TestEthiopicAmeteAlemCalendarInTemporalLeapYear() {
4277     UErrorCode status = U_ZERO_ERROR;
4278     Locale l(Locale::getRoot());
4279     l.setKeywordValue("calendar", "ethiopic-amete-alem", status);
4280     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4281     if (failure(status, "construct EthiopicAmeteAlemCalendar")) return;
4282     Run366DaysIsLeapYearCalendarInTemporalLeapYearTest(cal.getAlias());
4283 }
4284 
monthCode(int32_t month,bool leap)4285 std::string monthCode(int32_t month, bool leap) {
4286     std::string code("M");
4287     if (month < 10) {
4288         code += "0";
4289         code += ('0' + month);
4290     } else {
4291         code += "1";
4292         code += ('0' + (month % 10));
4293     }
4294     if (leap) {
4295         code += "L";
4296     }
4297     return code;
4298 }
hebrewMonthCode(int32_t icuMonth)4299 std::string hebrewMonthCode(int32_t icuMonth) {
4300     if (icuMonth == icu::HebrewCalendar::ADAR_1) {
4301         return monthCode(icuMonth, true);
4302     }
4303     return monthCode(icuMonth < icu::HebrewCalendar::ADAR_1 ? icuMonth+1 : icuMonth, false);
4304 }
4305 
RunChineseCalendarGetTemporalMonthCode(Calendar * cal)4306 void CalendarTest::RunChineseCalendarGetTemporalMonthCode(Calendar* cal) {
4307     UErrorCode status = U_ZERO_ERROR;
4308     GregorianCalendar gc(status);
4309     if (failure(status, "construct GregorianCalendar")) return;
4310     // Start our test from 1900, Jan 1.
4311     // Check every 29 days in exhausted mode.
4312     int32_t incrementDays = 29;
4313     int32_t startYear = 1900;
4314     int32_t stopYear = 2400;
4315 
4316     if (quick) {
4317         incrementDays = 317;
4318         startYear = 1950;
4319         stopYear = 2050;
4320     }
4321     for (gc.set(startYear, UCAL_JANUARY, 1);
4322          gc.get(UCAL_YEAR, status) <= stopYear;
4323          gc.add(UCAL_DATE, incrementDays, status)) {
4324         cal->setTime(gc.getTime(status), status);
4325         if (failure(status, "set/getTime/setTime incorrect")) return;
4326         int32_t cal_month = cal->get(UCAL_MONTH, status);
4327         std::string expected = monthCode(
4328             cal_month + 1, cal->get(UCAL_IS_LEAP_MONTH, status) != 0);
4329         assertEquals("getTemporalMonthCode", expected.c_str(), cal->getTemporalMonthCode(status));
4330         if (failure(status, "getTemporalMonthCode")) return;
4331     }
4332 }
4333 
TestChineseCalendarGetTemporalMonthCode()4334 void CalendarTest::TestChineseCalendarGetTemporalMonthCode() {
4335     UErrorCode status = U_ZERO_ERROR;
4336     Locale l(Locale::getRoot());
4337     l.setKeywordValue("calendar", "chinese", status);
4338     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4339     if (failure(status, "construct ChineseCalendar")) return;
4340     RunChineseCalendarGetTemporalMonthCode(cal.getAlias());
4341 }
4342 
TestDangiCalendarGetTemporalMonthCode()4343 void CalendarTest::TestDangiCalendarGetTemporalMonthCode() {
4344     UErrorCode status = U_ZERO_ERROR;
4345     Locale l(Locale::getRoot());
4346     l.setKeywordValue("calendar", "dangi", status);
4347     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4348     if (failure(status, "construct DangiCalendar")) return;
4349     RunChineseCalendarGetTemporalMonthCode(cal.getAlias());
4350 }
4351 
TestHebrewCalendarGetTemporalMonthCode()4352 void CalendarTest::TestHebrewCalendarGetTemporalMonthCode() {
4353     UErrorCode status = U_ZERO_ERROR;
4354     Locale l(Locale::getRoot());
4355     l.setKeywordValue("calendar", "hebrew", status);
4356     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4357     GregorianCalendar gc(status);
4358     if (failure(status, "construct Calendar")) return;
4359     // Start our test from 1900, Jan 1.
4360     // Check every 29 days in exhausted mode.
4361     int32_t incrementDays = 29;
4362     int32_t startYear = 1900;
4363     int32_t stopYear = 2400;
4364 
4365     if (quick) {
4366         incrementDays = 317;
4367         stopYear = 2100;
4368     }
4369     for (gc.set(startYear, UCAL_JANUARY, 1);
4370          gc.get(UCAL_YEAR, status) <= stopYear;
4371          gc.add(UCAL_DATE, incrementDays, status)) {
4372         cal->setTime(gc.getTime(status), status);
4373         if (failure(status, "set/getTime/setTime incorrect")) return;
4374         std::string expected = hebrewMonthCode(cal->get(UCAL_MONTH, status));
4375         if (failure(status, "get failed")) return;
4376         assertEquals("getTemporalMonthCode", expected.c_str(), cal->getTemporalMonthCode(status));
4377         if (failure(status, "getTemporalMonthCode")) return;
4378     }
4379 }
4380 
RunCECalendarGetTemporalMonthCode(Calendar * cal)4381 void CalendarTest::RunCECalendarGetTemporalMonthCode(Calendar* cal) {
4382     UErrorCode status = U_ZERO_ERROR;
4383     GregorianCalendar gc(status);
4384     if (failure(status, "construct Calendar")) return;
4385     // Start testing from 1900
4386     gc.set(1900, UCAL_JANUARY, 1);
4387     cal->setTime(gc.getTime(status), status);
4388     int32_t year = cal->get(UCAL_YEAR, status);
4389     for (int32_t m = 0; m < 13; m++) {
4390         std::string expected = monthCode(m+1, false);
4391         for (int32_t y = year; y < year + 500 ; y++) {
4392             cal->set(y, m, 1);
4393             assertEquals("getTemporalMonthCode", expected.c_str(), cal->getTemporalMonthCode(status));
4394             if (failure(status, "getTemporalMonthCode")) continue;
4395         }
4396     }
4397 
4398 }
4399 
TestCopticCalendarGetTemporalMonthCode()4400 void CalendarTest::TestCopticCalendarGetTemporalMonthCode() {
4401     UErrorCode status = U_ZERO_ERROR;
4402     Locale l(Locale::getRoot());
4403     l.setKeywordValue("calendar", "coptic", status);
4404     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4405     if (failure(status, "construct CopticCalendar")) return;
4406     RunCECalendarGetTemporalMonthCode(cal.getAlias());
4407 }
4408 
TestEthiopicCalendarGetTemporalMonthCode()4409 void CalendarTest::TestEthiopicCalendarGetTemporalMonthCode() {
4410     UErrorCode status = U_ZERO_ERROR;
4411     Locale l(Locale::getRoot());
4412     l.setKeywordValue("calendar", "ethiopic", status);
4413     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4414     if (failure(status, "construct EthiopicCalendar")) return;
4415     RunCECalendarGetTemporalMonthCode(cal.getAlias());
4416 }
4417 
TestEthiopicAmeteAlemCalendarGetTemporalMonthCode()4418 void CalendarTest::TestEthiopicAmeteAlemCalendarGetTemporalMonthCode() {
4419     UErrorCode status = U_ZERO_ERROR;
4420     Locale l(Locale::getRoot());
4421     l.setKeywordValue("calendar", "ethiopic-amete-alem", status);
4422     LocalPointer<Calendar> cal(Calendar::createInstance(l, status));
4423     if (failure(status, "construct EthiopicAmeteAlemCalendar")) return;
4424     RunCECalendarGetTemporalMonthCode(cal.getAlias());
4425 }
4426 
TestGregorianCalendarSetTemporalMonthCode()4427 void CalendarTest::TestGregorianCalendarSetTemporalMonthCode() {
4428 
4429     struct TestCase {
4430       int32_t gYear;
4431       int32_t gMonth;
4432       int32_t gDate;
4433       const char* monthCode;
4434       int32_t ordinalMonth;
4435     } cases[] = {
4436       { 1911, UCAL_JANUARY, 31, "M01", 0 },
4437       { 1970, UCAL_FEBRUARY, 22, "M02", 1 },
4438       { 543, UCAL_MARCH, 3, "M03", 2 },
4439       { 2340, UCAL_APRIL, 21, "M04", 3 },
4440       { 1234, UCAL_MAY, 21, "M05", 4 },
4441       { 1931, UCAL_JUNE, 17, "M06", 5 },
4442       { 2000, UCAL_JULY, 1, "M07", 6 },
4443       { 2033, UCAL_AUGUST, 3, "M08", 7 },
4444       { 2013, UCAL_SEPTEMBER, 9, "M09", 8 },
4445       { 1849, UCAL_OCTOBER, 31, "M10", 9 },
4446       { 1433, UCAL_NOVEMBER, 30, "M11", 10 },
4447       { 2022, UCAL_DECEMBER, 25, "M12", 11 },
4448     };
4449     UErrorCode status = U_ZERO_ERROR;
4450     GregorianCalendar gc1(status);
4451     GregorianCalendar gc2(status);
4452     if (failure(status, "construct GregorianCalendar")) return;
4453     for (auto& cas : cases) {
4454         gc1.clear();
4455         gc2.clear();
4456         gc1.set(cas.gYear, cas.gMonth, cas.gDate);
4457 
4458         gc2.set(UCAL_YEAR, cas.gYear);
4459         gc2.setTemporalMonthCode(cas.monthCode, status);
4460         gc2.set(UCAL_DATE, cas.gDate);
4461         if (failure(status, "set/setTemporalMonthCode")) return;
4462 
4463         assertTrue("by set and setTemporalMonthCode()", gc1.equals(gc2, status));
4464         const char* actualMonthCode1 = gc1.getTemporalMonthCode(status);
4465         const char* actualMonthCode2 = gc2.getTemporalMonthCode(status);
4466         if (failure(status, "getTemporalMonthCode")) continue;
4467         assertEquals("getTemporalMonthCode()", 0,
4468                      uprv_strcmp(actualMonthCode1, actualMonthCode2));
4469         assertEquals("getTemporalMonthCode()", 0,
4470                      uprv_strcmp(cas.monthCode, actualMonthCode2));
4471         assertEquals("ordinalMonth", cas.ordinalMonth, gc2.get(UCAL_ORDINAL_MONTH, status));
4472         assertEquals("ordinalMonth", gc1.get(UCAL_ORDINAL_MONTH, status),
4473                      gc2.get(UCAL_ORDINAL_MONTH, status));
4474     }
4475 }
4476 
TestChineseCalendarSetTemporalMonthCode()4477 void CalendarTest::TestChineseCalendarSetTemporalMonthCode() {
4478     UErrorCode status = U_ZERO_ERROR;
4479     Locale l(Locale::getRoot());
4480     l.setKeywordValue("calendar", "chinese", status);
4481     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
4482     if (failure(status, "construct ChineseCalendar")) return;
4483     LocalPointer<Calendar> cc2(cc1->clone());
4484 
4485     struct TestCase {
4486       int32_t gYear;
4487       int32_t gMonth;
4488       int32_t gDate;
4489       int32_t cYear;
4490       int32_t cMonth;
4491       int32_t cDate;
4492       const char* cMonthCode;
4493       bool cLeapMonth;
4494       int32_t cOrdinalMonth;
4495     } cases[] = {
4496       // https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2022.pdf
4497       { 2022, UCAL_DECEMBER, 15, 4659, UCAL_NOVEMBER, 22, "M11", false, 10},
4498       // M01L is very hard to find. Cannot find a year has M01L in these several
4499       // centuries.
4500       // M02L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2004.pdf
4501       { 2004, UCAL_MARCH, 20, 4641, UCAL_FEBRUARY, 30, "M02", false, 1},
4502       { 2004, UCAL_MARCH, 21, 4641, UCAL_FEBRUARY, 1, "M02L", true, 2},
4503       { 2004, UCAL_APRIL, 18, 4641, UCAL_FEBRUARY, 29, "M02L", true, 2},
4504       { 2004, UCAL_APRIL, 19, 4641, UCAL_MARCH, 1, "M03", false, 3},
4505       // M03L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1995.pdf
4506       { 1955, UCAL_APRIL, 21, 4592, UCAL_MARCH, 29, "M03", false, 2},
4507       { 1955, UCAL_APRIL, 22, 4592, UCAL_MARCH, 1, "M03L", true, 3},
4508       { 1955, UCAL_MAY, 21, 4592, UCAL_MARCH, 30, "M03L", true, 3},
4509       { 1955, UCAL_MAY, 22, 4592, UCAL_APRIL, 1, "M04", false, 4},
4510       // M12 https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1996.pdf
4511       { 1956, UCAL_FEBRUARY, 11, 4592, UCAL_DECEMBER, 30, "M12", false, 12},
4512       // M04L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2001.pdf
4513       { 2001, UCAL_MAY, 22, 4638, UCAL_APRIL, 30, "M04", false, 3},
4514       { 2001, UCAL_MAY, 23, 4638, UCAL_APRIL, 1, "M04L", true, 4},
4515       { 2001, UCAL_JUNE, 20, 4638, UCAL_APRIL, 29, "M04L", true, 4},
4516       { 2001, UCAL_JUNE, 21, 4638, UCAL_MAY, 1, "M05", false, 5},
4517       // M05L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2009.pdf
4518       { 2009, UCAL_JUNE, 22, 4646, UCAL_MAY, 30, "M05", false, 4},
4519       { 2009, UCAL_JUNE, 23, 4646, UCAL_MAY, 1, "M05L", true, 5},
4520       { 2009, UCAL_JULY, 21, 4646, UCAL_MAY, 29, "M05L", true, 5},
4521       { 2009, UCAL_JULY, 22, 4646, UCAL_JUNE, 1, "M06", false, 6},
4522       // M06L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2017.pdf
4523       { 2017, UCAL_JULY, 22, 4654, UCAL_JUNE, 29, "M06", false, 5},
4524       { 2017, UCAL_JULY, 23, 4654, UCAL_JUNE, 1, "M06L", true, 6},
4525       { 2017, UCAL_AUGUST, 21, 4654, UCAL_JUNE, 30, "M06L", true, 6},
4526       { 2017, UCAL_AUGUST, 22, 4654, UCAL_JULY, 1, "M07", false, 7},
4527       // M07L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2006.pdf
4528       { 2006, UCAL_AUGUST, 23, 4643, UCAL_JULY, 30, "M07", false, 6},
4529       { 2006, UCAL_AUGUST, 24, 4643, UCAL_JULY, 1, "M07L", true, 7},
4530       { 2006, UCAL_SEPTEMBER, 21, 4643, UCAL_JULY, 29, "M07L", true, 7},
4531       { 2006, UCAL_SEPTEMBER, 22, 4643, UCAL_AUGUST, 1, "M08", false, 8},
4532       // M08L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1995.pdf
4533       { 1995, UCAL_SEPTEMBER, 24, 4632, UCAL_AUGUST, 30, "M08", false, 7},
4534       { 1995, UCAL_SEPTEMBER, 25, 4632, UCAL_AUGUST, 1, "M08L", true, 8},
4535       { 1995, UCAL_OCTOBER, 23, 4632, UCAL_AUGUST, 29, "M08L", true, 8},
4536       { 1995, UCAL_OCTOBER, 24, 4632, UCAL_SEPTEMBER, 1, "M09", false, 9},
4537       // M09L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2014.pdf
4538       { 2014, UCAL_OCTOBER, 23, 4651, UCAL_SEPTEMBER, 30, "M09", false, 8},
4539       { 2014, UCAL_OCTOBER, 24, 4651, UCAL_SEPTEMBER, 1, "M09L", true, 9},
4540       { 2014, UCAL_NOVEMBER, 21, 4651, UCAL_SEPTEMBER, 29, "M09L", true, 9},
4541       { 2014, UCAL_NOVEMBER, 22, 4651, UCAL_OCTOBER, 1, "M10", false, 10},
4542       // M10L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/1984.pdf
4543       { 1984, UCAL_NOVEMBER, 22, 4621, UCAL_OCTOBER, 30, "M10", false, 9},
4544       { 1984, UCAL_NOVEMBER, 23, 4621, UCAL_OCTOBER, 1, "M10L", true, 10},
4545       { 1984, UCAL_DECEMBER, 21, 4621, UCAL_OCTOBER, 29, "M10L", true, 10},
4546       { 1984, UCAL_DECEMBER, 22, 4621, UCAL_NOVEMBER, 1, "M11", false, 11},
4547       // M11L https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2033.pdf
4548       //      https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2034.pdf
4549       { 2033, UCAL_DECEMBER, 21, 4670, UCAL_NOVEMBER, 30, "M11", false, 10},
4550       { 2033, UCAL_DECEMBER, 22, 4670, UCAL_NOVEMBER, 1, "M11L", true, 11},
4551       { 2034, UCAL_JANUARY, 19, 4670, UCAL_NOVEMBER, 29, "M11L", true, 11},
4552       { 2034, UCAL_JANUARY, 20, 4670, UCAL_DECEMBER, 1, "M12", false, 12},
4553       // M12L is very hard to find. Cannot find a year has M01L in these several
4554       // centuries.
4555     };
4556     GregorianCalendar gc1(status);
4557     if (failure(status, "construct Calendar")) return;
4558     for (auto& cas : cases) {
4559         gc1.clear();
4560         cc1->clear();
4561         cc2->clear();
4562         gc1.set(cas.gYear, cas.gMonth, cas.gDate);
4563         cc1->setTime(gc1.getTime(status), status);
4564 
4565         cc2->set(UCAL_EXTENDED_YEAR, cas.cYear);
4566         cc2->setTemporalMonthCode(cas.cMonthCode, status);
4567         cc2->set(UCAL_DATE, cas.cDate);
4568 
4569         assertEquals("year", cas.cYear, cc1->get(UCAL_EXTENDED_YEAR, status));
4570         assertEquals("month", cas.cMonth, cc1->get(UCAL_MONTH, status));
4571         assertEquals("date", cas.cDate, cc1->get(UCAL_DATE, status));
4572         assertEquals("is_leap_month", cas.cLeapMonth ? 1 : 0, cc1->get(UCAL_IS_LEAP_MONTH, status));
4573         assertEquals("getTemporalMonthCode()", 0,
4574                      uprv_strcmp(cas.cMonthCode, cc1->getTemporalMonthCode(status)));
4575         if (failure(status, "getTemporalMonthCode")) continue;
4576         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc1->get(UCAL_ORDINAL_MONTH, status));
4577         if (! cc2->equals(*cc1, status)) {
4578             printf("g=%f %f vs %f. diff = %f %d/%d%s/%d vs %d/%d%s/%d\n",
4579                    gc1.getTime(status), cc1->getTime(status), cc2->getTime(status),
4580                    cc1->getTime(status) - cc2->getTime(status),
4581                    cc1->get(UCAL_EXTENDED_YEAR, status),
4582                    cc1->get(UCAL_MONTH, status)+1,
4583                    cc1->get(UCAL_IS_LEAP_MONTH, status) == 0 ? "" : "L",
4584                    cc1->get(UCAL_DATE, status),
4585                    cc2->get(UCAL_EXTENDED_YEAR, status),
4586                    cc2->get(UCAL_MONTH, status)+1,
4587                    cc2->get(UCAL_IS_LEAP_MONTH, status) == 0 ? "" : "L",
4588                    cc2->get(UCAL_DATE, status));
4589         }
4590         assertTrue("by set() and setTemporalMonthCode()", cc2->equals(*cc1, status));
4591     }
4592 }
4593 
TestHebrewCalendarSetTemporalMonthCode()4594 void CalendarTest::TestHebrewCalendarSetTemporalMonthCode() {
4595     UErrorCode status = U_ZERO_ERROR;
4596     Locale l(Locale::getRoot());
4597     l.setKeywordValue("calendar", "hebrew", status);
4598     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
4599     if (failure(status, "construct HebrewCalendar")) return;
4600     LocalPointer<Calendar> cc2(cc1->clone());
4601 
4602     struct TestCase {
4603       int32_t gYear;
4604       int32_t gMonth;
4605       int32_t gDate;
4606       int32_t cYear;
4607       int32_t cMonth;
4608       int32_t cDate;
4609       const char* cMonthCode;
4610       int32_t cOrdinalMonth;
4611     } cases[] = {
4612       { 2022, UCAL_JANUARY, 11, 5782, icu::HebrewCalendar::SHEVAT, 9, "M05", 4},
4613       { 2022, UCAL_FEBRUARY, 12, 5782, icu::HebrewCalendar::ADAR_1, 11, "M05L", 5},
4614       { 2022, UCAL_MARCH, 13, 5782, icu::HebrewCalendar::ADAR, 10, "M06", 6},
4615       { 2022, UCAL_APRIL, 14, 5782, icu::HebrewCalendar::NISAN, 13, "M07", 7},
4616       { 2022, UCAL_MAY, 15, 5782, icu::HebrewCalendar::IYAR, 14, "M08", 8},
4617       { 2022, UCAL_JUNE, 16, 5782, icu::HebrewCalendar::SIVAN, 17, "M09", 9},
4618       { 2022, UCAL_JULY, 17, 5782, icu::HebrewCalendar::TAMUZ, 18, "M10", 10},
4619       { 2022, UCAL_AUGUST, 18, 5782, icu::HebrewCalendar::AV, 21, "M11", 11},
4620       { 2022, UCAL_SEPTEMBER, 19, 5782, icu::HebrewCalendar::ELUL, 23, "M12", 12},
4621       { 2022, UCAL_OCTOBER, 20, 5783, icu::HebrewCalendar::TISHRI, 25, "M01", 0},
4622       { 2022, UCAL_NOVEMBER, 21, 5783, icu::HebrewCalendar::HESHVAN, 27, "M02", 1},
4623       { 2022, UCAL_DECEMBER, 22, 5783, icu::HebrewCalendar::KISLEV, 28, "M03", 2},
4624       { 2023, UCAL_JANUARY, 20, 5783, icu::HebrewCalendar::TEVET, 27, "M04", 3},
4625     };
4626     GregorianCalendar gc1(status);
4627     if (failure(status, "construct Calendar")) return;
4628     for (auto& cas : cases) {
4629         gc1.clear();
4630         cc1->clear();
4631         cc2->clear();
4632         gc1.set(cas.gYear, cas.gMonth, cas.gDate);
4633         cc1->setTime(gc1.getTime(status), status);
4634 
4635         cc2->set(UCAL_EXTENDED_YEAR, cas.cYear);
4636         cc2->setTemporalMonthCode(cas.cMonthCode, status);
4637         cc2->set(UCAL_DATE, cas.cDate);
4638 
4639         assertEquals("year", cas.cYear, cc1->get(UCAL_EXTENDED_YEAR, status));
4640         assertEquals("month", cas.cMonth, cc1->get(UCAL_MONTH, status));
4641         assertEquals("date", cas.cDate, cc1->get(UCAL_DATE, status));
4642         assertEquals("getTemporalMonthCode()", 0,
4643                      uprv_strcmp(cas.cMonthCode, cc1->getTemporalMonthCode(status)));
4644         if (failure(status, "getTemporalMonthCode")) continue;
4645         if (! cc2->equals(*cc1, status)) {
4646             printf("g=%f %f vs %f. diff = %f %d/%d/%d vs %d/%d/%d\n",
4647                    gc1.getTime(status), cc1->getTime(status), cc2->getTime(status),
4648                    cc1->getTime(status) - cc2->getTime(status),
4649                    cc1->get(UCAL_EXTENDED_YEAR, status),
4650                    cc1->get(UCAL_MONTH, status)+1,
4651                    cc1->get(UCAL_DATE, status),
4652                    cc2->get(UCAL_EXTENDED_YEAR, status),
4653                    cc2->get(UCAL_MONTH, status)+1,
4654                    cc2->get(UCAL_DATE, status));
4655         }
4656         assertTrue("by set() and setTemporalMonthCode()", cc2->equals(*cc1, status));
4657         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc1->get(UCAL_ORDINAL_MONTH, status));
4658         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc2->get(UCAL_ORDINAL_MONTH, status));
4659     }
4660 }
4661 
TestCopticCalendarSetTemporalMonthCode()4662 void CalendarTest::TestCopticCalendarSetTemporalMonthCode() {
4663     UErrorCode status = U_ZERO_ERROR;
4664     Locale l(Locale::getRoot());
4665     l.setKeywordValue("calendar", "coptic", status);
4666     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
4667     if (failure(status, "construct CopticCalendar")) return;
4668     LocalPointer<Calendar> cc2(cc1->clone());
4669 
4670     struct TestCase {
4671       int32_t gYear;
4672       int32_t gMonth;
4673       int32_t gDate;
4674       int32_t cYear;
4675       int32_t cMonth;
4676       int32_t cDate;
4677       const char* cMonthCode;
4678       int32_t cOrdinalMonth;
4679     } cases[] = {
4680       { 1900, UCAL_JANUARY, 1, 1616, icu::CopticCalendar::KIAHK, 23, "M04", 3},
4681       { 1900, UCAL_SEPTEMBER, 6, 1616, icu::CopticCalendar::NASIE, 1, "M13", 12},
4682       { 1900, UCAL_SEPTEMBER, 10, 1616, icu::CopticCalendar::NASIE, 5, "M13", 12},
4683       { 1900, UCAL_SEPTEMBER, 11, 1617, icu::CopticCalendar::TOUT, 1, "M01", 0},
4684 
4685       { 2022, UCAL_JANUARY, 11, 1738, icu::CopticCalendar::TOBA, 3, "M05", 4},
4686       { 2022, UCAL_FEBRUARY, 12, 1738, icu::CopticCalendar::AMSHIR, 5, "M06", 5},
4687       { 2022, UCAL_MARCH, 13, 1738, icu::CopticCalendar::BARAMHAT, 4, "M07", 6},
4688       { 2022, UCAL_APRIL, 14, 1738, icu::CopticCalendar::BARAMOUDA, 6, "M08", 7},
4689       { 2022, UCAL_MAY, 15, 1738, icu::CopticCalendar::BASHANS, 7, "M09", 8},
4690       { 2022, UCAL_JUNE, 16, 1738, icu::CopticCalendar::PAONA, 9, "M10", 9},
4691       { 2022, UCAL_JULY, 17, 1738, icu::CopticCalendar::EPEP, 10, "M11", 10},
4692       { 2022, UCAL_AUGUST, 18, 1738, icu::CopticCalendar::MESRA, 12, "M12", 11},
4693       { 2022, UCAL_SEPTEMBER, 6, 1738, icu::CopticCalendar::NASIE, 1, "M13", 12},
4694       { 2022, UCAL_SEPTEMBER, 10, 1738, icu::CopticCalendar::NASIE, 5, "M13", 12},
4695       { 2022, UCAL_SEPTEMBER, 11, 1739, icu::CopticCalendar::TOUT, 1, "M01", 0},
4696       { 2022, UCAL_SEPTEMBER, 19, 1739, icu::CopticCalendar::TOUT, 9, "M01", 0},
4697       { 2022, UCAL_OCTOBER, 20, 1739, icu::CopticCalendar::BABA, 10, "M02", 1},
4698       { 2022, UCAL_NOVEMBER, 21, 1739, icu::CopticCalendar::HATOR, 12, "M03", 2},
4699       { 2022, UCAL_DECEMBER, 22, 1739, icu::CopticCalendar::KIAHK, 13, "M04", 3},
4700 
4701       { 2023, UCAL_JANUARY, 1, 1739, icu::CopticCalendar::KIAHK, 23, "M04", 3},
4702       { 2023, UCAL_SEPTEMBER, 6, 1739, icu::CopticCalendar::NASIE, 1, "M13", 12},
4703       { 2023, UCAL_SEPTEMBER, 11, 1739, icu::CopticCalendar::NASIE, 6, "M13", 12},
4704       { 2023, UCAL_SEPTEMBER, 12, 1740, icu::CopticCalendar::TOUT, 1, "M01", 0},
4705 
4706       { 2030, UCAL_JANUARY, 1, 1746, icu::CopticCalendar::KIAHK, 23, "M04", 3},
4707       { 2030, UCAL_SEPTEMBER, 6, 1746, icu::CopticCalendar::NASIE, 1, "M13", 12},
4708       { 2030, UCAL_SEPTEMBER, 10, 1746, icu::CopticCalendar::NASIE, 5, "M13", 12},
4709       { 2030, UCAL_SEPTEMBER, 11, 1747, icu::CopticCalendar::TOUT, 1, "M01", 0},
4710     };
4711     GregorianCalendar gc1(status);
4712     if (failure(status, "construct Calendar")) return;
4713     for (auto& cas : cases) {
4714         gc1.clear();
4715         cc1->clear();
4716         cc2->clear();
4717         gc1.set(cas.gYear, cas.gMonth, cas.gDate);
4718         cc1->setTime(gc1.getTime(status), status);
4719 
4720         cc2->set(UCAL_EXTENDED_YEAR, cas.cYear);
4721         cc2->setTemporalMonthCode(cas.cMonthCode, status);
4722         cc2->set(UCAL_DATE, cas.cDate);
4723 
4724         assertEquals("year", cas.cYear, cc1->get(UCAL_EXTENDED_YEAR, status));
4725         assertEquals("month", cas.cMonth, cc1->get(UCAL_MONTH, status));
4726         assertEquals("date", cas.cDate, cc1->get(UCAL_DATE, status));
4727         assertEquals("getTemporalMonthCode()", 0,
4728                      uprv_strcmp(cas.cMonthCode, cc1->getTemporalMonthCode(status)));
4729         if (failure(status, "getTemporalMonthCode")) continue;
4730         assertTrue("by set() and setTemporalMonthCode()", cc2->equals(*cc1, status));
4731         if (! cc2->equals(*cc1, status)) {
4732             printf("g=%f %f vs %f. diff = %f %d/%d/%d vs %d/%d/%d\n",
4733                    gc1.getTime(status), cc1->getTime(status), cc2->getTime(status),
4734                    cc1->getTime(status) - cc2->getTime(status),
4735                    cc1->get(UCAL_EXTENDED_YEAR, status),
4736                    cc1->get(UCAL_MONTH, status)+1,
4737                    cc1->get(UCAL_DATE, status),
4738                    cc2->get(UCAL_EXTENDED_YEAR, status),
4739                    cc2->get(UCAL_MONTH, status)+1,
4740                    cc2->get(UCAL_DATE, status));
4741         }
4742         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc1->get(UCAL_ORDINAL_MONTH, status));
4743         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc2->get(UCAL_ORDINAL_MONTH, status));
4744     }
4745 }
4746 
TestEthiopicCalendarSetTemporalMonthCode()4747 void CalendarTest::TestEthiopicCalendarSetTemporalMonthCode() {
4748     UErrorCode status = U_ZERO_ERROR;
4749     Locale l(Locale::getRoot());
4750     l.setKeywordValue("calendar", "ethiopic", status);
4751     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
4752     if (failure(status, "construct EthiopicCalendar")) return;
4753     LocalPointer<Calendar> cc2(cc1->clone());
4754 
4755     struct TestCase {
4756       int32_t gYear;
4757       int32_t gMonth;
4758       int32_t gDate;
4759       int32_t cYear;
4760       int32_t cMonth;
4761       int32_t cDate;
4762       const char* cMonthCode;
4763       int32_t cOrdinalMonth;
4764     } cases[] = {
4765       { 1900, UCAL_JANUARY, 1, 1892, icu::EthiopicCalendar::TAHSAS, 23, "M04", 3},
4766       { 1900, UCAL_SEPTEMBER, 6, 1892, icu::EthiopicCalendar::PAGUMEN, 1, "M13", 12},
4767       { 1900, UCAL_SEPTEMBER, 10, 1892, icu::EthiopicCalendar::PAGUMEN, 5, "M13", 12},
4768       { 1900, UCAL_SEPTEMBER, 11, 1893, icu::EthiopicCalendar::MESKEREM, 1, "M01", 0},
4769 
4770       { 2022, UCAL_JANUARY, 11, 2014, icu::EthiopicCalendar::TER, 3, "M05", 4},
4771       { 2022, UCAL_FEBRUARY, 12, 2014, icu::EthiopicCalendar::YEKATIT, 5, "M06", 5},
4772       { 2022, UCAL_MARCH, 13, 2014, icu::EthiopicCalendar::MEGABIT, 4, "M07", 6},
4773       { 2022, UCAL_APRIL, 14, 2014, icu::EthiopicCalendar::MIAZIA, 6, "M08", 7},
4774       { 2022, UCAL_MAY, 15, 2014, icu::EthiopicCalendar::GENBOT, 7, "M09", 8},
4775       { 2022, UCAL_JUNE, 16, 2014, icu::EthiopicCalendar::SENE, 9, "M10", 9},
4776       { 2022, UCAL_JULY, 17, 2014, icu::EthiopicCalendar::HAMLE, 10, "M11", 10},
4777       { 2022, UCAL_AUGUST, 18, 2014, icu::EthiopicCalendar::NEHASSE, 12, "M12", 11},
4778       { 2022, UCAL_SEPTEMBER, 6, 2014, icu::EthiopicCalendar::PAGUMEN, 1, "M13", 12},
4779       { 2022, UCAL_SEPTEMBER, 10, 2014, icu::EthiopicCalendar::PAGUMEN, 5, "M13", 12},
4780       { 2022, UCAL_SEPTEMBER, 11, 2015, icu::EthiopicCalendar::MESKEREM, 1, "M01", 0},
4781       { 2022, UCAL_SEPTEMBER, 19, 2015, icu::EthiopicCalendar::MESKEREM, 9, "M01", 0},
4782       { 2022, UCAL_OCTOBER, 20, 2015, icu::EthiopicCalendar::TEKEMT, 10, "M02", 1},
4783       { 2022, UCAL_NOVEMBER, 21, 2015, icu::EthiopicCalendar::HEDAR, 12, "M03", 2},
4784       { 2022, UCAL_DECEMBER, 22, 2015, icu::EthiopicCalendar::TAHSAS, 13, "M04", 3},
4785 
4786       { 2023, UCAL_JANUARY, 1, 2015, icu::EthiopicCalendar::TAHSAS, 23, "M04", 3},
4787       { 2023, UCAL_SEPTEMBER, 6, 2015, icu::EthiopicCalendar::PAGUMEN, 1, "M13", 12},
4788       { 2023, UCAL_SEPTEMBER, 11, 2015, icu::EthiopicCalendar::PAGUMEN, 6, "M13", 12},
4789       { 2023, UCAL_SEPTEMBER, 12, 2016, icu::EthiopicCalendar::MESKEREM, 1, "M01", 0},
4790 
4791       { 2030, UCAL_JANUARY, 1, 2022, icu::EthiopicCalendar::TAHSAS, 23, "M04", 3},
4792       { 2030, UCAL_SEPTEMBER, 6, 2022, icu::EthiopicCalendar::PAGUMEN, 1, "M13", 12},
4793       { 2030, UCAL_SEPTEMBER, 10, 2022, icu::EthiopicCalendar::PAGUMEN, 5, "M13", 12},
4794       { 2030, UCAL_SEPTEMBER, 11, 2023, icu::EthiopicCalendar::MESKEREM, 1, "M01", 0},
4795     };
4796     GregorianCalendar gc1(status);
4797     if (failure(status, "construct Calendar")) return;
4798     for (auto& cas : cases) {
4799         gc1.clear();
4800         cc1->clear();
4801         cc2->clear();
4802         gc1.set(cas.gYear, cas.gMonth, cas.gDate);
4803         cc1->setTime(gc1.getTime(status), status);
4804 
4805         cc2->set(UCAL_EXTENDED_YEAR, cas.cYear);
4806         cc2->setTemporalMonthCode(cas.cMonthCode, status);
4807         cc2->set(UCAL_DATE, cas.cDate);
4808 
4809         assertEquals("year", cas.cYear, cc1->get(UCAL_EXTENDED_YEAR, status));
4810         assertEquals("month", cas.cMonth, cc1->get(UCAL_MONTH, status));
4811         assertEquals("date", cas.cDate, cc1->get(UCAL_DATE, status));
4812         assertEquals("getTemporalMonthCode()", 0,
4813                      uprv_strcmp(cas.cMonthCode, cc1->getTemporalMonthCode(status)));
4814         if (failure(status, "getTemporalMonthCode")) continue;
4815         if (! cc2->equals(*cc1, status)) {
4816             printf("g=%f %f vs %f. diff = %f %d/%d/%d vs %d/%d/%d\n",
4817                    gc1.getTime(status), cc1->getTime(status), cc2->getTime(status),
4818                    cc1->getTime(status) - cc2->getTime(status),
4819                    cc1->get(UCAL_EXTENDED_YEAR, status),
4820                    cc1->get(UCAL_MONTH, status)+1,
4821                    cc1->get(UCAL_DATE, status),
4822                    cc2->get(UCAL_EXTENDED_YEAR, status),
4823                    cc2->get(UCAL_MONTH, status)+1,
4824                    cc2->get(UCAL_DATE, status));
4825         }
4826         assertTrue("by set() and setTemporalMonthCode()", cc2->equals(*cc1, status));
4827         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc1->get(UCAL_ORDINAL_MONTH, status));
4828         assertEquals("ordinalMonth", cas.cOrdinalMonth, cc2->get(UCAL_ORDINAL_MONTH, status));
4829     }
4830 }
4831 
VerifyMonth(CalendarTest * test,const char * message,Calendar * cc,int32_t expectedMonth,int32_t expectedOrdinalMonth,bool expectedLeapMonth,const char * expectedMonthCode)4832 void VerifyMonth(CalendarTest* test, const char* message, Calendar* cc, int32_t expectedMonth,
4833                  int32_t expectedOrdinalMonth, bool expectedLeapMonth,
4834                  const char* expectedMonthCode) {
4835     UErrorCode status = U_ZERO_ERROR;
4836     std::string buf(message);
4837     buf.append(" get(UCAL_MONTH)");
4838     test->assertEquals(buf.c_str(), expectedMonth, cc->get(UCAL_MONTH, status));
4839     buf = message;
4840     buf.append(" get(UCAL_ORDINAL_MONTH)");
4841     test->assertEquals(buf.c_str(), expectedOrdinalMonth, cc->get(UCAL_ORDINAL_MONTH, status));
4842     buf = message;
4843     buf.append(" get(UCAL_IS_LEAP_MONTH)");
4844     test->assertEquals(buf.c_str(), expectedLeapMonth ? 1 : 0, cc->get(UCAL_IS_LEAP_MONTH, status));
4845     buf = message;
4846     buf.append(" getTemporalMonthCode()");
4847     test->assertTrue(buf.c_str(), uprv_strcmp(cc->getTemporalMonthCode(status), expectedMonthCode) == 0);
4848 }
4849 
TestMostCalendarsOrdinalMonthSet()4850 void CalendarTest::TestMostCalendarsOrdinalMonthSet() {
4851     UErrorCode status = U_ZERO_ERROR;
4852     Locale l(Locale::getRoot());
4853     std::unique_ptr<icu::StringEnumeration> enumeration(
4854         Calendar::getKeywordValuesForLocale("calendar", l, false, status));
4855     for (const char* name = enumeration->next(nullptr, status);
4856         U_SUCCESS(status) && name != nullptr;
4857         name = enumeration->next(nullptr, status)) {
4858 
4859         // Test these three calendars differently.
4860         if (uprv_strcmp(name, "chinese") == 0) continue;
4861         if (uprv_strcmp(name, "dangi") == 0) continue;
4862         if (uprv_strcmp(name, "hebrew") == 0) continue;
4863 
4864         l.setKeywordValue("calendar", name, status);
4865         LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
4866         if (failure(status, "Construct Calendar")) return;
4867 
4868         LocalPointer<Calendar> cc2(cc1->clone());
4869         LocalPointer<Calendar> cc3(cc1->clone());
4870 
4871         cc1->set(UCAL_EXTENDED_YEAR, 2134);
4872         cc2->set(UCAL_EXTENDED_YEAR, 2134);
4873         cc3->set(UCAL_EXTENDED_YEAR, 2134);
4874         cc1->set(UCAL_MONTH, 5);
4875         cc2->set(UCAL_ORDINAL_MONTH, 5);
4876         cc3->setTemporalMonthCode("M06", status);
4877         if (failure(status, "setTemporalMonthCode failure")) return;
4878         cc1->set(UCAL_DATE, 23);
4879         cc2->set(UCAL_DATE, 23);
4880         cc3->set(UCAL_DATE, 23);
4881         assertTrue("M06 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
4882         assertTrue("M06 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
4883         if (failure(status, "equals failure")) return;
4884         VerifyMonth(this, "cc1", cc1.getAlias(), 5, 5, false, "M06");
4885         VerifyMonth(this, "cc2", cc2.getAlias(), 5, 5, false, "M06");
4886         VerifyMonth(this, "cc3", cc3.getAlias(), 5, 5, false, "M06");
4887 
4888         cc1->set(UCAL_ORDINAL_MONTH, 6);
4889         cc2->setTemporalMonthCode("M07", status);
4890         if (failure(status, "setTemporalMonthCode failure")) return;
4891         cc3->set(UCAL_MONTH, 6);
4892         assertTrue("M07 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
4893         assertTrue("M07 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
4894         if (failure(status, "equals failure")) return;
4895         VerifyMonth(this, "cc1", cc1.getAlias(), 6, 6, false, "M07");
4896         VerifyMonth(this, "cc2", cc2.getAlias(), 6, 6, false, "M07");
4897         VerifyMonth(this, "cc3", cc3.getAlias(), 6, 6, false, "M07");
4898 
4899         cc1->setTemporalMonthCode("M08", status);
4900         if (failure(status, "setTemporalMonthCode failure")) return;
4901         cc2->set(UCAL_MONTH, 7);
4902         cc3->set(UCAL_ORDINAL_MONTH, 7);
4903         assertTrue("M08 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
4904         assertTrue("M08 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
4905         if (failure(status, "equals failure")) return;
4906         VerifyMonth(this, "cc1", cc1.getAlias(), 7, 7, false, "M08");
4907         VerifyMonth(this, "cc2", cc2.getAlias(), 7, 7, false, "M08");
4908         VerifyMonth(this, "cc3", cc3.getAlias(), 7, 7, false, "M08");
4909 
4910         cc1->set(UCAL_DATE, 3);
4911         // For "M13", do not return error for these three calendars.
4912         if ((uprv_strcmp(name, "coptic") == 0) ||
4913             (uprv_strcmp(name, "ethiopic") == 0) ||
4914             (uprv_strcmp(name, "ethiopic-amete-alem") == 0)) {
4915 
4916             cc1->setTemporalMonthCode("M13", status);
4917             assertEquals("setTemporalMonthCode(\"M13\")", U_ZERO_ERROR, status);
4918             assertEquals("get(UCAL_MONTH) after setTemporalMonthCode(\"M13\")",
4919                          12, cc1->get(UCAL_MONTH, status));
4920             assertEquals("get(UCAL_ORDINAL_MONTH) after setTemporalMonthCode(\"M13\")",
4921                          12, cc1->get(UCAL_ORDINAL_MONTH, status));
4922             assertEquals("get", U_ZERO_ERROR, status);
4923         } else {
4924             cc1->setTemporalMonthCode("M13", status);
4925             assertEquals("setTemporalMonthCode(\"M13\")", U_ILLEGAL_ARGUMENT_ERROR, status);
4926         }
4927         status = U_ZERO_ERROR;
4928 
4929         // Out of bound monthCodes should return error.
4930         // These are not valid for calendar do not have a leap month
4931         const char* kInvalidMonthCodes[] = {
4932           "M00", "M14", "M01L", "M02L", "M03L", "M04L", "M05L", "M06L", "M07L",
4933           "M08L", "M09L", "M10L", "M11L", "M12L"};
4934         for (auto& cas : kInvalidMonthCodes) {
4935            cc1->setTemporalMonthCode(cas, status);
4936            assertEquals("setTemporalMonthCode(\"M13\")", U_ILLEGAL_ARGUMENT_ERROR, status);
4937            status = U_ZERO_ERROR;
4938         }
4939 
4940     }
4941 }
4942 
TestChineseCalendarOrdinalMonthSet()4943 void CalendarTest::TestChineseCalendarOrdinalMonthSet() {
4944     UErrorCode status = U_ZERO_ERROR;
4945     Locale l(Locale::getRoot());
4946     l.setKeywordValue("calendar", "chinese", status);
4947     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
4948     if (failure(status, "Construct Calendar")) return;
4949 
4950     LocalPointer<Calendar> cc2(cc1->clone());
4951     LocalPointer<Calendar> cc3(cc1->clone());
4952 
4953     constexpr int32_t notLeapYear = 4591;
4954     constexpr int32_t leapMarchYear = 4592;
4955 
4956     cc1->set(UCAL_EXTENDED_YEAR, leapMarchYear);
4957     cc2->set(UCAL_EXTENDED_YEAR, leapMarchYear);
4958     cc3->set(UCAL_EXTENDED_YEAR, leapMarchYear);
4959 
4960     cc1->set(UCAL_MONTH, UCAL_MARCH); cc1->set(UCAL_IS_LEAP_MONTH, 1);
4961     cc2->set(UCAL_ORDINAL_MONTH, 3);
4962     cc3->setTemporalMonthCode("M03L", status);
4963     if (failure(status, "setTemporalMonthCode failure")) return;
4964     cc1->set(UCAL_DATE, 1);
4965     cc2->set(UCAL_DATE, 1);
4966     cc3->set(UCAL_DATE, 1);
4967     assertTrue("4592 M03L cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
4968     assertTrue("4592 M03L cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
4969     if (failure(status, "equals failure")) return;
4970     VerifyMonth(this, "4592 M03L cc1", cc1.getAlias(), UCAL_MARCH, 3, true, "M03L");
4971     VerifyMonth(this, "4592 M03L cc2", cc2.getAlias(), UCAL_MARCH, 3, true, "M03L");
4972     VerifyMonth(this, "4592 M03L cc3", cc3.getAlias(), UCAL_MARCH, 3, true, "M03L");
4973 
4974     cc1->set(UCAL_EXTENDED_YEAR, notLeapYear);
4975     cc2->set(UCAL_EXTENDED_YEAR, notLeapYear);
4976     cc3->set(UCAL_EXTENDED_YEAR, notLeapYear);
4977     cc1->set(UCAL_ORDINAL_MONTH, 5);
4978     cc2->setTemporalMonthCode("M06", status);
4979     if (failure(status, "setTemporalMonthCode failure")) return;
4980     cc3->set(UCAL_MONTH, UCAL_JUNE); cc3->set(UCAL_IS_LEAP_MONTH, 0);
4981     assertTrue("4591 M06 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
4982     assertTrue("4591 M06 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
4983     if (failure(status, "equals failure")) return;
4984     VerifyMonth(this, "4591 M06 cc1", cc1.getAlias(), UCAL_JUNE, 5, false, "M06");
4985     VerifyMonth(this, "4591 M06 cc2", cc2.getAlias(), UCAL_JUNE, 5, false, "M06");
4986     VerifyMonth(this, "4591 M06 cc3", cc3.getAlias(), UCAL_JUNE, 5, false, "M06");
4987 
4988     cc1->set(UCAL_EXTENDED_YEAR, leapMarchYear);
4989     cc2->set(UCAL_EXTENDED_YEAR, leapMarchYear);
4990     cc3->set(UCAL_EXTENDED_YEAR, leapMarchYear);
4991     cc1->setTemporalMonthCode("M04", status);
4992     if (failure(status, "setTemporalMonthCode failure")) return;
4993     cc2->set(UCAL_MONTH, UCAL_APRIL); cc2->set(UCAL_IS_LEAP_MONTH, 0);
4994     cc3->set(UCAL_ORDINAL_MONTH, 4);
4995     assertTrue("4592 M04 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
4996     assertTrue("4592 M04 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
4997     if (failure(status, "equals failure")) return;
4998     // 4592 has leap March so April is the 5th month in that year.
4999     VerifyMonth(this, "4592 M04 cc1", cc1.getAlias(), UCAL_APRIL, 4, false, "M04");
5000     VerifyMonth(this, "4592 M04 cc2", cc2.getAlias(), UCAL_APRIL, 4, false, "M04");
5001     VerifyMonth(this, "4592 M04 cc3", cc3.getAlias(), UCAL_APRIL, 4, false, "M04");
5002 
5003     cc1->set(UCAL_EXTENDED_YEAR, notLeapYear);
5004     cc2->set(UCAL_EXTENDED_YEAR, notLeapYear);
5005     cc3->set(UCAL_EXTENDED_YEAR, notLeapYear);
5006     assertTrue("4591 M04 no leap month before cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5007     assertTrue("4591 M04 no leap month before cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5008     if (failure(status, "equals failure")) return;
5009     // 4592 has no leap month before April so April is the 4th month in that year.
5010     VerifyMonth(this, "4591 M04 cc1", cc1.getAlias(), UCAL_APRIL, 3, false, "M04");
5011     VerifyMonth(this, "4591 M04 cc2", cc2.getAlias(), UCAL_APRIL, 3, false, "M04");
5012     VerifyMonth(this, "4591 M04 cc3", cc3.getAlias(), UCAL_APRIL, 3, false, "M04");
5013 
5014     // Out of bound monthCodes should return error.
5015     // These are not valid for calendar do not have a leap month
5016     UErrorCode expectedStatus = U_ILLEGAL_ARGUMENT_ERROR;
5017     const char* kInvalidMonthCodes[] = { "M00", "M13", "M14" };
5018 
5019 
5020     for (auto& cas : kInvalidMonthCodes) {
5021        cc1->setTemporalMonthCode(cas, status);
5022        if (status != expectedStatus) {
5023            errln("setTemporalMonthCode(%s) should return U_ILLEGAL_ARGUMENT_ERROR", cas);
5024        }
5025        status = U_ZERO_ERROR;
5026     }
5027 }
5028 
TestDangiCalendarOrdinalMonthSet()5029 void CalendarTest::TestDangiCalendarOrdinalMonthSet() {
5030     UErrorCode status = U_ZERO_ERROR;
5031     Locale l(Locale::getRoot());
5032     const char* name = "dangi";
5033     l.setKeywordValue("calendar", name, status);
5034     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
5035     if (failure(status, "Construct Calendar")) return;
5036 
5037     LocalPointer<Calendar> cc2(cc1->clone());
5038     LocalPointer<Calendar> cc3(cc1->clone());
5039 
5040     constexpr int32_t notLeapYear = 4287;
5041     constexpr int32_t leapMarchYear = 4288;
5042 
5043     cc1->set(UCAL_EXTENDED_YEAR, leapMarchYear);
5044     cc2->set(UCAL_EXTENDED_YEAR, leapMarchYear);
5045     cc3->set(UCAL_EXTENDED_YEAR, leapMarchYear);
5046 
5047     cc1->set(UCAL_MONTH, UCAL_MARCH); cc1->set(UCAL_IS_LEAP_MONTH, 1);
5048     cc2->set(UCAL_ORDINAL_MONTH, 3);
5049     cc3->setTemporalMonthCode("M03L", status);
5050     if (failure(status, "setTemporalMonthCode failure")) return;
5051     cc1->set(UCAL_DATE, 1);
5052     cc2->set(UCAL_DATE, 1);
5053     cc3->set(UCAL_DATE, 1);
5054     assertTrue("4288 M03L cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5055     assertTrue("4288 M03L cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5056     if (failure(status, "equals failure")) return;
5057     VerifyMonth(this, "4288 M03L cc1", cc1.getAlias(), UCAL_MARCH, 3, true, "M03L");
5058     VerifyMonth(this, "4288 M03L cc2", cc2.getAlias(), UCAL_MARCH, 3, true, "M03L");
5059     VerifyMonth(this, "4288 M03L cc3", cc3.getAlias(), UCAL_MARCH, 3, true, "M03L");
5060 
5061     cc1->set(UCAL_EXTENDED_YEAR, notLeapYear);
5062     cc2->set(UCAL_EXTENDED_YEAR, notLeapYear);
5063     cc3->set(UCAL_EXTENDED_YEAR, notLeapYear);
5064     cc1->set(UCAL_ORDINAL_MONTH, 5);
5065     cc2->setTemporalMonthCode("M06", status);
5066     if (failure(status, "setTemporalMonthCode failure")) return;
5067     cc3->set(UCAL_MONTH, UCAL_JUNE); cc3->set(UCAL_IS_LEAP_MONTH, 0);
5068     assertTrue("4287 M06 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5069     assertTrue("4287 M06 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5070     if (failure(status, "equals failure")) return;
5071     VerifyMonth(this, "4287 M06 cc1", cc1.getAlias(), UCAL_JUNE, 5, false, "M06");
5072     VerifyMonth(this, "4287 M06 cc2", cc2.getAlias(), UCAL_JUNE, 5, false, "M06");
5073     VerifyMonth(this, "4287 M06 cc3", cc3.getAlias(), UCAL_JUNE, 5, false, "M06");
5074 
5075     cc1->set(UCAL_EXTENDED_YEAR, leapMarchYear);
5076     cc2->set(UCAL_EXTENDED_YEAR, leapMarchYear);
5077     cc3->set(UCAL_EXTENDED_YEAR, leapMarchYear);
5078     cc1->setTemporalMonthCode("M04", status);
5079     if (failure(status, "setTemporalMonthCode failure")) return;
5080     cc2->set(UCAL_MONTH, UCAL_APRIL); cc2->set(UCAL_IS_LEAP_MONTH, 0);
5081     cc3->set(UCAL_ORDINAL_MONTH, 4);
5082     assertTrue("4288 M04 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5083     assertTrue("4288 M04 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5084     if (failure(status, "equals failure")) return;
5085     // 4592 has leap March so April is the 5th month in that year.
5086     VerifyMonth(this, "4288 M04 cc1", cc1.getAlias(), UCAL_APRIL, 4, false, "M04");
5087     VerifyMonth(this, "4288 M04 cc2", cc2.getAlias(), UCAL_APRIL, 4, false, "M04");
5088     VerifyMonth(this, "4288 M04 cc3", cc3.getAlias(), UCAL_APRIL, 4, false, "M04");
5089 
5090     cc1->set(UCAL_EXTENDED_YEAR, notLeapYear);
5091     cc2->set(UCAL_EXTENDED_YEAR, notLeapYear);
5092     cc3->set(UCAL_EXTENDED_YEAR, notLeapYear);
5093     assertTrue("4287 M04 no leap month before cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5094     assertTrue("4287 M04 no leap month before cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5095     if (failure(status, "equals failure")) return;
5096     // 4592 has no leap month before April so April is the 4th month in that year.
5097     VerifyMonth(this, "4287 M04 cc1", cc1.getAlias(), UCAL_APRIL, 3, false, "M04");
5098     VerifyMonth(this, "4287 M04 cc2", cc2.getAlias(), UCAL_APRIL, 3, false, "M04");
5099     VerifyMonth(this, "4287 M04 cc3", cc3.getAlias(), UCAL_APRIL, 3, false, "M04");
5100 
5101     // Out of bound monthCodes should return error.
5102     // These are not valid for calendar do not have a leap month
5103     UErrorCode expectedStatus = U_ILLEGAL_ARGUMENT_ERROR;
5104     const char* kInvalidMonthCodes[] = { "M00", "M13", "M14" };
5105 
5106     for (auto& cas : kInvalidMonthCodes) {
5107        cc1->setTemporalMonthCode(cas, status);
5108        if (status != expectedStatus) {
5109            errln("setTemporalMonthCode(%s) should return U_ILLEGAL_ARGUMENT_ERROR", cas);
5110        }
5111        status = U_ZERO_ERROR;
5112     }
5113 }
5114 
TestHebrewCalendarOrdinalMonthSet()5115 void CalendarTest::TestHebrewCalendarOrdinalMonthSet() {
5116     UErrorCode status = U_ZERO_ERROR;
5117     Locale l(Locale::getRoot());
5118     l.setKeywordValue("calendar", "hebrew", status);
5119     LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
5120     if (failure(status, "Construct Calendar")) return;
5121 
5122     LocalPointer<Calendar> cc2(cc1->clone());
5123     LocalPointer<Calendar> cc3(cc1->clone());
5124 
5125     // 5782 is leap year, 5781 is NOT.
5126     cc1->set(UCAL_EXTENDED_YEAR, 5782);
5127     cc2->set(UCAL_EXTENDED_YEAR, 5782);
5128     cc3->set(UCAL_EXTENDED_YEAR, 5782);
5129     cc1->set(UCAL_MONTH, icu::HebrewCalendar::ADAR_1);
5130     cc2->set(UCAL_ORDINAL_MONTH, 5);
5131     cc3->setTemporalMonthCode("M05L", status);
5132     if (failure(status, "setTemporalMonthCode failure")) return;
5133     cc1->set(UCAL_DATE, 1);
5134     cc2->set(UCAL_DATE, 1);
5135     cc3->set(UCAL_DATE, 1);
5136     assertTrue("5782 M05L cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5137     assertTrue("5782 M05L cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5138     if (failure(status, "equals failure")) return;
5139     VerifyMonth(this, "cc1", cc1.getAlias(), icu::HebrewCalendar::ADAR_1, 5, false, "M05L");
5140     VerifyMonth(this, "cc2", cc2.getAlias(), icu::HebrewCalendar::ADAR_1, 5, false, "M05L");
5141     VerifyMonth(this, "cc3", cc3.getAlias(), icu::HebrewCalendar::ADAR_1, 5, false, "M05L");
5142 
5143     cc1->set(UCAL_ORDINAL_MONTH, 4);
5144     cc2->setTemporalMonthCode("M05", status);
5145     if (failure(status, "setTemporalMonthCode failure")) return;
5146     cc3->set(UCAL_MONTH, icu::HebrewCalendar::SHEVAT);
5147     assertTrue("5782 M05 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5148     assertTrue("5782 M05 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5149     if (failure(status, "equals failure")) return;
5150     VerifyMonth(this, "cc1", cc1.getAlias(), icu::HebrewCalendar::SHEVAT, 4, false, "M05");
5151     VerifyMonth(this, "cc2", cc2.getAlias(), icu::HebrewCalendar::SHEVAT, 4, false, "M05");
5152     VerifyMonth(this, "cc3", cc3.getAlias(), icu::HebrewCalendar::SHEVAT, 4, false, "M05");
5153 
5154     cc1->set(UCAL_EXTENDED_YEAR, 5781);
5155     cc2->set(UCAL_EXTENDED_YEAR, 5781);
5156     cc3->set(UCAL_EXTENDED_YEAR, 5781);
5157     cc1->setTemporalMonthCode("M06", status);
5158     if (failure(status, "setTemporalMonthCode failure")) return;
5159     cc2->set(UCAL_MONTH, icu::HebrewCalendar::ADAR);
5160     cc3->set(UCAL_ORDINAL_MONTH, 5);
5161     assertTrue("5781 M06 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5162     assertTrue("5781 M06 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5163     if (failure(status, "equals failure")) return;
5164     VerifyMonth(this, "cc1", cc1.getAlias(), icu::HebrewCalendar::ADAR, 5, false, "M06");
5165     VerifyMonth(this, "cc2", cc2.getAlias(), icu::HebrewCalendar::ADAR, 5, false, "M06");
5166     VerifyMonth(this, "cc3", cc3.getAlias(), icu::HebrewCalendar::ADAR, 5, false, "M06");
5167 
5168     cc1->set(UCAL_EXTENDED_YEAR, 5782);
5169     cc2->set(UCAL_EXTENDED_YEAR, 5782);
5170     cc3->set(UCAL_EXTENDED_YEAR, 5782);
5171     assertTrue("5782 M06 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5172     assertTrue("5782 M06 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5173     if (failure(status, "equals failure")) return;
5174     VerifyMonth(this, "cc1", cc1.getAlias(), icu::HebrewCalendar::ADAR, 6, false, "M06");
5175     VerifyMonth(this, "cc2", cc2.getAlias(), icu::HebrewCalendar::ADAR, 6, false, "M06");
5176     VerifyMonth(this, "cc3", cc3.getAlias(), icu::HebrewCalendar::ADAR, 6, false, "M06");
5177 
5178     cc1->set(UCAL_ORDINAL_MONTH, 7);
5179     cc2->setTemporalMonthCode("M07", status);
5180     if (failure(status, "setTemporalMonthCode failure")) return;
5181     cc3->set(UCAL_MONTH, icu::HebrewCalendar::NISAN);
5182     assertTrue("5782 M07 cc2==cc1 set month by UCAL_MONTH and UCAL_UCAL_ORDINAL_MONTH", cc2->equals(*cc1, status));
5183     assertTrue("5782 M07 cc2==cc3 set month by UCAL_MONTH and setTemporalMonthCode", cc2->equals(*cc3, status));
5184     if (failure(status, "equals failure")) return;
5185     VerifyMonth(this, "cc1", cc1.getAlias(), icu::HebrewCalendar::NISAN, 7, false, "M07");
5186     VerifyMonth(this, "cc2", cc2.getAlias(), icu::HebrewCalendar::NISAN, 7, false, "M07");
5187     VerifyMonth(this, "cc3", cc3.getAlias(), icu::HebrewCalendar::NISAN, 7, false, "M07");
5188 
5189     // Out of bound monthCodes should return error.
5190     // These are not valid for calendar do not have a leap month
5191     UErrorCode expectedStatus = U_ILLEGAL_ARGUMENT_ERROR;
5192     const char* kInvalidMonthCodes[] = { "M00", "M13", "M14",
5193       "M01L", "M02L", "M03L", "M04L",
5194       /* M05L could be legal */
5195       "M06L", "M07L", "M08L", "M09L", "M10L", "M11L", "M12L",
5196     };
5197 
5198     for (auto& cas : kInvalidMonthCodes) {
5199        cc1->setTemporalMonthCode(cas, status);
5200        if (status != expectedStatus) {
5201            errln("setTemporalMonthCode(%s) should return U_ILLEGAL_ARGUMENT_ERROR", cas);
5202        }
5203        status = U_ZERO_ERROR;
5204     }
5205 }
5206 
TestCalendarAddOrdinalMonth()5207 void CalendarTest::TestCalendarAddOrdinalMonth() {
5208     UErrorCode status = U_ZERO_ERROR;
5209     GregorianCalendar gc(status);
5210     gc.set(2022, UCAL_DECEMBER, 16);
5211     Locale l(Locale::getRoot());
5212     std::unique_ptr<icu::StringEnumeration> enumeration(
5213         Calendar::getKeywordValuesForLocale("calendar", l, false, status));
5214     for (const char* name = enumeration->next(nullptr, status);
5215         U_SUCCESS(status) && name != nullptr;
5216         name = enumeration->next(nullptr, status)) {
5217 
5218         l.setKeywordValue("calendar", name, status);
5219         LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
5220         if (failure(status, "Construct Calendar")) return;
5221 
5222         cc1->setTime(gc.getTime(status), status);
5223         LocalPointer<Calendar> cc2(cc1->clone());
5224 
5225         for (int i = 0; i < 8; i++) {
5226             for (int j = 1; j < 8; j++) {
5227                 cc1->add(UCAL_MONTH, j, status);
5228                 cc2->add(UCAL_ORDINAL_MONTH, j, status);
5229                 if (failure(status, "add j")) return;
5230                 assertTrue("two add produce the same result", cc2->equals(*cc1, status));
5231             }
5232             for (int j = 1; j < 8; j++) {
5233                 cc1->add(UCAL_MONTH, -j, status);
5234                 cc2->add(UCAL_ORDINAL_MONTH, -j, status);
5235                 if (failure(status, "add -j")) return;
5236                 assertTrue("two add produce the same result", cc2->equals(*cc1, status));
5237             }
5238         }
5239     }
5240 }
5241 
TestCalendarRollOrdinalMonth()5242 void CalendarTest::TestCalendarRollOrdinalMonth() {
5243     UErrorCode status = U_ZERO_ERROR;
5244     GregorianCalendar gc(status);
5245     gc.set(2022, UCAL_DECEMBER, 16);
5246     Locale l(Locale::getRoot());
5247     std::unique_ptr<icu::StringEnumeration> enumeration(
5248         Calendar::getKeywordValuesForLocale("calendar", l, false, status));
5249     for (const char* name = enumeration->next(nullptr, status);
5250         U_SUCCESS(status) && name != nullptr;
5251         name = enumeration->next(nullptr, status)) {
5252 
5253         l.setKeywordValue("calendar", name, status);
5254         LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
5255         if (failure(status, "Construct Calendar")) return;
5256 
5257         cc1->setTime(gc.getTime(status), status);
5258         LocalPointer<Calendar> cc2(cc1->clone());
5259 
5260         for (int i = 0; i < 8; i++) {
5261             for (int j = 1; j < 8; j++) {
5262                 cc1->roll(UCAL_MONTH, j, status);
5263                 cc2->roll(UCAL_ORDINAL_MONTH, j, status);
5264                 if (failure(status, "roll j")) return;
5265                 assertTrue("two add produce the same result", cc2->equals(*cc1, status));
5266             }
5267             for (int j = 1; j < 8; j++) {
5268                 cc1->roll(UCAL_MONTH, -j, status);
5269                 cc2->roll(UCAL_ORDINAL_MONTH, -j, status);
5270                 if (failure(status, "roll -j")) return;
5271                 assertTrue("two add produce the same result", cc2->equals(*cc1, status));
5272             }
5273             for (int j = 1; j < 3; j++) {
5274                 cc1->roll(UCAL_MONTH, true, status);
5275                 cc2->roll(UCAL_ORDINAL_MONTH, true, status);
5276                 if (failure(status, "roll true")) return;
5277                 assertTrue("two add produce the same result", cc2->equals(*cc1, status));
5278             }
5279             for (int j = 1; j < 3; j++) {
5280                 cc1->roll(UCAL_MONTH, false, status);
5281                 cc2->roll(UCAL_ORDINAL_MONTH, false, status);
5282                 if (failure(status, "roll false")) return;
5283                 assertTrue("two add produce the same result", cc2->equals(*cc1, status));
5284             }
5285         }
5286     }
5287 }
5288 
TestLimitsOrdinalMonth()5289 void CalendarTest::TestLimitsOrdinalMonth() {
5290     UErrorCode status = U_ZERO_ERROR;
5291     GregorianCalendar gc(status);
5292     gc.set(2022, UCAL_DECEMBER, 16);
5293     Locale l(Locale::getRoot());
5294     std::unique_ptr<icu::StringEnumeration> enumeration(
5295         Calendar::getKeywordValuesForLocale("calendar", l, false, status));
5296 
5297     struct Expectation {
5298       const char* calendar;
5299       int32_t min;
5300       int32_t max;
5301       int32_t greatestMin;
5302       int32_t leastMax;
5303     } kExpectations[] = {
5304         { "gregorian", 0, 11, 0, 11 },
5305         { "japanese", 0, 11, 0, 11 },
5306         { "buddhist", 0, 11, 0, 11 },
5307         { "roc", 0, 11, 0, 11 },
5308         { "persian", 0, 11, 0, 11 },
5309         { "islamic-civil", 0, 11, 0, 11 },
5310         { "islamic", 0, 11, 0, 11 },
5311         { "hebrew", 0, 12, 0, 11 },
5312         { "chinese", 0, 12, 0, 11 },
5313         { "indian", 0, 11, 0, 11 },
5314         { "coptic", 0, 12, 0, 12 },
5315         { "ethiopic", 0, 12, 0, 12 },
5316         { "ethiopic-amete-alem", 0, 12, 0, 12 },
5317         { "iso8601", 0, 11, 0, 11 },
5318         { "dangi", 0, 12, 0, 11 },
5319         { "islamic-umalqura", 0, 11, 0, 11 },
5320         { "islamic-tbla", 0, 11, 0, 11 },
5321         { "islamic-rgsa", 0, 11, 0, 11 },
5322     };
5323 
5324     for (const char* name = enumeration->next(nullptr, status);
5325         U_SUCCESS(status) && name != nullptr;
5326         name = enumeration->next(nullptr, status)) {
5327         l.setKeywordValue("calendar", name, status);
5328         LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
5329         if (failure(status, "Construct Calendar")) return;
5330         bool found = false;
5331         for (auto& exp : kExpectations) {
5332             if (uprv_strcmp(exp.calendar, name) == 0) {
5333                 found = true;
5334                 assertEquals("getMinimum(UCAL_ORDINAL_MONTH)",
5335                              exp.min, cc1->getMinimum(UCAL_ORDINAL_MONTH));
5336                 assertEquals("getMaximum(UCAL_ORDINAL_MONTH)",
5337                              exp.max, cc1->getMaximum(UCAL_ORDINAL_MONTH));
5338                 assertEquals("getMinimum(UCAL_ORDINAL_MONTH)",
5339                              exp.greatestMin, cc1->getGreatestMinimum(UCAL_ORDINAL_MONTH));
5340                 assertEquals("getMinimum(UCAL_ORDINAL_MONTH)",
5341                              exp.leastMax, cc1->getLeastMaximum(UCAL_ORDINAL_MONTH));
5342                 break;
5343             }
5344         }
5345         if (!found) {
5346             errln("Cannot find expectation");
5347         }
5348     }
5349 }
5350 
TestActualLimitsOrdinalMonth()5351 void CalendarTest::TestActualLimitsOrdinalMonth() {
5352     UErrorCode status = U_ZERO_ERROR;
5353     GregorianCalendar gc(status);
5354     gc.set(2022, UCAL_DECEMBER, 16);
5355     Locale l(Locale::getRoot());
5356     std::unique_ptr<icu::StringEnumeration> enumeration(
5357         Calendar::getKeywordValuesForLocale("calendar", l, false, status));
5358 
5359     struct TestCases {
5360       const char* calendar;
5361       int32_t extended_year;
5362       int32_t actualMinOrdinalMonth;
5363       int32_t actualMaxOrdinalMonth;
5364     } cases[] = {
5365         { "gregorian", 2021, 0, 11 },
5366         { "gregorian", 2022, 0, 11 },
5367         { "gregorian", 2023, 0, 11 },
5368         { "japanese", 2021, 0, 11 },
5369         { "japanese", 2022, 0, 11 },
5370         { "japanese", 2023, 0, 11 },
5371         { "buddhist", 2021, 0, 11 },
5372         { "buddhist", 2022, 0, 11 },
5373         { "buddhist", 2023, 0, 11 },
5374         { "roc", 2021, 0, 11 },
5375         { "roc", 2022, 0, 11 },
5376         { "roc", 2023, 0, 11 },
5377         { "persian", 1400, 0, 11 },
5378         { "persian", 1401, 0, 11 },
5379         { "persian", 1402, 0, 11 },
5380         { "hebrew", 5782, 0, 12 },
5381         { "hebrew", 5783, 0, 11 },
5382         { "hebrew", 5789, 0, 11 },
5383         { "hebrew", 5790, 0, 12 },
5384         { "chinese", 4645, 0, 11 },
5385         { "chinese", 4646, 0, 12 },
5386         { "chinese", 4647, 0, 11 },
5387         { "dangi", 4645 + 304, 0, 11 },
5388         { "dangi", 4646 + 304, 0, 12 },
5389         { "dangi", 4647 + 304, 0, 11 },
5390         { "indian", 1944, 0, 11 },
5391         { "indian", 1945, 0, 11 },
5392         { "indian", 1946, 0, 11 },
5393         { "coptic", 1737, 0, 12 },
5394         { "coptic", 1738, 0, 12 },
5395         { "coptic", 1739, 0, 12 },
5396         { "ethiopic", 2013, 0, 12 },
5397         { "ethiopic", 2014, 0, 12 },
5398         { "ethiopic", 2015, 0, 12 },
5399         { "ethiopic-amete-alem", 2014, 0, 12 },
5400         { "ethiopic-amete-alem", 2015, 0, 12 },
5401         { "ethiopic-amete-alem", 2016, 0, 12 },
5402         { "iso8601", 2022, 0, 11 },
5403         { "islamic-civil", 1443, 0, 11 },
5404         { "islamic-civil", 1444, 0, 11 },
5405         { "islamic-civil", 1445, 0, 11 },
5406         { "islamic", 1443, 0, 11 },
5407         { "islamic", 1444, 0, 11 },
5408         { "islamic", 1445, 0, 11 },
5409         { "islamic-umalqura", 1443, 0, 11 },
5410         { "islamic-umalqura", 1444, 0, 11 },
5411         { "islamic-umalqura", 1445, 0, 11 },
5412         { "islamic-tbla", 1443, 0, 11 },
5413         { "islamic-tbla", 1444, 0, 11 },
5414         { "islamic-tbla", 1445, 0, 11 },
5415         { "islamic-rgsa", 1443, 0, 11 },
5416         { "islamic-rgsa", 1444, 0, 11 },
5417         { "islamic-rgsa", 1445, 0, 11 },
5418     };
5419 
5420     for (auto& cas : cases) {
5421         l.setKeywordValue("calendar", cas.calendar, status);
5422         LocalPointer<Calendar> cc1(Calendar::createInstance(l, status));
5423         if (failure(status, "Construct Calendar")) return;
5424         cc1->set(UCAL_EXTENDED_YEAR, cas.extended_year);
5425         cc1->set(UCAL_ORDINAL_MONTH, 0);
5426         cc1->set(UCAL_DATE, 1);
5427         assertEquals("getActualMinimum(UCAL_ORDINAL_MONTH)",
5428                      cas.actualMinOrdinalMonth, cc1->getActualMinimum(UCAL_ORDINAL_MONTH, status));
5429         assertEquals("getActualMaximum(UCAL_ORDINAL_MONTH)",
5430                      cas.actualMaxOrdinalMonth, cc1->getActualMaximum(UCAL_ORDINAL_MONTH, status));
5431     }
5432 }
5433 
5434 // The Lunar year which majorty part fall into 1889 and the early part of 1890
5435 // should have no leap months, but currently ICU calculate and show there is
5436 // a Leap month after the 12th month and before the first month of the Chinese
5437 // Calendar which overlapping most of the 1890 year in Gregorian.
5438 //
5439 // We use the value from
5440 // https://ytliu0.github.io/ChineseCalendar/table_period.html?period=qing
5441 // and https://ytliu0.github.io/ChineseCalendar/index_chinese.html
5442 // as the expected value. The same results were given by many several other
5443 // sites not just his one.
5444 //
5445 // There should be a Leap month after the 2nd month of the Chinese Calendar year
5446 // mostly overlapping with 1890 and should have no leap month in the Chinese
5447 // Calendar year mostly overlapping with 1889.
TestChineseCalendarMonthInSpecialYear()5448 void CalendarTest::TestChineseCalendarMonthInSpecialYear() {
5449     UErrorCode status = U_ZERO_ERROR;
5450     GregorianCalendar gc(status);
5451     ChineseCalendar cal(Locale::getRoot(), status);
5452     if (failure(status, "Constructor failed")) return;
5453     struct TestCase {
5454       int32_t gyear;
5455       int32_t gmonth;
5456       int32_t gdate;
5457       int32_t cmonth; // 0-based month number: 1st month = 0, 2nd month = 1.
5458       int32_t cdate;
5459       bool cleapmonth;
5460     } cases[] = {
5461         // Gregorian             Chinese Calendar
5462         // First some recent date
5463         // From https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2022.pdf
5464         { 2022, UCAL_DECEMBER, 15, 11-1, 22, false},
5465         //                          ^-- m-1 to convert to 0-based month from 1-based.
5466         // From https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2023.pdf
5467         { 2023, UCAL_MARCH, 21, 2-1, 30, false},
5468         { 2023, UCAL_MARCH, 22, 2-1, 1, true},
5469         // We know there are some problematic year, especially those involved
5470         // the rare cases of M11L and M12L.
5471         // Check 1890 and 2033.
5472         //
5473         // 2033 has M11L
5474         // From https://www.hko.gov.hk/tc/gts/time/calendar/pdf/files/2033.pdf
5475         { 2033, UCAL_DECEMBER, 21, 11-1, 30, false},
5476         { 2033, UCAL_DECEMBER, 22, 11-1, 1, true},
5477         // Here are the date we get from multiple external sources
5478         // https://ytliu0.github.io/ChineseCalendar/index_chinese.html
5479         // https://ytliu0.github.io/ChineseCalendar/table_period.html?period=qing
5480         // There should have no leap 12th month in the year mostly overlapping
5481         // 1889 but should have a leap 2th month in the year mostly overlapping
5482         // with 1890.
5483         { 1890, UCAL_JANUARY, 1, 12-1, 11, false},
5484         { 1890, UCAL_JANUARY, 20, 12-1, 30, false},
5485         { 1890, UCAL_JANUARY, 21, 1-1, 1, false},
5486         { 1890, UCAL_FEBRUARY, 1, 1-1, 12, false},
5487         { 1890, UCAL_FEBRUARY, 19, 2-1, 1, false},
5488         { 1890, UCAL_MARCH, 1, 2-1, 11, false},
5489         { 1890, UCAL_MARCH, 21, 2-1, 1, true},
5490         { 1890, UCAL_APRIL, 1, 2-1, 12, true},
5491         { 1890, UCAL_APRIL, 18, 2-1, 29, true},
5492         { 1890, UCAL_APRIL, 19, 3-1, 1, false},
5493         { 1890, UCAL_APRIL, 20, 3-1, 2, false},
5494     };
5495     for (auto& cas : cases) {
5496         gc.set(cas.gyear, cas.gmonth, cas.gdate);
5497         cal.setTime(gc.getTime(status), status);
5498         if (failure(status, "getTime/setTime failed")) return;
5499         int32_t actual_month = cal.get(UCAL_MONTH, status);
5500         int32_t actual_date = cal.get(UCAL_DATE, status);
5501         int32_t actual_in_leap_month = cal.get(UCAL_IS_LEAP_MONTH, status);
5502         if (failure(status, "get failed")) return;
5503         if (cas.cmonth != actual_month ||
5504             cas.cdate != actual_date ||
5505             cas.cleapmonth != (actual_in_leap_month != 0)) {
5506             if (cas.gyear == 1890 &&
5507                 logKnownIssue("ICU-22230", "Problem between 1890/1/21 and 1890/4/18")) {
5508                   continue;
5509             }
5510             errln("Fail: Gregorian(%d/%d/%d) should be Chinese %d%s/%d but got %d%s/%d",
5511                   cas.gyear, cas.gmonth+1, cas.gdate,
5512                   cas.cmonth+1, cas.cleapmonth ? "L" : "" , cas.cdate,
5513                   actual_month+1, ((actual_in_leap_month != 0) ? "L" : ""), actual_date );
5514         }
5515     }
5516 }
5517 
5518 // Test the stack will not overflow with dangi calendar during "roll".
TestDangiOverflowIsLeapMonthBetween22507()5519 void CalendarTest::TestDangiOverflowIsLeapMonthBetween22507() {
5520     Locale locale("en@calendar=dangi");
5521     UErrorCode status = U_ZERO_ERROR;
5522     LocalPointer<Calendar> cal(Calendar::createInstance(
5523             *TimeZone::getGMT(), locale, status));
5524     cal->clear();
5525     status = U_ZERO_ERROR;
5526     cal->add(UCAL_MONTH, 1242972234, status);
5527     status = U_ZERO_ERROR;
5528     cal->roll(UCAL_MONTH, 1249790538, status);
5529     status = U_ZERO_ERROR;
5530     // Without the fix, the stack will overflow during this roll().
5531     cal->roll(UCAL_MONTH, 1246382666, status);
5532 }
5533 
TestFWWithISO8601()5534 void CalendarTest::TestFWWithISO8601() {
5535     // ICU UCAL_SUNDAY is 1, UCAL_MONDAY is 2, ... UCAL_SATURDAY is 7.
5536     const char *locales[]  = {
5537       "",
5538       "en-u-ca-iso8601-fw-sun",
5539       "en-u-ca-iso8601-fw-mon",
5540       "en-u-ca-iso8601-fw-tue",
5541       "en-u-ca-iso8601-fw-wed",
5542       "en-u-ca-iso8601-fw-thu",
5543       "en-u-ca-iso8601-fw-fri",
5544       "en-u-ca-iso8601-fw-sat"
5545     };
5546     for (int i = UCAL_SUNDAY; i <= UCAL_SATURDAY; i++) {
5547         UErrorCode status = U_ZERO_ERROR;
5548         const char* locale = locales[i];
5549         LocalPointer<Calendar> cal(
5550             Calendar::createInstance(
5551                 Locale(locale), status), status);
5552         if (failure(status, "Constructor failed")) continue;
5553         std::string msg("Calendar::createInstance(\"");
5554         msg = msg + locale + "\")->getFirstDayOfWeek()";
5555         assertEquals(msg.c_str(), i, cal->getFirstDayOfWeek());
5556     }
5557 }
TestRollWeekOfYear()5558 void CalendarTest::TestRollWeekOfYear() {
5559     UErrorCode status = U_ZERO_ERROR;
5560     Locale l("zh_TW@calendar=chinese");
5561     LocalPointer<Calendar> cal(Calendar::createInstance(l, status), status);
5562     cal->set(UCAL_EXTENDED_YEAR, -1107626);
5563     cal->set(UCAL_MONTH, UCAL_JANUARY);
5564     cal->set(UCAL_DATE, 1);
5565     cal->roll(UCAL_WEEK_OF_YEAR, 0x7fffff, status);
5566     U_ASSERT(U_SUCCESS(status));
5567     cal->roll(UCAL_WEEK_OF_YEAR, 1, status);
5568 }
5569 
verifyFirstDayOfWeek(const char * locale,UCalendarDaysOfWeek expected)5570 void CalendarTest::verifyFirstDayOfWeek(const char* locale, UCalendarDaysOfWeek expected) {
5571     UErrorCode status = U_ZERO_ERROR;
5572     Locale l = Locale::forLanguageTag(locale, status);
5573     U_ASSERT(U_SUCCESS(status));
5574     LocalPointer<Calendar> cal(Calendar::createInstance(l, status), status);
5575     U_ASSERT(U_SUCCESS(status));
5576     assertEquals(locale,
5577                  expected, cal->getFirstDayOfWeek(status));
5578     U_ASSERT(U_SUCCESS(status));
5579 }
5580 
5581 /**
5582  * Test "First Day Overrides" behavior
5583  * https://unicode.org/reports/tr35/tr35-dates.html#first-day-overrides
5584  * And data in <firstDay> of
5585  * https://github.com/unicode-org/cldr/blob/main/common/supplemental/supplementalData.xml
5586  *
5587  * Examples of region for First Day of a week
5588  * Friday: MV
5589  * Saturday: AE AF
5590  * Sunday: US JP
5591  * Monday: GB
5592  */
TestFirstDayOfWeek()5593 void CalendarTest::TestFirstDayOfWeek() {
5594     // Test -u-fw- value
5595     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-fw-sun-rg-mvzzzz-sd-usca", UCAL_SUNDAY);
5596     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-fw-mon-rg-mvzzzz-sd-usca", UCAL_MONDAY);
5597     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-fw-tue-rg-mvzzzz-sd-usca", UCAL_TUESDAY);
5598     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-fw-wed-rg-mvzzzz-sd-usca", UCAL_WEDNESDAY);
5599     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-fw-thu-rg-mvzzzz-sd-usca", UCAL_THURSDAY);
5600     verifyFirstDayOfWeek("en-AE-u-ca-iso8601-fw-fri-rg-aezzzz-sd-usca", UCAL_FRIDAY);
5601     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-fw-sat-rg-mvzzzz-sd-usca", UCAL_SATURDAY);
5602 
5603     // Test -u-rg- value
5604     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-rg-mvzzzz-sd-usca", UCAL_FRIDAY);
5605     // Android-changed: the first week day in UAE is Monday.
5606     // verifyFirstDayOfWeek("en-MV-u-ca-iso8601-rg-aezzzz-sd-usca", UCAL_SATURDAY);
5607     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-rg-aezzzz-sd-usca", UCAL_MONDAY);
5608     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-rg-uszzzz-sd-usca", UCAL_SUNDAY);
5609     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-rg-gbzzzz-sd-usca", UCAL_MONDAY);
5610 
5611     // Test -u-ca-iso8601
5612     verifyFirstDayOfWeek("en-MV-u-ca-iso8601-sd-mv00", UCAL_MONDAY);
5613     verifyFirstDayOfWeek("en-AE-u-ca-iso8601-sd-aeaj", UCAL_MONDAY);
5614     verifyFirstDayOfWeek("en-US-u-ca-iso8601-sd-usca", UCAL_MONDAY);
5615 
5616     // Test Region Tags only
5617     verifyFirstDayOfWeek("en-MV", UCAL_FRIDAY);
5618     // Android-changed: the first week day in UAE is Monday.
5619     // verifyFirstDayOfWeek("en-AE", UCAL_SATURDAY);
5620     verifyFirstDayOfWeek("en-AE", UCAL_MONDAY);
5621     verifyFirstDayOfWeek("en-US", UCAL_SUNDAY);
5622     verifyFirstDayOfWeek("dv-GB", UCAL_MONDAY);
5623 
5624     // Test -u-sd-
5625     verifyFirstDayOfWeek("en-u-sd-mv00", UCAL_FRIDAY);
5626     // Android-changed: the first week day in UAE is Monday.
5627     // verifyFirstDayOfWeek("en-u-sd-aeaj", UCAL_SATURDAY);
5628     verifyFirstDayOfWeek("en-u-sd-aeaj", UCAL_MONDAY);
5629     verifyFirstDayOfWeek("en-u-sd-usca", UCAL_SUNDAY);
5630     verifyFirstDayOfWeek("dv-u-sd-gbsct", UCAL_MONDAY);
5631 
5632     // Test Add Likely Subtags algorithm produces a region
5633     // dv => dv_Thaa_MV => Friday
5634     verifyFirstDayOfWeek("dv", UCAL_FRIDAY);
5635     // und_Thaa => dv_Thaa_MV => Friday
5636     verifyFirstDayOfWeek("und-Thaa", UCAL_FRIDAY);
5637 
5638     // Android-changed: the first week day in UAE is Monday.
5639     // ssh => ssh_Arab_AE => Saturday
5640     // verifyFirstDayOfWeek("ssh", UCAL_SATURDAY);
5641     // ssh => ssh_Arab_AE => Monday
5642     verifyFirstDayOfWeek("ssh", UCAL_MONDAY);
5643     // wbl_Arab => wbl_Arab_AF => Saturday
5644     verifyFirstDayOfWeek("wbl-Arab", UCAL_SATURDAY);
5645 
5646     // en => en_Latn_US => Sunday
5647     verifyFirstDayOfWeek("en", UCAL_SUNDAY);
5648     // und_Hira => ja_Hira_JP => Sunday
5649     verifyFirstDayOfWeek("und-Hira", UCAL_SUNDAY);
5650 
5651     verifyFirstDayOfWeek("zxx", UCAL_MONDAY);
5652 }
5653 
Test22633ChineseOverflow()5654 void CalendarTest::Test22633ChineseOverflow() {
5655     UErrorCode status = U_ZERO_ERROR;
5656     LocalPointer<Calendar> cal(Calendar::createInstance(Locale("en@calendar=chinese"), status), status);
5657     U_ASSERT(U_SUCCESS(status));
5658     cal->setTime(2043071457431218011677338081118001787485161156097100985923226601036925437809699842362992455895409920480414647512899096575018732258582416938813614617757317338664031880042592085084690242819214720523061081124318514531466365480449329351434046537728.000000, status);
5659     U_ASSERT(U_SUCCESS(status));
5660     cal->set(UCAL_EXTENDED_YEAR, -1594662558);
5661     cal->get(UCAL_YEAR, status);
5662     assertTrue("Should return success", U_SUCCESS(status));
5663 
5664     cal->setTime(17000065021099877464213620139773683829419175940649608600213244013003611130029599692535053209683880603725167923910423116397083334648012657787978113960494455603744210944.000000, status);
5665     cal->add(UCAL_YEAR, 1935762034, status);
5666     assertTrue("Should return falure", U_FAILURE(status));
5667 
5668     status = U_ZERO_ERROR;
5669     cal->set(UCAL_ERA, 1651667877);
5670     cal->add(UCAL_YEAR, 1935762034, status);
5671     assertTrue("Should return falure", U_FAILURE(status));
5672 }
Test22633IndianOverflow()5673 void CalendarTest::Test22633IndianOverflow() {
5674     UErrorCode status = U_ZERO_ERROR;
5675     LocalPointer<Calendar> cal(Calendar::createInstance(Locale("en@calendar=indian"), status), status);
5676     U_ASSERT(U_SUCCESS(status));
5677     cal->roll(UCAL_EXTENDED_YEAR, -2120158417, status);
5678     assertTrue("Should return success", U_SUCCESS(status));
5679 }
Test22633IslamicUmalquraOverflow()5680 void CalendarTest::Test22633IslamicUmalquraOverflow() {
5681     UErrorCode status = U_ZERO_ERROR;
5682     LocalPointer<Calendar> cal(Calendar::createInstance(Locale("en@calendar=islamic-umalqura"), status), status);
5683     U_ASSERT(U_SUCCESS(status));
5684     cal->roll(UCAL_YEAR, -134404585, status);
5685     assertTrue("Should return success", U_SUCCESS(status));
5686 }
5687 
Test22633PersianOverflow()5688 void CalendarTest::Test22633PersianOverflow() {
5689     UErrorCode status = U_ZERO_ERROR;
5690     LocalPointer<Calendar> cal(Calendar::createInstance(Locale("en@calendar=persian"), status), status);
5691     U_ASSERT(U_SUCCESS(status));
5692     cal->add(UCAL_ORDINAL_MONTH, 1594095615, status);
5693     assertTrue("Should return success", U_SUCCESS(status));
5694 
5695     cal->clear();
5696     cal->fieldDifference(
5697         -874417153152678003697180890506448687181167523704194267774844295805672585701302166100950793070884718009504322601688549650298776623158701367393457997817732662883592665106020013730689242515513560464852918376875667091108609655859551000798163265126400.000000,
5698         UCAL_YEAR, status);
5699     assertFalse("Should not return success", U_SUCCESS(status));
5700 }
5701 
Test22633HebrewOverflow()5702 void CalendarTest::Test22633HebrewOverflow() {
5703     UErrorCode status = U_ZERO_ERROR;
5704     LocalPointer<Calendar> cal(Calendar::createInstance(Locale("en@calendar=hebrew"), status), status);
5705     U_ASSERT(U_SUCCESS(status));
5706     cal->clear();
5707     cal->roll(UCAL_JULIAN_DAY, -335544321, status);
5708     assertTrue("Should return success", U_SUCCESS(status));
5709     cal->roll(UCAL_JULIAN_DAY, -1812424430, status);
5710     assertEquals("Should return U_ILLEGAL_ARGUMENT_ERROR",
5711                  U_ILLEGAL_ARGUMENT_ERROR, status);
5712 }
5713 
Test22633AMPMOverflow()5714 void CalendarTest::Test22633AMPMOverflow() {
5715     UErrorCode status = U_ZERO_ERROR;
5716     LocalPointer<Calendar> cal(Calendar::createInstance(Locale("en"), status), status);
5717     U_ASSERT(U_SUCCESS(status));
5718     cal->setTimeZone(*TimeZone::getGMT());
5719     cal->clear();
5720     // Test to set a value > limit should not cause internal overflow.
5721     cal->set(UCAL_AM_PM, 370633137);
5722     assertEquals("set large odd value for UCAL_AM_PM should be treated as PM",
5723                  12.0 * 60.0 * 60.0 *1000.0, cal->getTime(status));
5724     assertTrue("Should return success", U_SUCCESS(status));
5725 
5726     cal->set(UCAL_AM_PM, 370633138);
5727     assertEquals("set large even value for UCAL_AM_PM should be treated as AM",
5728                  0.0, cal->getTime(status));
5729     assertTrue("Should return success", U_SUCCESS(status));
5730 }
5731 
RunTestOnCalendars(void (TestFunc)(Calendar *,UCalendarDateFields))5732 void CalendarTest::RunTestOnCalendars(void(TestFunc)(Calendar*, UCalendarDateFields)) {
5733     UErrorCode status = U_ZERO_ERROR;
5734     Locale locale = Locale::getEnglish();
5735     LocalPointer<StringEnumeration> values(
5736         Calendar::getKeywordValuesForLocale("calendar", locale, false, status),
5737         status);
5738     assertTrue("Should return success", U_SUCCESS(status));
5739     if (U_FAILURE(status)) {
5740       return;
5741     }
5742     const char* value = nullptr;
5743     while ((value = values->next(nullptr, status)) != nullptr && U_SUCCESS(status)) {
5744         locale.setKeywordValue("calendar", value, status);
5745         assertTrue("Should return success", U_SUCCESS(status));
5746 
5747         LocalPointer<Calendar> cal(Calendar::createInstance(*TimeZone::getGMT(), locale, status), status);
5748         assertTrue("Should return success", U_SUCCESS(status));
5749         for (int32_t i = 0; i < UCAL_FIELD_COUNT; i++) {
5750             TestFunc(cal.getAlias(), static_cast<UCalendarDateFields>(i));
5751         }
5752     }
5753 }
5754 
5755 // This test is designed to work with undefined behavior sanitizer UBSAN to
5756 // ensure we do not have math operation overflow int32_t.
Test22633SetGetTimeOverflow()5757 void CalendarTest::Test22633SetGetTimeOverflow() {
5758     RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
5759         auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
5760             UErrorCode status = U_ZERO_ERROR;
5761             cal->clear();
5762             cal->set(field, value);
5763             cal->getTime(status);
5764         };
5765         f(cal, field, INT32_MAX);
5766         f(cal, field, INT32_MIN);
5767     });
5768 }
5769 
Test22633Set2FieldsGetTimeOverflow()5770 void CalendarTest::Test22633Set2FieldsGetTimeOverflow() {
5771     RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
5772         auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
5773             for (int32_t j = 0; j < UCAL_FIELD_COUNT; j++) {
5774                 UCalendarDateFields field2 = static_cast<UCalendarDateFields>(j);
5775                 UErrorCode status = U_ZERO_ERROR;
5776                 cal->clear();
5777                 cal->set(field, value);
5778                 cal->set(field2, value);
5779                 cal->getTime(status);
5780             }
5781         };
5782         f(cal, field, INT32_MAX);
5783         f(cal, field, INT32_MIN);
5784     });
5785 }
5786 
Test22633SetAddGetTimeOverflow()5787 void CalendarTest::Test22633SetAddGetTimeOverflow() {
5788     RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
5789         auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
5790             UErrorCode status = U_ZERO_ERROR;
5791             cal->clear();
5792             cal->set(field, value);
5793             cal->add(field, value, status);
5794             status = U_ZERO_ERROR;
5795             cal->getTime(status);
5796         };
5797         f(cal, field, INT32_MAX);
5798         f(cal, field, INT32_MIN);
5799     });
5800 }
5801 
Test22633AddTwiceGetTimeOverflow()5802 void CalendarTest::Test22633AddTwiceGetTimeOverflow() {
5803     RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
5804         auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
5805             UErrorCode status = U_ZERO_ERROR;
5806             cal->clear();
5807             cal->add(field, value, status);
5808             status = U_ZERO_ERROR;
5809             cal->add(field, value, status);
5810             status = U_ZERO_ERROR;
5811             cal->getTime(status);
5812         };
5813         f(cal, field, INT32_MAX);
5814         f(cal, field, INT32_MIN);
5815     });
5816 }
5817 
Test22633SetRollGetTimeOverflow()5818 void CalendarTest::Test22633SetRollGetTimeOverflow() {
5819     RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
5820         auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
5821             UErrorCode status = U_ZERO_ERROR;
5822             cal->clear();
5823             cal->set(field, value);
5824             cal->roll(field, value, status);
5825             status = U_ZERO_ERROR;
5826             cal->getTime(status);
5827         };
5828         f(cal, field, INT32_MAX);
5829         f(cal, field, INT32_MIN);
5830     });
5831 }
5832 
Test22633RollTwiceGetTimeOverflow()5833 void CalendarTest::Test22633RollTwiceGetTimeOverflow() {
5834     RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
5835         auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
5836             UErrorCode status = U_ZERO_ERROR;
5837             cal->clear();
5838             cal->roll(field, value, status);
5839             status = U_ZERO_ERROR;
5840             cal->roll(field, value, status);
5841             status = U_ZERO_ERROR;
5842             cal->getTime(status);
5843         };
5844         f(cal, field, INT32_MAX);
5845         f(cal, field, INT32_MIN);
5846     });
5847 }
5848 
TestChineseCalendarComputeMonthStart()5849 void CalendarTest::TestChineseCalendarComputeMonthStart() {  // ICU-22639
5850     UErrorCode status = U_ZERO_ERROR;
5851 
5852     // An extended year for which hasLeapMonthBetweenWinterSolstices is true.
5853     constexpr int32_t eyear = 4643;
5854     constexpr int64_t monthStart = 2453764;
5855 
5856     LocalPointer<Calendar> calendar(
5857         Calendar::createInstance(Locale("en_US@calendar=chinese"), status),
5858         status);
5859     if (failure(status, "Calendar::createInstance")) return;
5860 
5861     // This test case is a friend of ChineseCalendar and may access internals.
5862     const ChineseCalendar& chinese =
5863         *dynamic_cast<ChineseCalendar*>(calendar.getAlias());
5864 
5865     // The initial value of hasLeapMonthBetweenWinterSolstices should be false.
5866     assertFalse("hasLeapMonthBetweenWinterSolstices [#1]",
5867                 chinese.hasLeapMonthBetweenWinterSolstices);
5868 
5869     assertEquals("monthStart", monthStart,
5870                  chinese.handleComputeMonthStart(eyear, 0, false, status));
5871 
5872     // Calling a const method must not haved changed the state of the object.
5873     assertFalse("hasLeapMonthBetweenWinterSolstices [#2]",
5874                 chinese.hasLeapMonthBetweenWinterSolstices);
5875 }
5876 
Test22633HebrewLargeNegativeDay()5877 void CalendarTest::Test22633HebrewLargeNegativeDay() {
5878     UErrorCode status = U_ZERO_ERROR;
5879     LocalPointer<Calendar> calendar(
5880         Calendar::createInstance(Locale("en-u-ca-hebrew"), status),
5881         status);
5882     calendar->clear();
5883     calendar->set(UCAL_DAY_OF_YEAR, -2147483648);
5884     calendar->get(UCAL_HOUR, status);
5885     assertEquals("status return without hang", status, U_ILLEGAL_ARGUMENT_ERROR);
5886 }
5887 
TestAddOverflow()5888 void CalendarTest::TestAddOverflow() {
5889     UErrorCode status = U_ZERO_ERROR;
5890 
5891     LocalPointer<Calendar> calendar(
5892         Calendar::createInstance(Locale("en"), status),
5893         status);
5894     if (failure(status, "Calendar::createInstance")) return;
5895     for (int32_t i = 0; i < UCAL_FIELD_COUNT; i++) {
5896         status = U_ZERO_ERROR;
5897         calendar->setTime(0, status);
5898         calendar->add(static_cast<UCalendarDateFields>(i), INT32_MAX / 2, status);
5899         calendar->add(static_cast<UCalendarDateFields>(i), INT32_MAX, status);
5900         if ((i == UCAL_ERA) ||
5901             (i == UCAL_YEAR) ||
5902             (i == UCAL_YEAR_WOY) ||
5903             (i == UCAL_EXTENDED_YEAR) ||
5904             (i == UCAL_IS_LEAP_MONTH) ||
5905             (i == UCAL_MONTH) ||
5906             (i == UCAL_ORDINAL_MONTH) ||
5907             (i == UCAL_ZONE_OFFSET) ||
5908             (i == UCAL_DST_OFFSET)) {
5909             assertTrue("add INT32_MAX should fail", U_FAILURE(status));
5910         } else {
5911             assertTrue("add INT32_MAX should still success", U_SUCCESS(status));
5912         }
5913 
5914         status = U_ZERO_ERROR;
5915         calendar->setTime(0, status);
5916         calendar->add(static_cast<UCalendarDateFields>(i), INT32_MIN / 2, status);
5917         calendar->add(static_cast<UCalendarDateFields>(i), INT32_MIN, status);
5918         if ((i == UCAL_YEAR) ||
5919             (i == UCAL_YEAR_WOY) ||
5920             (i == UCAL_EXTENDED_YEAR) ||
5921             (i == UCAL_IS_LEAP_MONTH) ||
5922             (i == UCAL_ZONE_OFFSET) ||
5923             (i == UCAL_DST_OFFSET)) {
5924             assertTrue("add INT32_MIN should fail", U_FAILURE(status));
5925         } else {
5926             assertTrue("add INT32_MIN should still success", U_SUCCESS(status));
5927         }
5928     }
5929 }
5930 #endif /* #if !UCONFIG_NO_FORMATTING */
5931 
5932 //eof
5933