+/**
+ * TestAddRollEra0AndEraBounds, for #9226
+ */
+
+ typedef struct {
+ const char * locale;
+ UBool era0YearsGoBackwards; /* until we have API to get this, per #9393 */
+ } EraTestItem;
+
+static const EraTestItem eraTestItems[] = {
+ /* calendars with non-modern era 0 that goes backwards, max era == 1 */
+ { "en@calendar=gregorian", TRUE },
+ { "en@calendar=roc", TRUE },
+ { "en@calendar=coptic", TRUE },
+ /* calendars with non-modern era 0 that goes forwards, max era > 1 */
+ { "en@calendar=japanese", FALSE },
+ { "en@calendar=chinese", FALSE },
+ /* calendars with non-modern era 0 that goes forwards, max era == 1 */
+ { "en@calendar=ethiopic", FALSE },
+ /* calendars with only one era = 0, forwards */
+ { "en@calendar=buddhist", FALSE },
+ { "en@calendar=hebrew", FALSE },
+ { "en@calendar=islamic", FALSE },
+ { "en@calendar=indian", FALSE },
+ { "en@calendar=persian", FALSE },
+ { "en@calendar=ethiopic-amete-alem", FALSE },
+ { NULL, FALSE }
+};
+
+static const UChar zoneGMT[] = { 0x47,0x4D,0x54,0 };
+
+void TestAddRollEra0AndEraBounds() {
+ const EraTestItem * eraTestItemPtr;
+ for (eraTestItemPtr = eraTestItems; eraTestItemPtr->locale != NULL; eraTestItemPtr++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar *ucalTest = ucal_open(zoneGMT, -1, eraTestItemPtr->locale, UCAL_DEFAULT, &status);
+ if ( U_SUCCESS(status) ) {
+ int32_t yrBefore, yrAfter, yrMax, eraAfter, eraMax, eraNow;
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 2);
+ ucal_set(ucalTest, UCAL_ERA, 0);
+ yrBefore = ucal_get(ucalTest, UCAL_YEAR, &status);
+ ucal_add(ucalTest, UCAL_YEAR, 1, &status);
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 0 year 2 then add 1 year and get year for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ } else if ( (eraTestItemPtr->era0YearsGoBackwards && yrAfter>yrBefore) ||
+ (!eraTestItemPtr->era0YearsGoBackwards && yrAfter<yrBefore) ) {
+ log_err("FAIL: era 0 add 1 year does not move forward in time for %s\n", eraTestItemPtr->locale);
+ }
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 2);
+ ucal_set(ucalTest, UCAL_ERA, 0);
+ yrBefore = ucal_get(ucalTest, UCAL_YEAR, &status);
+ ucal_roll(ucalTest, UCAL_YEAR, 1, &status);
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 0 year 2 then roll 1 year and get year for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ } else if ( (eraTestItemPtr->era0YearsGoBackwards && yrAfter>yrBefore) ||
+ (!eraTestItemPtr->era0YearsGoBackwards && yrAfter<yrBefore) ) {
+ log_err("FAIL: era 0 roll 1 year does not move forward in time for %s\n", eraTestItemPtr->locale);
+ }
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 1);
+ ucal_set(ucalTest, UCAL_ERA, 0);
+ if (eraTestItemPtr->era0YearsGoBackwards) {
+ ucal_roll(ucalTest, UCAL_YEAR, 1, &status); /* roll forward in time to era 0 boundary */
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ eraAfter = ucal_get(ucalTest, UCAL_ERA, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 0 year 1 then roll 1 year and get year,era for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ /* all calendars with era0YearsGoBackwards have "unbounded" era0 year values, so we should pin at yr 1 */
+ } else if (eraAfter != 0 || yrAfter != 1) {
+ log_err("FAIL: era 0 roll 1 year from year 1 does not stay within era or pin to year 1 for %s (get era %d year %d)\n",
+ eraTestItemPtr->locale, eraAfter, yrAfter);
+ }
+ } else {
+ /* roll backward in time to where era 0 years go negative, except for the Chinese
+ calendar, which uses negative eras instead of having years outside the range 1-60 */
+ const char * calType = ucal_getType(ucalTest, &status);
+ ucal_roll(ucalTest, UCAL_YEAR, -2, &status);
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ eraAfter = ucal_get(ucalTest, UCAL_ERA, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 0 year 1 then roll -2 years and get year,era for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ } else if ( uprv_strcmp(calType,"chinese")!=0 && (eraAfter != 0 || yrAfter != -1) ) {
+ log_err("FAIL: era 0 roll -2 years from year 1 does not stay within era or produce year -1 for %s (get era %d year %d)\n",
+ eraTestItemPtr->locale, eraAfter, yrAfter);
+ }
+ }
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ {
+ int32_t eraMin = ucal_getLimit(ucalTest, UCAL_ERA, UCAL_MINIMUM, &status);
+ const char * calType = ucal_getType(ucalTest, &status);
+ if (eraMin != 0 && uprv_strcmp(calType, "chinese") != 0) {
+ log_err("FAIL: ucal_getLimit returns minimum era %d (should be 0) for calType %s, error %s\n", eraMin, calType, u_errorName(status));
+ }
+ }
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 1);
+ ucal_set(ucalTest, UCAL_ERA, 0);
+ eraMax = ucal_getLimit(ucalTest, UCAL_ERA, UCAL_MAXIMUM, &status);
+ if ( U_SUCCESS(status) && eraMax > 0 ) {
+ /* try similar tests for era 1 (if calendar has it), in which years always go forward */
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 2);
+ ucal_set(ucalTest, UCAL_ERA, 1);
+ yrBefore = ucal_get(ucalTest, UCAL_YEAR, &status);
+ ucal_add(ucalTest, UCAL_YEAR, 1, &status);
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 1 year 2 then add 1 year and get year for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ } else if ( yrAfter<yrBefore ) {
+ log_err("FAIL: era 1 add 1 year does not move forward in time for %s\n", eraTestItemPtr->locale);
+ }
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 2);
+ ucal_set(ucalTest, UCAL_ERA, 1);
+ yrBefore = ucal_get(ucalTest, UCAL_YEAR, &status);
+ ucal_roll(ucalTest, UCAL_YEAR, 1, &status);
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 1 year 2 then roll 1 year and get year for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ } else if ( yrAfter<yrBefore ) {
+ log_err("FAIL: era 1 roll 1 year does not move forward in time for %s\n", eraTestItemPtr->locale);
+ }
+
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 1);
+ ucal_set(ucalTest, UCAL_ERA, 1);
+ yrMax = ucal_getLimit(ucalTest, UCAL_YEAR, UCAL_ACTUAL_MAXIMUM, &status); /* max year value for era 1 */
+ ucal_roll(ucalTest, UCAL_YEAR, -1, &status); /* roll down which should pin or wrap to end */
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ eraAfter = ucal_get(ucalTest, UCAL_ERA, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era 1 year 1 then roll -1 year and get year,era for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ /* if yrMax is reasonable we should wrap to that, else we should pin at yr 1 */
+ } else if (yrMax >= 32768) {
+ if (eraAfter != 1 || yrAfter != 1) {
+ log_err("FAIL: era 1 roll -1 year from year 1 does not stay within era or pin to year 1 for %s (get era %d year %d)\n",
+ eraTestItemPtr->locale, eraAfter, yrAfter);
+ }
+ } else if (eraAfter != 1 || yrAfter != yrMax) {
+ log_err("FAIL: era 1 roll -1 year from year 1 does not stay within era or wrap to year %d for %s (get era %d year %d)\n",
+ yrMax, eraTestItemPtr->locale, eraAfter, yrAfter);
+ } else {
+ /* now roll up which should wrap to beginning */
+ ucal_roll(ucalTest, UCAL_YEAR, 1, &status); /* now roll up which should wrap to beginning */
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ eraAfter = ucal_get(ucalTest, UCAL_ERA, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: era 1 roll 1 year from end and get year,era for %s, error %s\n",
+ eraTestItemPtr->locale, u_errorName(status));
+ } else if (eraAfter != 1 || yrAfter != 1) {
+ log_err("FAIL: era 1 roll 1 year from year %d does not stay within era or wrap to year 1 for %s (get era %d year %d)\n",
+ yrMax, eraTestItemPtr->locale, eraAfter, yrAfter);
+ }
+ }
+
+ /* if current era > 1, try the same roll tests for current era */
+ ucal_setMillis(ucalTest, ucal_getNow(), &status);
+ eraNow = ucal_get(ucalTest, UCAL_ERA, &status);
+ if ( U_SUCCESS(status) && eraNow > 1 ) {
+ status = U_ZERO_ERROR;
+ ucal_clear(ucalTest);
+ ucal_set(ucalTest, UCAL_YEAR, 1);
+ ucal_set(ucalTest, UCAL_ERA, eraNow);
+ yrMax = ucal_getLimit(ucalTest, UCAL_YEAR, UCAL_ACTUAL_MAXIMUM, &status); /* max year value for this era */
+ ucal_roll(ucalTest, UCAL_YEAR, -1, &status);
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ eraAfter = ucal_get(ucalTest, UCAL_ERA, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: set era %d year 1 then roll -1 year and get year,era for %s, error %s\n",
+ eraNow, eraTestItemPtr->locale, u_errorName(status));
+ /* if yrMax is reasonable we should wrap to that, else we should pin at yr 1 */
+ } else if (yrMax >= 32768) {
+ if (eraAfter != eraNow || yrAfter != 1) {
+ log_err("FAIL: era %d roll -1 year from year 1 does not stay within era or pin to year 1 for %s (get era %d year %d)\n",
+ eraNow, eraTestItemPtr->locale, eraAfter, yrAfter);
+ }
+ } else if (eraAfter != eraNow || yrAfter != yrMax) {
+ log_err("FAIL: era %d roll -1 year from year 1 does not stay within era or wrap to year %d for %s (get era %d year %d)\n",
+ eraNow, yrMax, eraTestItemPtr->locale, eraAfter, yrAfter);
+ } else {
+ /* now roll up which should wrap to beginning */
+ ucal_roll(ucalTest, UCAL_YEAR, 1, &status); /* now roll up which should wrap to beginning */
+ yrAfter = ucal_get(ucalTest, UCAL_YEAR, &status);
+ eraAfter = ucal_get(ucalTest, UCAL_ERA, &status);
+ if (U_FAILURE(status)) {
+ log_err("FAIL: era %d roll 1 year from end and get year,era for %s, error %s\n",
+ eraNow, eraTestItemPtr->locale, u_errorName(status));
+ } else if (eraAfter != eraNow || yrAfter != 1) {
+ log_err("FAIL: era %d roll 1 year from year %d does not stay within era or wrap to year 1 for %s (get era %d year %d)\n",
+ eraNow, yrMax, eraTestItemPtr->locale, eraAfter, yrAfter);
+ }
+ }
+ }
+ }
+
+ ucal_close(ucalTest);
+ } else {
+ log_data_err("FAIL: ucal_open fails for zone GMT, locale %s, UCAL_DEFAULT\n", eraTestItemPtr->locale);
+ }
+ }
+}
+
+/**
+ * TestGetTZTransition, for #9606
+ */
+
+typedef struct {
+ const char *descrip; /* test description */
+ const UChar * zoneName; /* pointer to zero-terminated zone name */
+ int32_t year; /* starting point for test is gregorian calendar noon on day specified by y,M,d here */
+ int32_t month;
+ int32_t day;
+ UBool hasPrev; /* does it have a previous transition from starting point? If so we test inclusive from that */
+ UBool hasNext; /* does it have a next transition from starting point? If so we test inclusive from that */
+} TZTransitionItem;
+
+/* have zoneGMT above */
+static const UChar zoneUSPacific[] = { 0x55,0x53,0x2F,0x50,0x61,0x63,0x69,0x66,0x69,0x63,0 }; /* "US/Pacific" */
+static const UChar zoneCairo[] = { 0x41,0x66,0x72,0x69,0x63,0x61,0x2F,0x43,0x61,0x69,0x72,0x6F,0 }; /* "Africa/Cairo", DST cancelled since 2011 */
+static const UChar zoneIceland[] = { 0x41,0x74,0x6C,0x61,0x6E,0x74,0x69,0x63,0x2F,0x52,0x65,0x79,0x6B,0x6A,0x61,0x76,0x69,0x6B,0 }; /* "Atlantic/Reykjavik", always on DST (since when?) */
+
+static const TZTransitionItem tzTransitionItems[] = {
+ { "USPacific mid 2012", zoneUSPacific, 2012, UCAL_JULY, 1, TRUE , TRUE },
+ { "USPacific mid 100", zoneUSPacific, 100, UCAL_JULY, 1, FALSE, TRUE }, /* no transitions before 100 CE... */
+ { "Cairo mid 2012", zoneCairo, 2012, UCAL_JULY, 1, TRUE , TRUE }, /* DST cancelled since 2011 (Changed since 2014c) */
+ { "Iceland mid 2012", zoneIceland, 2012, UCAL_JULY, 1, TRUE , FALSE }, /* always on DST */
+ { NULL, NULL, 0, 0, 0, FALSE, FALSE } /* terminator */
+};
+
+void TestGetTZTransition() {
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar * ucal = ucal_open(zoneGMT, -1, "en", UCAL_GREGORIAN, &status);
+ if ( U_SUCCESS(status) ) {
+ const TZTransitionItem * itemPtr;
+ for (itemPtr = tzTransitionItems; itemPtr->descrip != NULL; itemPtr++) {
+ UDate curMillis;
+ ucal_setTimeZone(ucal, itemPtr->zoneName, -1, &status);
+ ucal_setDateTime(ucal, itemPtr->year, itemPtr->month, itemPtr->day, 12, 0, 0, &status);
+ curMillis = ucal_getMillis(ucal, &status);
+ (void)curMillis; /* Suppress set but not used warning. */
+ if ( U_SUCCESS(status) ) {
+ UDate transition1, transition2;
+ UBool result;
+
+ result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_PREVIOUS, &transition1, &status);
+ if (U_FAILURE(status) || result != itemPtr->hasPrev) {
+ log_data_err("FAIL: %s ucal_getTimeZoneTransitionDate prev status %s, expected result %d but got %d\n",
+ itemPtr->descrip, u_errorName(status), itemPtr->hasPrev, result);
+ } else if (result) {
+ ucal_setMillis(ucal, transition1, &status);
+ result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE, &transition2, &status);
+ if (U_FAILURE(status) || !result || transition2 != transition1) {
+ log_err("FAIL: %s ucal_getTimeZoneTransitionDate prev_inc status %s, result %d, expected date %.1f but got %.1f\n",
+ itemPtr->descrip, u_errorName(status), result, transition1, transition2);
+ }
+ }
+ status = U_ZERO_ERROR;
+
+ result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_NEXT, &transition1, &status);
+ if (U_FAILURE(status) || result != itemPtr->hasNext) {
+ log_data_err("FAIL: %s ucal_getTimeZoneTransitionDate next status %s, expected result %d but got %d\n",
+ itemPtr->descrip, u_errorName(status), itemPtr->hasNext, result);
+ } else if (result) {
+ ucal_setMillis(ucal, transition1, &status);
+ result = ucal_getTimeZoneTransitionDate(ucal, UCAL_TZ_TRANSITION_NEXT_INCLUSIVE, &transition2, &status);
+ if (U_FAILURE(status) || !result || transition2 != transition1) {
+ log_err("FAIL: %s ucal_getTimeZoneTransitionDate next_inc status %s, result %d, expected date %.1f but got %.1f\n",
+ itemPtr->descrip, u_errorName(status), result, transition1, transition2);
+ }
+ }
+ status = U_ZERO_ERROR;
+ } else {
+ log_data_err("FAIL setup: can't setup calendar for %s, status %s\n",
+ itemPtr->descrip, u_errorName(status));
+ status = U_ZERO_ERROR;
+ }
+ }
+ ucal_close(ucal);
+ } else {
+ log_data_err("FAIL setup: ucal_open status %s\n", u_errorName(status));
+ }
+}
+
+static const UChar winEastern[] = /* Eastern Standard Time */
+ {0x45,0x61,0x73,0x74,0x65,0x72,0x6E,0x20,0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x54,0x69,0x6D,0x65,0x00};
+
+static const UChar tzNewYork[] = /* America/New_York */
+ {0x41,0x6D,0x65,0x72,0x69,0x63,0x61,0x2F,0x4E,0x65,0x77,0x5F,0x59,0x6F,0x72,0x6B,0x00};
+static const UChar tzTronto[] = /* America/Toronto */
+ {0x41,0x6D,0x65,0x72,0x69,0x63,0x61,0x2F,0x54,0x6F,0x72,0x6F,0x6E,0x74,0x6F,0x00};
+
+static const UChar sBogus[] = /* Bogus */
+ {0x42,0x6F,0x67,0x75,0x73,0x00};
+
+#ifndef U_DEBUG
+static const UChar sBogusWithVariantCharacters[] = /* Bogus with Variant characters: Hèℓℓô Wôřℓδ */
+ {0x48,0xE8,0x2113,0x2113,0xF4,0x20,0x57,0xF4,0x159,0x2113,0x3B4,0x00};
+#endif
+
+void TestGetWindowsTimeZoneID() {
+ UErrorCode status;
+ UChar winID[64];
+ int32_t len;
+
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getWindowsTimeZoneID(tzNewYork, u_strlen(tzNewYork), winID, UPRV_LENGTHOF(winID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: Windows ID for America/New_York, status %s\n", u_errorName(status));
+ } else if (len != u_strlen(winEastern) || u_strncmp(winID, winEastern, len) != 0) {
+ log_data_err("FAIL: Windows ID for America/New_York\n");
+ }
+ }
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getWindowsTimeZoneID(tzTronto, u_strlen(tzTronto), winID, UPRV_LENGTHOF(winID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: Windows ID for America/Toronto, status %s\n", u_errorName(status));
+ } else if (len != u_strlen(winEastern) || u_strncmp(winID, winEastern, len) != 0) {
+ log_data_err("FAIL: Windows ID for America/Toronto\n");
+ }
+ }
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getWindowsTimeZoneID(sBogus, u_strlen(sBogus), winID, UPRV_LENGTHOF(winID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: Windows ID for Bogus, status %s\n", u_errorName(status));
+ } else if (len != 0) {
+ log_data_err("FAIL: Windows ID for Bogus\n");
+ }
+ }
+}
+
+void TestGetTimeZoneIDByWindowsID() {
+ UErrorCode status;
+ UChar tzID[64];
+ int32_t len;
+
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getTimeZoneIDForWindowsID(winEastern, -1, NULL, tzID, UPRV_LENGTHOF(tzID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: TZ ID for Eastern Standard Time, status %s\n", u_errorName(status));
+ } else if (len != u_strlen(tzNewYork) || u_strncmp(tzID, tzNewYork, len) != 0) {
+ log_err("FAIL: TZ ID for Eastern Standard Time\n");
+ }
+ }
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getTimeZoneIDForWindowsID(winEastern, u_strlen(winEastern), "US", tzID, UPRV_LENGTHOF(tzID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: TZ ID for Eastern Standard Time - US, status %s\n", u_errorName(status));
+ } else if (len != u_strlen(tzNewYork) || u_strncmp(tzID, tzNewYork, len) != 0) {
+ log_err("FAIL: TZ ID for Eastern Standard Time - US\n");
+ }
+ }
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getTimeZoneIDForWindowsID(winEastern, u_strlen(winEastern), "CA", tzID, UPRV_LENGTHOF(tzID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: TZ ID for Eastern Standard Time - CA, status %s\n", u_errorName(status));
+ } else if (len != u_strlen(tzTronto) || u_strncmp(tzID, tzTronto, len) != 0) {
+ log_err("FAIL: TZ ID for Eastern Standard Time - CA\n");
+ }
+ }
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getTimeZoneIDForWindowsID(sBogus, -1, NULL, tzID, UPRV_LENGTHOF(tzID), &status);
+ if (U_FAILURE(status)) {
+ log_data_err("FAIL: TZ ID for Bogus, status %s\n", u_errorName(status));
+ } else if (len != 0) {
+ log_err("FAIL: TZ ID for Bogus\n");
+ }
+ }
+#ifndef U_DEBUG
+ // This test is only for release mode because it will cause an assertion failure in debug builds.
+ // We don't check the API result for errors as the only purpose of this test is to ensure that
+ // input variant characters don't cause abort() to be called and/or that ICU doesn't crash.
+ {
+ status = U_ZERO_ERROR;
+ len = ucal_getTimeZoneIDForWindowsID(sBogusWithVariantCharacters, -1, NULL, tzID, UPRV_LENGTHOF(tzID), &status);
+ }
+#endif
+}
+
+// The following currently assumes that Reiwa is the last known/valid era.
+// Filed ICU-20551 to generalize this when we have more time...
+void TestJpnCalAddSetNextEra() {
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar *jCal = ucal_open(NULL, 0, "ja_JP@calendar=japanese", UCAL_DEFAULT, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("FAIL: ucal_open for ja_JP@calendar=japanese, status %s\n", u_errorName(status));
+ } else {
+ ucal_clear(jCal); // This sets to 1970, in Showa
+ int32_t sEra = ucal_get(jCal, UCAL_ERA, &status); // Don't assume era number for Showa
+ if ( U_FAILURE(status) ) {
+ log_data_err("FAIL: ucal_get ERA for Showa, status %s\n", u_errorName(status));
+ } else {
+ int32_t iEra, eYear;
+ int32_t startYears[4] = { 1926, 1989, 2019, 0 }; // start years for Showa, Heisei, Reiwa; 0 marks invalid era
+ for (iEra = 1; iEra < 4; iEra++) {
+ status = U_ZERO_ERROR;
+ ucal_clear(jCal);
+ ucal_set(jCal, UCAL_ERA, sEra+iEra);
+ eYear = ucal_get(jCal, UCAL_EXTENDED_YEAR, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: set %d, ucal_get EXTENDED_YEAR, status %s\n", iEra, u_errorName(status));
+ } else if (startYears[iEra] == 0) { // Apple-specific section, for iEra==3
+ // invalid era, start should be in the far future with non-negative millis
+ if (eYear < 10000) {
+ log_err("ERROR: set %d, invalid era should have faraway start year, but get %d\n", iEra, eYear);
+ }
+ UDate date = ucal_getMillis(jCal, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: set %d, ucal_getMillis, status %s\n", iEra, u_errorName(status));
+ } else if (date < 0) {
+ log_err("ERROR: set %d, ucal_getMillis should be positive, but get %.1f\n", iEra, date);
+ }
+ } else if (eYear != startYears[iEra]) {
+ log_err("ERROR: set %d, expected start year %d but get %d\n", iEra, startYears[iEra], eYear);
+ } else {
+ ucal_add(jCal, UCAL_ERA, 1, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: set %d, ucal_add ERA 1, status %s\n", iEra, u_errorName(status));
+ } else {
+ eYear = ucal_get(jCal, UCAL_EXTENDED_YEAR, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: set %d then add ERA 1, ucal_get EXTENDED_YEAR, status %s\n", iEra, u_errorName(status));
+ } else {
+ // If this is the last valid era, we expect adding an era to pin to the current era
+ int32_t nextEraStart = (startYears[iEra+1] == 0)? startYears[iEra]: startYears[iEra+1];
+ if (eYear != nextEraStart) {
+ log_err("ERROR: set %d then add ERA 1, expected start year %d but get %d\n", iEra, nextEraStart, eYear);
+ }
+ }
+ }
+ }
+ }
+ }
+ ucal_close(jCal);
+ }
+}
+
+typedef struct {
+ const char * localeWithCal;
+ UDate clearDate;
+} LocaleWithCalendarAndClearDate;
+
+static const LocaleWithCalendarAndClearDate calAndClearDates[] = {
+// ucal_clear sets era grego
+// this date for GMT in cal date
+ { "en@calendar=gregorian", 0.0 }, // 1 1970-01-01
+ { "en@calendar=iso8601", 0.0 }, // 1 1970-01-01
+ { "en@calendar=buddhist", 0.0 }, // 0 1970-01-01
+ { "en@calendar=japanese", 0.0 }, // 234 1970-01-01
+ { "en@calendar=roc", 0.0 }, // 1 1970-01-01
+ { "en@calendar=chinese", 444528000000.0 }, // 78 1984-02-02
+ { "en@calendar=dangi", 444528000000.0 }, // 78 1984-02-02
+ { "en@calendar=coptic", -53184211200000.0 }, // 1 284-08-29
+ { "en@calendar=ethiopic", -61894108800000.0 }, // 1 8-08-29
+ { "en@calendar=ethiopic-amete-alem", -61894108800000.0 }, // 0 8-08-29
+ { "en@calendar=hebrew", -180799776000000.0 }, // 0 3761-10-07
+ { "en@calendar=indian", -59667235200000.0 }, // 0 79-03-24
+ { "en@calendar=islamic", -42521673600000.0 }, // 0 622-07-15
+ { "en@calendar=islamic-civil", -42521587200000.0 }, // 0 622-07-16
+ { "en@calendar=islamic-tbla", -42521673600000.0 }, // 0 622-07-15
+ { "en@calendar=islamic-umalqura", -42521587200000.0 }, // 0 622-07-16
+ { "en@calendar=persian", -42531955200000.0 }, // 0 622-03-18
+ { NULL, 0.0 }
+};
+
+void TestClear() {
+ const LocaleWithCalendarAndClearDate * calAndClearDatesPtr;
+ for (calAndClearDatesPtr = calAndClearDates; calAndClearDatesPtr->localeWithCal != NULL; calAndClearDatesPtr++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar * ucal = ucal_open(zoneGMT, -1, calAndClearDatesPtr->localeWithCal, UCAL_DEFAULT, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("FAIL: ucal_open for locale %s, status %s\n", calAndClearDatesPtr->localeWithCal, u_errorName(status));
+ } else {
+ UDate date;
+ ucal_clear(ucal);
+ date = ucal_getMillis(ucal, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: ucal_clear, ucal_getMillis for locale %s, status %s\n", calAndClearDatesPtr->localeWithCal, u_errorName(status));
+ } else if (date != calAndClearDatesPtr->clearDate) {
+ log_err("FAIL: ucal_clear, ucal_getMillis for locale %s, expected %.1f, got %.1f\n", calAndClearDatesPtr->localeWithCal, calAndClearDatesPtr->clearDate, date);
+ }
+ ucal_close(ucal);
+ }
+ }
+}
+
+void TestPersianCalOverflow() {
+ const char * locale = "bs_Cyrl@calendar=persian";
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar * ucal = ucal_open(NULL, 0, locale, UCAL_DEFAULT, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("FAIL: ucal_open for locale %s, status %s\n", locale, u_errorName(status));
+ } else {
+ int32_t maxMonth = ucal_getLimit(ucal, UCAL_MONTH, UCAL_MAXIMUM, &status);
+ int32_t maxDayOfMonth = ucal_getLimit(ucal, UCAL_DATE, UCAL_MAXIMUM, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: ucal_getLimit MONTH/DATE for locale %s, status %s\n", locale, u_errorName(status));
+ } else {
+ int32_t jd, month, dayOfMonth;
+ for (jd = 67023580; jd <= 67023584; jd++) { // year 178171, int32_t overflow if jd >= 67023582
+ status = U_ZERO_ERROR;
+ ucal_clear(ucal);
+ ucal_set(ucal, UCAL_JULIAN_DAY, jd);
+ month = ucal_get(ucal, UCAL_MONTH, &status);
+ dayOfMonth = ucal_get(ucal, UCAL_DATE, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: ucal_get MONTH/DATE for locale %s, julianDay %d, status %s\n", locale, jd, u_errorName(status));
+ } else if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
+ log_err("FAIL: locale %s, julianDay %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d\n",
+ locale, jd, maxMonth, month, maxDayOfMonth, dayOfMonth);
+ }
+ }
+ }
+ ucal_close(ucal);
+ }
+}
+
+/* Apple-specific */
+typedef struct {
+ const char * locale;
+ UBool formatStyle;
+ UADayPeriod expected[12]; // expected results for 0..22 in 2-hour increments
+} DayPeriodTestItem;
+
+static const DayPeriodTestItem dpItems[] = {
+ { "en", FALSE, { UADAYPERIOD_NIGHT1, UADAYPERIOD_NIGHT1, UADAYPERIOD_NIGHT1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING1,
+ UADAYPERIOD_AFTERNOON1, UADAYPERIOD_AFTERNOON1, UADAYPERIOD_AFTERNOON1, UADAYPERIOD_EVENING1, UADAYPERIOD_EVENING1, UADAYPERIOD_NIGHT1 } },
+ { "en", TRUE, { UADAYPERIOD_MIDNIGHT, UADAYPERIOD_NIGHT1, UADAYPERIOD_NIGHT1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING1,
+ UADAYPERIOD_NOON, UADAYPERIOD_AFTERNOON1, UADAYPERIOD_AFTERNOON1, UADAYPERIOD_EVENING1, UADAYPERIOD_EVENING1, UADAYPERIOD_NIGHT1 } },
+ { "ta", FALSE, { UADAYPERIOD_NIGHT1, UADAYPERIOD_NIGHT1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING2, UADAYPERIOD_MORNING2, UADAYPERIOD_MORNING2,
+ UADAYPERIOD_AFTERNOON1, UADAYPERIOD_AFTERNOON2, UADAYPERIOD_EVENING1, UADAYPERIOD_EVENING2, UADAYPERIOD_EVENING2, UADAYPERIOD_NIGHT1 } },
+ // test fallback for languages with no data. Should be to root, but that is broken in the data, so to en for now.
+ { "tlh", FALSE, { UADAYPERIOD_NIGHT1, UADAYPERIOD_NIGHT1, UADAYPERIOD_NIGHT1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING1, UADAYPERIOD_MORNING1,
+ UADAYPERIOD_AFTERNOON1, UADAYPERIOD_AFTERNOON1, UADAYPERIOD_AFTERNOON1, UADAYPERIOD_EVENING1, UADAYPERIOD_EVENING1, UADAYPERIOD_NIGHT1 } },
+ { NULL, FALSE, { 0 } }
+};
+
+void TestGetDayPeriods() {
+ static const DayPeriodTestItem * dpItemPtr;
+ for (dpItemPtr = dpItems; dpItemPtr->locale != NULL; dpItemPtr++) {
+ int32_t hourIndex;
+ for (hourIndex = 0; hourIndex < 12; hourIndex++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UADayPeriod dp = uacal_getDayPeriod(dpItemPtr->locale, hourIndex*2, 0, dpItemPtr->formatStyle, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: uacal_getDayPeriod, locale %s, hour %d, formatStyle %d, status %s\n",
+ dpItemPtr->locale, hourIndex*2, dpItemPtr->formatStyle, u_errorName(status));
+ } else if (dp != dpItemPtr->expected[hourIndex]) {
+ log_err("FAIL: uacal_getDayPeriod, locale %s, hour %d, formatStyle %d, expected dp %d, got %d\n",
+ dpItemPtr->locale, hourIndex*2, dpItemPtr->formatStyle, dpItemPtr->expected[hourIndex], dp);
+ }
+ }
+ }
+}
+