void CalendarLimitTest::TestLimits(void) { static const UDate DEFAULT_START = 944006400000.0; // 1999-12-01T00:00Z static const int32_t DEFAULT_END = -120; // Default for non-quick is run 2 minutes static const struct { const char *type; UBool hasLeapMonth; UDate actualTestStart; int32_t actualTestEnd; } TestCases[] = { {"gregorian", FALSE, DEFAULT_START, DEFAULT_END}, {"japanese", FALSE, 596937600000.0, DEFAULT_END}, // 1988-12-01T00:00Z, Showa 63 {"buddhist", FALSE, DEFAULT_START, DEFAULT_END}, {"roc", FALSE, DEFAULT_START, DEFAULT_END}, {"persian", FALSE, DEFAULT_START, DEFAULT_END}, {"islamic-civil", FALSE, DEFAULT_START, DEFAULT_END}, {"islamic", FALSE, DEFAULT_START, 800000}, // Approx. 2250 years from now, after which some rounding errors occur in Islamic calendar {"hebrew", TRUE, DEFAULT_START, DEFAULT_END}, {"chinese", TRUE, DEFAULT_START, DEFAULT_END}, {"dangi", TRUE, DEFAULT_START, DEFAULT_END}, {"indian", FALSE, DEFAULT_START, DEFAULT_END}, {"coptic", FALSE, DEFAULT_START, DEFAULT_END}, {"ethiopic", FALSE, DEFAULT_START, DEFAULT_END}, {"ethiopic-amete-alem", FALSE, DEFAULT_START, DEFAULT_END}, {NULL, FALSE, 0, 0} }; int16_t i = 0; char buf[64]; for (i = 0; TestCases[i].type; i++) { UErrorCode status = U_ZERO_ERROR; uprv_strcpy(buf, "root@calendar="); strcat(buf, TestCases[i].type); Calendar *cal = Calendar::createInstance(buf, status); if (failure(status, "Calendar::createInstance", TRUE)) { continue; } if (uprv_strcmp(cal->getType(), TestCases[i].type) != 0) { errln((UnicodeString)"FAIL: Wrong calendar type: " + cal->getType() + " Requested: " + TestCases[i].type); delete cal; continue; } // Do the test doTheoreticalLimitsTest(*cal, TestCases[i].hasLeapMonth); doLimitsTest(*cal, TestCases[i].actualTestStart,TestCases[i].actualTestEnd); delete cal; } }
/** * Test various API methods for API completeness. */ void IntlCalendarTest::TestTypes() { Calendar *c = NULL; UErrorCode status = U_ZERO_ERROR; int j; const char *locs [40] = { "en_US_VALLEYGIRL", "en_US_VALLEYGIRL@collation=phonebook;calendar=japanese", "en_US_VALLEYGIRL@collation=phonebook;calendar=gregorian", "ja_JP@calendar=japanese", "th_TH@calendar=buddhist", "ja_JP_TRADITIONAL", "th_TH_TRADITIONAL", "th_TH_TRADITIONAL@calendar=gregorian", "en_US", "th_TH", // Default calendar for th_TH is buddhist "th", // th's default region is TH and buddhist is used as default for TH "en_TH", // Default calendar for any locales with region TH is buddhist "en-TH-u-ca-gregory", NULL }; const char *types[40] = { "gregorian", "japanese", "gregorian", "japanese", "buddhist", "japanese", "buddhist", "gregorian", // android-changed. "buddhist", "gregorian", // android-changed. "buddhist", "gregorian", // android-changed. "buddhist", "gregorian", // android-changed. "buddhist", "gregorian", // android-changed. "buddhist", "gregorian", NULL }; for(j=0;locs[j];j++) { logln(UnicodeString("Creating calendar of locale ") + locs[j]); status = U_ZERO_ERROR; c = Calendar::createInstance(locs[j], status); CHECK(status, "creating '" + UnicodeString(locs[j]) + "' calendar"); if(U_SUCCESS(status)) { logln(UnicodeString(" type is ") + c->getType()); if(strcmp(c->getType(), types[j])) { dataerrln(UnicodeString(locs[j]) + UnicodeString("Calendar type ") + c->getType() + " instead of " + types[j]); } } delete c; } }
void CalendarLimitTest::TestLimits(void) { static const UDate DEFAULT_START = 944006400000.0; // 1999-12-01T00:00Z static const struct { const char *type; UBool hasLeapMonth; UDate actualTestStart; } TestCases[] = { {"gregorian", FALSE, DEFAULT_START}, {"japanese", FALSE, 596937600000.0}, // 1988-12-01T00:00Z, Showa 63 {"buddhist", FALSE, DEFAULT_START}, {"roc", FALSE, DEFAULT_START}, {"persian", FALSE, DEFAULT_START}, {"islamic-civil", FALSE, DEFAULT_START}, //{"islamic", FALSE, DEFAULT_START}, // TODO: there is a bug in monthlength calculation {"hebrew", TRUE, DEFAULT_START}, {"chinese", TRUE, DEFAULT_START}, {"indian", FALSE, DEFAULT_START}, {"coptic", FALSE, DEFAULT_START}, {"ethiopic", FALSE, DEFAULT_START}, {"ethiopic-amete-alem", FALSE, DEFAULT_START}, {NULL, FALSE, 0.0} }; int16_t i = 0; char buf[64]; for (i = 0; TestCases[i].type; i++) { UErrorCode status = U_ZERO_ERROR; uprv_strcpy(buf, "root@calendar="); strcat(buf, TestCases[i].type); Calendar *cal = Calendar::createInstance(buf, status); if (failure(status, "Calendar::createInstance")) { continue; } if (uprv_strcmp(cal->getType(), TestCases[i].type) != 0) { errln((UnicodeString)"FAIL: Wrong calendar type: " + cal->getType() + " Requested: " + TestCases[i].type); delete cal; continue; } // Do the test doTheoreticalLimitsTest(*cal, TestCases[i].hasLeapMonth); doLimitsTest(*cal, TestCases[i].actualTestStart); delete cal; } }
/** * Run a test of a quasi-Gregorian calendar. This is a calendar * that behaves like a Gregorian but has different year/era mappings. * The int[] data array should have the format: * * { era, year, gregorianYear, month, dayOfMonth, ... ... , -1 } */ void IntlCalendarTest::quasiGregorianTest(Calendar& cal, const Locale& gcl, const int32_t *data) { UErrorCode status = U_ZERO_ERROR; // As of JDK 1.4.1_01, using the Sun JDK GregorianCalendar as // a reference throws us off by one hour. This is most likely // due to the JDK 1.4 incorporation of historical time zones. //java.util.Calendar grego = java.util.Calendar.getInstance(); Calendar *grego = Calendar::createInstance(gcl, status); if (U_FAILURE(status)) { dataerrln("Error calling Calendar::createInstance"); return; } int32_t tz1 = cal.get(UCAL_ZONE_OFFSET,status); int32_t tz2 = grego -> get (UCAL_ZONE_OFFSET, status); if(tz1 != tz2) { errln((UnicodeString)"cal's tz " + tz1 + " != grego's tz " + tz2); } for (int32_t i=0; data[i]!=-1; ) { int32_t era = data[i++]; int32_t year = data[i++]; int32_t gregorianYear = data[i++]; int32_t month = data[i++]; int32_t dayOfMonth = data[i++]; grego->clear(); grego->set(gregorianYear, month, dayOfMonth); UDate D = grego->getTime(status); cal.clear(); cal.set(UCAL_ERA, era); cal.set(year, month, dayOfMonth); UDate d = cal.getTime(status); #ifdef U_DEBUG_DUMPCALS logln((UnicodeString)"cal : " + CalendarTest::calToStr(cal)); logln((UnicodeString)"grego: " + CalendarTest::calToStr(*grego)); #endif if (d == D) { logln(UnicodeString("OK: ") + era + ":" + year + "/" + (month+1) + "/" + dayOfMonth + " => " + d + " (" + UnicodeString(cal.getType()) + ")"); } else { errln(UnicodeString("Fail: (fields to millis)") + era + ":" + year + "/" + (month+1) + "/" + dayOfMonth + " => " + d + ", expected " + D + " (" + UnicodeString(cal.getType()) + "Off by: " + (d-D)); } // Now, set the gregorian millis on the other calendar cal.clear(); cal.setTime(D, status); int e = cal.get(UCAL_ERA, status); int y = cal.get(UCAL_YEAR, status); #ifdef U_DEBUG_DUMPCALS logln((UnicodeString)"cal : " + CalendarTest::calToStr(cal)); logln((UnicodeString)"grego: " + CalendarTest::calToStr(*grego)); #endif if (y == year && e == era) { logln((UnicodeString)"OK: " + D + " => " + cal.get(UCAL_ERA, status) + ":" + cal.get(UCAL_YEAR, status) + "/" + (cal.get(UCAL_MONTH, status)+1) + "/" + cal.get(UCAL_DATE, status) + " (" + UnicodeString(cal.getType()) + ")"); } else { errln((UnicodeString)"Fail: (millis to fields)" + D + " => " + cal.get(UCAL_ERA, status) + ":" + cal.get(UCAL_YEAR, status) + "/" + (cal.get(UCAL_MONTH, status)+1) + "/" + cal.get(UCAL_DATE, status) + ", expected " + era + ":" + year + "/" + (month+1) + "/" + dayOfMonth + " (" + UnicodeString(cal.getType())); } } delete grego; CHECK(status, "err during quasiGregorianTest()"); }
void CalendarLimitTest::doLimitsTest(Calendar& cal, const int32_t* fieldsToTest, UDate startDate, int32_t testDuration) { static const int32_t FIELDS[] = { UCAL_ERA, UCAL_YEAR, UCAL_MONTH, UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_MONTH, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, -1, }; static const char* FIELD_NAME[] = { "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH", "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK", "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY", "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET", "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR", "JULIAN_DAY", "MILLISECONDS_IN_DAY", "IS_LEAP_MONTH" }; UErrorCode status = U_ZERO_ERROR; int32_t i, j; UnicodeString ymd; GregorianCalendar greg(status); if (failure(status, "new GregorianCalendar")) { return; } greg.setTime(startDate, status); if (failure(status, "GregorianCalendar::setTime")) { return; } logln((UnicodeString)"Start: " + startDate); if (fieldsToTest == NULL) { fieldsToTest = FIELDS; } // Keep a record of minima and maxima that we actually see. // These are kept in an array of arrays of hashes. int32_t limits[UCAL_FIELD_COUNT][4]; for (j = 0; j < UCAL_FIELD_COUNT; j++) { limits[j][0] = INT32_MAX; limits[j][1] = INT32_MIN; limits[j][2] = INT32_MAX; limits[j][3] = INT32_MIN; } // This test can run for a long time; show progress. UDate millis = ucal_getNow(); UDate mark = millis + 5000; // 5 sec millis -= testDuration * 1000; // stop time if testDuration<0 for (i = 0; testDuration > 0 ? i < testDuration : ucal_getNow() < millis; ++i) { if (ucal_getNow() >= mark) { logln((UnicodeString)"(" + i + " days)"); mark += 5000; // 5 sec } UDate testMillis = greg.getTime(status); cal.setTime(testMillis, status); cal.setMinimalDaysInFirstWeek(1); if (failure(status, "Calendar set/getTime")) { return; } for (j = 0; fieldsToTest[j] >= 0; ++j) { UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j]; int32_t v = cal.get(f, status); int32_t minActual = cal.getActualMinimum(f, status); int32_t maxActual = cal.getActualMaximum(f, status); int32_t minLow = cal.getMinimum(f); int32_t minHigh = cal.getGreatestMinimum(f); int32_t maxLow = cal.getLeastMaximum(f); int32_t maxHigh = cal.getMaximum(f); if (limits[j][0] > minActual) { // the minimum limits[j][0] = minActual; } if (limits[j][1] < minActual) { // the greatest minimum limits[j][1] = minActual; } if (limits[j][2] > maxActual) { // the least maximum limits[j][2] = maxActual; } if (limits[j][3] < maxActual) { // the maximum limits[j][3] = maxActual; } if (minActual < minLow || minActual > minHigh) { errln((UnicodeString)"Fail: [" + cal.getType() + "] " + ymdToString(cal, ymd) + " Range for min of " + FIELD_NAME[f] + "(" + f + ")=" + minLow + ".." + minHigh + ", actual_min=" + minActual); } if (maxActual < maxLow || maxActual > maxHigh) { if ( uprv_strcmp(cal.getType(), "chinese") == 0 && testMillis >= 2842992000000.0 && testMillis <= 2906668800000.0 && logKnownIssue("12620", "chinese calendar failures for some actualMax tests")) { logln((UnicodeString)"KnownFail: [" + cal.getType() + "] " + ymdToString(cal, ymd) + " Range for max of " + FIELD_NAME[f] + "(" + f + ")=" + maxLow + ".." + maxHigh + ", actual_max=" + maxActual); } else { errln((UnicodeString)"Fail: [" + cal.getType() + "] " + ymdToString(cal, ymd) + " Range for max of " + FIELD_NAME[f] + "(" + f + ")=" + maxLow + ".." + maxHigh + ", actual_max=" + maxActual); } } if (v < minActual || v > maxActual) { // timebomb per #9967, fix with #9972 if ( uprv_strcmp(cal.getType(), "dangi") == 0 && testMillis >= 1865635198000.0 && logKnownIssue("9972", "as per #9967")) { // Feb 2029 gregorian, end of dangi 4361 logln((UnicodeString)"KnownFail: [" + cal.getType() + "] " + ymdToString(cal, ymd) + " " + FIELD_NAME[f] + "(" + f + ")=" + v + ", actual=" + minActual + ".." + maxActual + ", allowed=(" + minLow + ".." + minHigh + ")..(" + maxLow + ".." + maxHigh + ")"); } else if ( uprv_strcmp(cal.getType(), "chinese") == 0 && testMillis >= 2842992000000.0 && testMillis <= 2906668800000.0 && logKnownIssue("12620", "chinese calendar failures for some actualMax tests")) { logln((UnicodeString)"KnownFail: [" + cal.getType() + "] " + ymdToString(cal, ymd) + " " + FIELD_NAME[f] + "(" + f + ")=" + v + ", actual=" + minActual + ".." + maxActual + ", allowed=(" + minLow + ".." + minHigh + ")..(" + maxLow + ".." + maxHigh + ")"); } else { errln((UnicodeString)"Fail: [" + cal.getType() + "] " + ymdToString(cal, ymd) + " " + FIELD_NAME[f] + "(" + f + ")=" + v + ", actual=" + minActual + ".." + maxActual + ", allowed=(" + minLow + ".." + minHigh + ")..(" + maxLow + ".." + maxHigh + ")"); } } } greg.add(UCAL_DAY_OF_YEAR, 1, status); if (failure(status, "Calendar::add")) { return; } } // Check actual maxima and minima seen against ranges returned // by API. UnicodeString buf; for (j = 0; fieldsToTest[j] >= 0; ++j) { int32_t rangeLow, rangeHigh; UBool fullRangeSeen = TRUE; UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j]; buf.remove(); buf.append((UnicodeString)"[" + cal.getType() + "] " + FIELD_NAME[f]); // Minumum rangeLow = cal.getMinimum(f); rangeHigh = cal.getGreatestMinimum(f); if (limits[j][0] != rangeLow || limits[j][1] != rangeHigh) { fullRangeSeen = FALSE; } buf.append((UnicodeString)" minima range=" + rangeLow + ".." + rangeHigh); buf.append((UnicodeString)" minima actual=" + limits[j][0] + ".." + limits[j][1]); // Maximum rangeLow = cal.getLeastMaximum(f); rangeHigh = cal.getMaximum(f); if (limits[j][2] != rangeLow || limits[j][3] != rangeHigh) { fullRangeSeen = FALSE; } buf.append((UnicodeString)" maxima range=" + rangeLow + ".." + rangeHigh); buf.append((UnicodeString)" maxima actual=" + limits[j][2] + ".." + limits[j][3]); if (fullRangeSeen) { logln((UnicodeString)"OK: " + buf); } else { // This may or may not be an error -- if the range of dates // we scan over doesn't happen to contain a minimum or // maximum, it doesn't mean some other range won't. logln((UnicodeString)"Warning: " + buf); } } logln((UnicodeString)"End: " + greg.getTime(status)); }
void CalendarLimitTest::doTheoreticalLimitsTest(Calendar& cal, UBool leapMonth) { const char* calType = cal.getType(); int32_t nDOW = cal.getMaximum(UCAL_DAY_OF_WEEK); int32_t maxDOY = cal.getMaximum(UCAL_DAY_OF_YEAR); int32_t lmaxDOW = cal.getLeastMaximum(UCAL_DAY_OF_YEAR); int32_t maxWOY = cal.getMaximum(UCAL_WEEK_OF_YEAR); int32_t lmaxWOY = cal.getLeastMaximum(UCAL_WEEK_OF_YEAR); int32_t maxM = cal.getMaximum(UCAL_MONTH) + 1; int32_t lmaxM = cal.getLeastMaximum(UCAL_MONTH) + 1; int32_t maxDOM = cal.getMaximum(UCAL_DAY_OF_MONTH); int32_t lmaxDOM = cal.getLeastMaximum(UCAL_DAY_OF_MONTH); int32_t maxDOWIM = cal.getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH); int32_t lmaxDOWIM = cal.getLeastMaximum(UCAL_DAY_OF_WEEK_IN_MONTH); int32_t maxWOM = cal.getMaximum(UCAL_WEEK_OF_MONTH); int32_t lmaxWOM = cal.getLeastMaximum(UCAL_WEEK_OF_MONTH); int32_t minDaysInFirstWeek = cal.getMinimalDaysInFirstWeek(); // Day of year int32_t expected; if (!leapMonth) { expected = maxM*maxDOM; if (maxDOY > expected) { errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_YEAR is too big: " + maxDOY + "/expected: <=" + expected); } expected = lmaxM*lmaxDOM; if (lmaxDOW < expected) { errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_YEAR is too small: " + lmaxDOW + "/expected: >=" + expected); } } // Week of year expected = maxDOY/nDOW + 1; if (maxWOY > expected) { errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_YEAR is too big: " + maxWOY + "/expected: <=" + expected); } expected = lmaxDOW/nDOW; if (lmaxWOY < expected) { errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_YEAR is too small: " + lmaxWOY + "/expected >=" + expected); } // Day of week in month expected = (maxDOM + nDOW - 1)/nDOW; if (maxDOWIM != expected) { errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: " + maxDOWIM + "/expected: " + expected); } expected = (lmaxDOM + nDOW - 1)/nDOW; if (lmaxDOWIM != expected) { errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: " + lmaxDOWIM + "/expected: " + expected); } // Week of month expected = (maxDOM + (nDOW - 1) + (nDOW - minDaysInFirstWeek)) / nDOW; if (maxWOM != expected) { errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_MONTH is incorrect: " + maxWOM + "/expected: " + expected); } expected = (lmaxDOM + (nDOW - minDaysInFirstWeek)) / nDOW; if (lmaxWOM != expected) { errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_MONTH is incorrect: " + lmaxWOM + "/expected: " + expected); } }