ICU-5845 Calendar 'actual' limit test ported from J to C. Also ported bug fixes related to calendar limits implementation.

X-SVN-Rev: 23775
This commit is contained in:
Yoshito Umaoka 2008-04-09 18:26:42 +00:00
parent 4b2b264cbf
commit 32c3313145
14 changed files with 639 additions and 248 deletions

View file

@ -1268,7 +1268,6 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
return;
}
int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
int32_t year = fFields[UCAL_YEAR];
int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];
@ -1281,7 +1280,7 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
// the previous year; days at the end of the year may fall into the
// first week of the next year. ASSUME that the year length is less than
// 7000 days.
int32_t yearOfWeekOfYear = year;
int32_t yearOfWeekOfYear = eyear;
int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
@ -2096,6 +2095,25 @@ int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) cons
case UCAL_MILLISECONDS_IN_DAY:
case UCAL_IS_LEAP_MONTH:
return kCalendarLimits[field][limitType];
case UCAL_WEEK_OF_MONTH:
{
int32_t limit;
if (limitType == UCAL_LIMIT_MINIMUM) {
limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
} else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
limit = 1;
} else {
int32_t minDaysInFirst = getMinimalDaysInFirstWeek();
int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType);
if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) {
limit = (daysInMonth + (7 - minDaysInFirst)) / 7;
} else { // limitType == UCAL_LIMIT_MAXIMUM
limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7;
}
}
return limit;
}
default:
return handleGetLimit(field, limitType);
}
@ -2904,11 +2922,13 @@ void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErr
switch (field) {
case UCAL_YEAR:
case UCAL_YEAR_WOY:
case UCAL_EXTENDED_YEAR:
set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR));
break;
case UCAL_YEAR_WOY:
set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR));
case UCAL_MONTH:
set(UCAL_DATE, getGreatestMinimum(UCAL_DATE));
break;
@ -2941,7 +2961,7 @@ void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErr
}
break;
default:
;
break;
}
// Do this last to give it the newest time stamp
@ -2966,39 +2986,37 @@ int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue,
Calendar *work = clone();
if(!work) { status = U_MEMORY_ALLOCATION_ERROR; return startValue; }
work->setLenient(TRUE);
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d - getActualHelper - %s\n", __FILE__, __LINE__, u_errorName(status));
#endif
work->prepareGetActual(field, delta < 0, status);
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d - getActualHelper - %s\n", __FILE__, __LINE__, u_errorName(status));
#endif
// now try each value from the start to the end one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual maximum for the current date
work->set(field, startValue);
// prepareGetActual sets the first day of week in the same week with
// the first day of a month. Unlike WEEK_OF_YEAR, week number for the
// week which contains days from both previous and current month is
// not unique. For example, last several days in the previous month
// is week 5, and the rest of week is week 1.
int32_t result = startValue;
do {
if (work->get(field, status) != startValue
&& field != UCAL_WEEK_OF_MONTH && delta > 0 || U_FAILURE(status)) {
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d - getActualHelper - %s\n", __FILE__, __LINE__, u_errorName(status));
fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status));
#endif
work->set(field, startValue);
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d - getActualHelper - %s (set to %d)\n", __FILE__, __LINE__, u_errorName(status), startValue);
#endif
if (work->get(field, status) != startValue) {
#if defined (U_DEBUG_CAL)
fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status));
#endif
break;
} else {
result = startValue;
} else {
do {
startValue += delta;
#if defined (U_DEBUG_CAL)
fprintf(stderr, "getActualHelper(%d) result=%d (start), start += %d to %d\n", field, result, delta, startValue);
work->add(field, delta, status);
if (work->get(field, status) != startValue || U_FAILURE(status)) {
#if defined (U_DEBUG_CAL)
fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status));
#endif
}
} while (result != endValue && U_SUCCESS(status));
break;
}
result = startValue;
} while (startValue != endValue);
}
delete work;
#if defined (U_DEBUG_CAL)
fprintf(stderr, "getActualHelper(%d) = %d\n", field, result);

View file

@ -19,13 +19,13 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
// Minimum Maximum
{ 0, 0, 1, 1}, // ERA
{ 1, 1, 5000000, 5000000}, // YEAR
{ 0, 0, 13, 13}, // MONTH
{ 0, 0, 12, 12}, // MONTH
{ 1, 1, 52, 53}, // WEEK_OF_YEAR
{ 0, 0, 1, 6}, // WEEK_OF_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 5, 30}, // DAY_OF_MONTH
{ 1, 1, 365, 366}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ -1, -1, 4, 6}, // DAY_OF_WEEK_IN_MONTH
{ -1, -1, 1, 5}, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY

View file

@ -135,13 +135,13 @@ const char *ChineseCalendar::getType() const {
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
// Minimum Greatest Least Maximum
// Minimum Maximum
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 1, 1, 83333, 83333}, // ERA
{ 1, 1, 70, 70}, // YEAR
{ 1, 1, 60, 60}, // YEAR
{ 0, 0, 11, 11}, // MONTH
{ 1, 1, 50, 55}, // WEEK_OF_YEAR
{ 1, 1, 4, 6}, // WEEK_OF_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 29, 30}, // DAY_OF_MONTH
{ 1, 1, 353, 385}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
@ -154,12 +154,12 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ -5000001, -5000001, 5000001, 5000001}, // YEAR_WOY
{ -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
{ 0, 0, 1, 1}, // IS_LEAP_MONTH
};

View file

@ -76,27 +76,27 @@ static const int8_t kLeapMonthLength[]
static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = {
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 1, 1 }, // ERA
{ 1, 1, 140742, 144683 }, // YEAR
{ 0, 0, 11, 11 }, // MONTH
{ 1, 1, 52, 53 }, // WEEK_OF_YEAR
{ 0, 0, 4, 6 }, // WEEK_OF_MONTH
{ 1, 1, 28, 31 }, // DAY_OF_MONTH
{ 1, 1, 365, 366 }, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// DAY_OF_WEEK
{ -1, -1, 4, 5 }, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// HOUR_OF_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// MINUTE
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// SECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// DST_OFFSET
{ -140742, -140742, 140742, 144683 }, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// DOW_LOCAL
{ -140742, -140742, 140742, 144683 }, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1},// JULIAN_DAY
{ 0, 0, 1, 1}, // ERA
{ 1, 1, 140742, 144683}, // YEAR
{ 0, 0, 11, 11}, // MONTH
{ 1, 1, 52, 53}, // WEEK_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 28, 31}, // DAY_OF_MONTH
{ 1, 1, 365, 366}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ -1, -1, 4, 5}, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ -140742, -140742, 140742, 144683}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ -140742, -140742, 140742, 144683}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
};

View file

@ -37,16 +37,16 @@
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 0, 0 }, // ERA
{ 1, 1, 5000000, 5000000 }, // YEAR
{ 0, 0, 12, 12 }, // MONTH
{ 1, 1, 51, 56 }, // WEEK_OF_YEAR
{ 0, 0, 4, 6 }, // WEEK_OF_MONTH
{ 1, 1, 29, 30 }, // DAY_OF_MONTH
{ 1, 1, 353, 385 }, // DAY_OF_YEAR
{ 0, 0, 0, 0}, // ERA
{ -5000000, -5000000, 5000000, 5000000}, // YEAR
{ 0, 0, 12, 12}, // MONTH
{ 1, 1, 51, 56}, // WEEK_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 29, 30}, // DAY_OF_MONTH
{ 1, 1, 353, 385}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ -1, -1, 5, 5 }, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1/* */}, // AM_PM
{ -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
@ -54,9 +54,9 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ -5000001, -5000001, 5000001, 5000001 }, // YEAR_WOY
{ -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
@ -392,8 +392,8 @@ int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status)
if (day == 0) {
int32_t months = (235 * year - 234) / 19; // # of months before year
int32_t frac = months * MONTH_FRACT + BAHARAD; // Fractional part of day #
day = months * 29 + (frac / DAY_PARTS); // Whole # part of calculation
int64_t frac = (int64_t)months * MONTH_FRACT + BAHARAD; // Fractional part of day #
day = months * 29 + (int32_t)(frac / DAY_PARTS); // Whole # part of calculation
frac = frac % DAY_PARTS; // Time of day
int32_t wd = (day % 7); // Day of week (0 == Monday)
@ -573,9 +573,23 @@ void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
UBool isLeap = isLeapYear(year);
int32_t month = 0;
while (dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) {
int32_t momax = sizeof(MONTH_START) / (3 * sizeof(int16_t));
while (month < momax && dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) {
month++;
}
if (month >= momax) {
// TODO: I found dayOfYear could be out of range when
// a large value is set to julianDay. I patched startOfYear
// to reduce the chace, but it could be still reproduced either
// by startOfYear or other places. For now, we check
// the month is in valid range to avoid out of array index
// access problem here. However, we need to carefully review
// the calendar implementation to check the extreme limit of
// each calendar field and the code works well for any values
// in the valid value range. -yoshito
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
month--;
int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2007, International Business Machines Corporation
* Copyright (C) 2003-2008, International Business Machines Corporation
* and others. All Rights Reserved.
******************************************************************************
*
@ -55,31 +55,31 @@ const char *IndianCalendar::getType() const {
}
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 0, 0 }, // ERA
{ 1, 1, 5000000, 5000000 }, // YEAR
{ 0, 0, 11, 11 }, // MONTH
{ 1, 1, 52, 53 }, // WEEK_OF_YEAR
{ 0, 0, 4, 6 }, // WEEK_OF_MONTH
{ 1, 1, 30, 31 }, // DAY_OF_MONTH
{ 1, 1, 365, 366 }, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ -1, -1, 5, 5 }, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ 1, 1, 5000001, 5000001}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ 1, 1, 5000000, 5000000}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 0, 0}, // ERA
{ -5000000, -5000000, 5000000, 5000000}, // YEAR
{ 0, 0, 11, 11}, // MONTH
{ 1, 1, 52, 53}, // WEEK_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 30, 31}, // DAY_OF_MONTH
{ 1, 1, 365, 366}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
};
static const double JULIAN_EPOCH = 1721425.5;
@ -289,44 +289,47 @@ int32_t IndianCalendar::handleGetExtendedYear() {
* calendar equivalents for the given Julian day.
*/
void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) {
double jdAtStartOfGregYear;
int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
int32_t gregorianYear; // Stores gregorian date corresponding to Julian day;
int32_t gd[3];
double jdAtStartOfGregYear;
int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
int32_t gregorianYear; // Stores gregorian date corresponding to Julian day;
int32_t gd[3];
gregorianYear = jdToGregorian(julianDay, gd)[0]; // Gregorian date for Julian day
IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era
jdAtStartOfGregYear = gregorianToJD(gregorianYear, 1, 1); // JD at start of Gregorian year
yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0)
leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year
gregorianYear = jdToGregorian(julianDay, gd)[0]; // Gregorian date for Julian day
IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era
jdAtStartOfGregYear = gregorianToJD(gregorianYear, 1, 1); // JD at start of Gregorian year
yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0)
if (yday < INDIAN_YEAR_START) {
// Day is at the end of the preceding Saka year
IndianYear -= 1;
yday += leapMonth + (31 * 5) + (30 * 3) + 10 + INDIAN_YEAR_START;
}
if (yday < INDIAN_YEAR_START) {
// Day is at the end of the preceding Saka year
IndianYear -= 1;
leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year
yday += leapMonth + (31 * 5) + (30 * 3) + 10;
} else {
leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year
yday -= INDIAN_YEAR_START;
}
yday -= INDIAN_YEAR_START;
if (yday < leapMonth) {
IndianMonth = 0;
IndianDayOfMonth = yday + 1;
} else {
mday = yday - leapMonth;
if (mday < (31 * 5)) {
IndianMonth = (int32_t)uprv_floor(mday / 31) + 1;
IndianDayOfMonth = (mday % 31) + 1;
} else {
mday -= 31 * 5;
IndianMonth = (int32_t)uprv_floor(mday / 30) + 6;
IndianDayOfMonth = (mday % 30) + 1;
}
if (yday < leapMonth) {
IndianMonth = 0;
IndianDayOfMonth = yday + 1;
} else {
mday = yday - leapMonth;
if (mday < (31 * 5)) {
IndianMonth = (int32_t)uprv_floor(mday / 31) + 1;
IndianDayOfMonth = (mday % 31) + 1;
} else {
mday -= 31 * 5;
IndianMonth = (int32_t)uprv_floor(mday / 30) + 6;
IndianDayOfMonth = (mday % 30) + 1;
}
}
internalSet(UCAL_ERA, 0);
internalSet(UCAL_EXTENDED_YEAR, IndianYear);
internalSet(UCAL_YEAR, IndianYear);
internalSet(UCAL_MONTH, IndianMonth);
internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth );
internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth);
internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based
}
UBool

View file

@ -138,18 +138,21 @@ UBool IslamicCalendar::isCivil() {
// Minimum / Maximum access functions
//-------------------------------------------------------------------------
// Note: Current IslamicCalendar implementation does not work
// well with negative years.
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 0, 0 }, // ERA
{ 1, 1, 5000000, 5000000 }, // YEAR
{ 0, 0, 11, 11 }, // MONTH
{ 1, 1, 50, 51 }, // WEEK_OF_YEAR
{ 0, 0, 4, 6 }, // WEEK_OF_MONTH
{ 1, 1, 29, 30 }, // DAY_OF_MONTH
{ 1, 1, 354, 355 }, // DAY_OF_YEAR
{ 0, 0, 0, 0}, // ERA
{ 1, 1, 5000000, 5000000}, // YEAR
{ 0, 0, 11, 11}, // MONTH
{ 1, 1, 50, 51}, // WEEK_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 29, 30}, // DAY_OF_MONTH
{ 1, 1, 354, 355}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ -1, -1, 5, 5 }, // DAY_OF_WEEK_IN_MONTH
{ -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
@ -158,9 +161,9 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ 1, 1, 5000001, 5000001 }, // YEAR_WOY
{ 1, 1, 5000000, 5000000}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ 1, 1, 5000000, 5000000 }, // EXTENDED_YEAR
{ 1, 1, 5000000, 5000000}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2003-2007, International Business Machines Corporation and *
* Copyright (C) 2003-2008, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -476,69 +476,55 @@ int32_t JapaneseCalendar::defaultCenturyStartYear() const
return 0;
}
static int32_t gJapanCalendarLimits[2][4] = {
// Minimum Greatest min Least max Greatest max
{ 0, 0, kCurrentEra, kCurrentEra }, // ERA
{ 1, 1, 0, 0 }, // YEAR least-max/greatest-max computed at runtime
};
static UBool gJapanYearLimitsKnown = FALSE;
int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const
{
switch(field) {
case UCAL_ERA:
if (limitType == UCAL_LIMIT_MINIMUM || limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
return 1;
}
return kCurrentEra;
case UCAL_YEAR:
{
UBool needCalc;
UMTX_CHECK(NULL, (gJapanYearLimitsKnown == FALSE), needCalc);
if(needCalc) {
int32_t min = kEraInfo[1].year - kEraInfo[0].year;
int32_t max = min;
for (uint32_t i=2; i<kEraCount; i++) { // counting by year, not field (3's)
int32_t d = kEraInfo[i].year - kEraInfo[i-1].year;
U_ASSERT(d >= 0);
if (d < min) {
min = d;
}
if (d > max) {
max = d;
}
}
U_ASSERT(min >= 0 && max > min);
umtx_lock(NULL);
if(gJapanYearLimitsKnown==FALSE) {
gJapanCalendarLimits[field][UCAL_LIMIT_LEAST_MAXIMUM] = ++min; // 1-based
gJapanCalendarLimits[field][UCAL_LIMIT_MAXIMUM] = ++max; // 1-based
gJapanYearLimitsKnown = TRUE;
}
umtx_unlock(NULL);
switch (limitType) {
case UCAL_LIMIT_MINIMUM:
case UCAL_LIMIT_GREATEST_MINIMUM:
return 1;
case UCAL_LIMIT_LEAST_MAXIMUM:
return 1;
case UCAL_LIMIT_MAXIMUM:
return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - kEraInfo[kCurrentEra].year;
}
return gJapanCalendarLimits[field][limitType];
}
case UCAL_ERA:
return gJapanCalendarLimits[field][limitType];
case UCAL_EXTENDED_YEAR: // extended year limits
switch(limitType) {
case UCAL_LIMIT_GREATEST_MINIMUM:
case UCAL_LIMIT_MINIMUM:
return kEraInfo[0].year; /* minimum is 1st era year */
case UCAL_LIMIT_LEAST_MAXIMUM:
case UCAL_LIMIT_MAXIMUM:
/* use Gregorian calendar max */
default:
return GregorianCalendar::handleGetLimit(field,limitType);
}
break;
default:
return GregorianCalendar::handleGetLimit(field,limitType);
}
}
int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const {
if (field == UCAL_YEAR) {
int32_t era = get(UCAL_ERA, status);
if (U_FAILURE(status)) {
return 0; // error case... any value
}
if (era == kCurrentEra) {
// TODO: Investigate what value should be used here - revisit after 4.0.
return handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM);
} else {
int32_t nextEraYear = kEraInfo[era + 1].year;
int32_t nextEraMonth = kEraInfo[era + 1].month;
int32_t nextEraDate = kEraInfo[era + 1].day;
int32_t maxYear = nextEraYear - kEraInfo[era].year + 1; // 1-base
if (nextEraMonth == 1 && nextEraDate == 1) {
// Subtract 1, because the next era starts at Jan 1
maxYear--;
}
return maxYear;
}
}
return GregorianCalendar::getActualMaximum(field, status);
}
U_NAMESPACE_END

View file

@ -1,6 +1,6 @@
/*
********************************************************************************
* Copyright (C) 2003-2007, International Business Machines Corporation
* Copyright (C) 2003-2008, International Business Machines Corporation
* and others. All Rights Reserved.
********************************************************************************
*
@ -108,6 +108,13 @@ public:
*/
virtual int32_t handleGetExtendedYear();
/**
* Return the maximum value that this field could have, given the current date.
* @internal
*/
virtual int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const;
public:
/**
* Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual

View file

@ -201,28 +201,28 @@ PersianCalendar::~PersianCalendar()
//-------------------------------------------------------------------------
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 0, 0 }, // ERA
{ -2500, -2500, 2500, 2500 }, // YEAR
{ 0, 0, 11, 11 }, // MONTH
{ 1, 1, 52, 53 }, // WEEK_OF_YEAR
{ 0, 0, 5, 6 }, // WEEK_OF_MONTH
{ 1, 1, 29, 31 }, // DAY_OF_MONTH
{ 1, 1, 365, 366 }, // DAY_OF_YEAR
{ 0, 0, 6, 6 }, // DAY_OF_WEEK
{ 1, 1, 4, 6 }, // DAY_OF_WEEK_IN_MONTH
{ 0, 0, 1, 1 }, // AM_PM
{ 0, 0, 11, 11 }, // HOUR
{ 0, 0, 23, 23 }, // HOUR_OF_DAY
{ 0, 0, 59, 59 }, // MINUTE
{ 0, 0, 59, 59 }, // SECOND
{ 0, 0, 999, 999 }, // MILLISECOND
{ -12, -12, 12, 12 }, // ZONE_OFFSET
{ 0, 0, 1, 1 }, // DST_OFFSET
{ -140742, -140742, 144683, 140742 }, // YEAR_WOY
{ 0, 0, 6, 6 }, // DOW_LOCAL
{ -2500, -2500, 2500, 2500 }, // EXTENDED_YEAR
// Minimum Greatest Least Maximum
// Minimum Maximum
{ 0, 0, 0, 0}, // ERA
{ -5000000, -5000000, 5000000, 5000000}, // YEAR
{ 0, 0, 11, 11}, // MONTH
{ 1, 1, 52, 53}, // WEEK_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 29, 31}, // DAY_OF_MONTH
{ 1, 1, 365, 366}, // DAY_OF_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
{ 1, 1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
{ -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
{ -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH

View file

@ -1,6 +1,6 @@
/*
********************************************************************************
* Copyright (C) 1997-2006, International Business Machines
* Copyright (C) 1997-2008, International Business Machines
* Corporation and others. All Rights Reserved.
********************************************************************************
*
@ -962,7 +962,7 @@ public:
* @return the minimum of the given field for the current date of this Calendar
* @stable ICU 2.6.
*/
int32_t getActualMinimum(UCalendarDateFields field, UErrorCode& status) const;
virtual int32_t getActualMinimum(UCalendarDateFields field, UErrorCode& status) const;
/**
* Return the maximum value that this field could have, given the current date.
@ -998,7 +998,7 @@ public:
* @return the maximum of the given field for the current date of this Calendar
* @stable ICU 2.6.
*/
int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const;
virtual int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const;
/**
* Gets the value for a given time field. Recalculate the current time field values

View file

@ -1,6 +1,6 @@
/***********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2006, International Business Machines Corporation
* Copyright (c) 1997-2008, International Business Machines Corporation
* and others. All Rights Reserved.
***********************************************************************/
@ -15,6 +15,7 @@
#include "unicode/datefmt.h"
#include "unicode/smpdtfmt.h"
#include "putilimp.h"
#include "cstring.h"
U_NAMESPACE_USE
void CalendarLimitTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
@ -23,12 +24,20 @@ void CalendarLimitTest::runIndexedTest( int32_t index, UBool exec, const char* &
switch (index) {
// Re-enable this later
case 0:
name = "TestCalendarLimit";
name = "TestCalendarExtremeLimit";
if (exec) {
logln("TestCalendarLimit---"); logln("");
TestCalendarLimit();
logln("TestCalendarExtremeLimit---"); logln("");
TestCalendarExtremeLimit();
}
break;
case 1:
name = "TestLimits";
if (exec) {
logln("TestLimits---"); logln("");
TestLimits();
}
break;
default: name = ""; break;
}
}
@ -84,7 +93,7 @@ CalendarLimitTest::withinErr(double a, double b, double err)
}
void
CalendarLimitTest::TestCalendarLimit()
CalendarLimitTest::TestCalendarExtremeLimit()
{
UErrorCode status = U_ZERO_ERROR;
Calendar *cal = Calendar::createInstance(status);
@ -130,6 +139,317 @@ CalendarLimitTest::TestCalendarLimit()
delete fmt;
}
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},
{"taiwan", 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;
}
}
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);
}
}
void
CalendarLimitTest::doLimitsTest(Calendar& cal, UDate startDate) {
int32_t testTime = quick ? -3 : -120;
doLimitsTest(cal, NULL /*default fields*/, startDate, testTime);
}
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
}
cal.setTime(greg.getTime(status), status);
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) {
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) {
errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
ymdToString(cal, ymd) +
" " + FIELD_NAME[f] + "(" + f + ")=" + v +
", actual range=" + 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));
}
UnicodeString&
CalendarLimitTest::ymdToString(const Calendar& cal, UnicodeString& str) {
UErrorCode status = U_ZERO_ERROR;
str.remove();
str.append((UnicodeString)"" + cal.get(UCAL_EXTENDED_YEAR, status)
+ "/" + (cal.get(UCAL_MONTH, status) + 1)
+ (cal.get(UCAL_IS_LEAP_MONTH, status) == 1 ? "(leap)" : "")
+ "/" + cal.get(UCAL_DATE, status)
+ ", time=" + cal.getTime(status));
return str;
}
#endif /* #if !UCONFIG_NO_FORMATTING */
// eof

View file

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2001, International Business Machines Corporation and
* Copyright (c) 1997-2008, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -33,7 +33,54 @@ public: // package
public:
// test behaviour and error reporting at boundaries of defined range
virtual void TestCalendarLimit(void);
virtual void TestCalendarExtremeLimit(void);
void TestLimits(void);
private:
/*
* Test the functions getMaximum/getGeratestMinimum logically correct.
* This method assumes day of week cycle is consistent.
* @param cal The calendar instance to be tested.
* @param leapMonth true if the calendar system has leap months
*/
void doTheoreticalLimitsTest(Calendar& cal, UBool leapMonth);
/*
* Test the functions getXxxMinimum() and getXxxMaximum() by marching a
* test calendar 'cal' through 'numberOfDays' sequential days starting
* with 'startDate'. For each date, read a field value along with its
* reported actual minimum and actual maximum. These values are
* checked against one another as well as against getMinimum(),
* getGreatestMinimum(), getLeastMaximum(), and getMaximum(). We
* expect to see:
*
* 1. minimum <= actualMinimum <= greatestMinimum <=
* leastMaximum <= actualMaximum <= maximum
*
* 2. actualMinimum <= value <= actualMaximum
*
* Note: In addition to outright failures, this test reports some
* results as warnings. These are not generally of concern, but they
* should be evaluated by a human. To see these, run this test in
* verbose mode.
* @param cal the calendar to be tested
* @param fieldsToTest an array of field values to be tested, e.g., new
* int[] { UCAL_MONTH, UCAL_DAY_OF_MONTH }. It only makes
* sense to test the day fields; the time fields are not tested by this
* method. If null, then test all standard fields.
* @param startDate the first date to test
* @param testDuration if positive, the number of days to be tested.
* If negative, the number of seconds to run the test.
*/
void doLimitsTest(Calendar& cal, const int32_t* fieldsToTest, UDate startDate, int32_t testDuration);
/**
* doLimitsTest with default test duration and fields
*/
void doLimitsTest(Calendar& cal, UDate startDate);
UnicodeString& ymdToString(const Calendar& cal, UnicodeString& str);
};
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -1,6 +1,6 @@
/************************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2007, International Business Machines Corporation
* Copyright (c) 1997-2008, International Business Machines Corporation
* and others. All Rights Reserved.
************************************************************************/
@ -343,8 +343,8 @@ CalendarTest::TestGenericAPI()
for (i=0; i<UCAL_FIELD_COUNT; ++i)
{
if (cal->getMinimum((UCalendarDateFields)i) != cal->getGreatestMinimum((UCalendarDateFields)i))
errln("FAIL: getMinimum doesn't match getGreatestMinimum for field " + i);
if (cal->getMinimum((UCalendarDateFields)i) > cal->getGreatestMinimum((UCalendarDateFields)i))
errln("FAIL: getMinimum larger than getGreatestMinimum for field " + i);
if (cal->getLeastMaximum((UCalendarDateFields)i) > cal->getMaximum((UCalendarDateFields)i))
errln("FAIL: getLeastMaximum larger than getMaximum for field " + i);
if (cal->getMinimum((UCalendarDateFields)i) >= cal->getMaximum((UCalendarDateFields)i))
@ -596,20 +596,16 @@ CalendarTest::TestDisambiguation765()
c->set(UCAL_MONTH, UCAL_JUNE);
c->set(UCAL_DAY_OF_WEEK_IN_MONTH, - 1);
verify765("1997 last Tuesday in June = ", c, 1997, UCAL_JUNE, 24);
// IllegalArgumentException e = null;
status = U_ZERO_ERROR;
//try {
c->clear();
c->set(UCAL_YEAR, 1997);
c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
c->set(UCAL_MONTH, UCAL_JUNE);
c->set(UCAL_DAY_OF_WEEK_IN_MONTH, 0);
c->getTime(status);
//}
//catch(IllegalArgumentException ex) {
// e = ex;
//}
c->clear();
c->set(UCAL_YEAR, 1997);
c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
c->set(UCAL_MONTH, UCAL_JUNE);
c->set(UCAL_DAY_OF_WEEK_IN_MONTH, 0);
c->getTime(status);
verify765("1997 zero-th Tuesday in June = ", status);
c->clear();
c->set(UCAL_YEAR, 1997);
c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
@ -622,19 +618,16 @@ CalendarTest::TestDisambiguation765()
c->set(UCAL_MONTH, UCAL_JUNE);
c->set(UCAL_WEEK_OF_MONTH, 5);
verify765("1997 Tuesday in week 5 of June = ", c, 1997, UCAL_JULY, 1);
status = U_ZERO_ERROR;
//try {
c->clear();
c->set(UCAL_YEAR, 1997);
c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
c->set(UCAL_MONTH, UCAL_JUNE);
c->set(UCAL_WEEK_OF_MONTH, 0);
verify765("1997 Tuesday in week 0 of June = ", c, 1997, UCAL_MAY, 27);
//}
//catch(IllegalArgumentException ex) {
// errln("FAIL: Exception seen:");
// ex.printStackTrace(log);
//}
c->clear();
c->set(UCAL_YEAR, 1997);
c->set(UCAL_DAY_OF_WEEK, UCAL_TUESDAY);
c->set(UCAL_MONTH, UCAL_JUNE);
c->set(UCAL_WEEK_OF_MONTH, 0);
c->getTime(status);
verify765("1997 Tuesday in week 0 of June = ", status);
/* Note: The following test used to expect YEAR 1997, WOY 1 to
* resolve to a date in Dec 1996; that is, to behave as if
* YEAR_WOY were 1997. With the addition of a new explicit