IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success)
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
{
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
}
// Note: Current IslamicCalendar implementation does not work // well with negative years.
// TODO: In some cases the current ICU Islamic calendar implementation shows // a month as having 31 days. Since date parsing now uses range checks based // on the table below, we need to change the range for last day of month to // include 31 as a workaround until the implementation is fixed. staticconst 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
{/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
{ 1, 1, 29, 31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30
{ 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
{/*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, 5000000, 5000000}, // 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
{ 0, 0, 11, 11}, // ORDINAL_MONTH
};
/** * Determine whether a year is a leap year in the Islamic civil calendar
*/ inlinebool civilLeapYear(int32_t year) { return (14 + 11 * year) % 30 < 11;
}
/** * Return the day # on which the given year starts. Days are counted * from the Hijri epoch, origin 0.
*/
int64_t IslamicCalendar::yearStart(int32_t year, UErrorCode& status) const { return trueMonthStart(12*(year-1), status);
}
/** * Return the day # on which the given month starts. Days are counted * from the Hijri epoch, origin 0. * * @param year The hijri year * @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const { if (U_FAILURE(status)) { return 0;
}
int32_t temp; if (uprv_add32_overflow(year, -1, &temp) ||
uprv_mul32_overflow(temp, 12, &temp) ||
uprv_add32_overflow(temp, month, &month)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return 0;
}
return trueMonthStart(month, status);
}
namespace { /** * Return the "age" of the moon at the given time; this is the difference * in ecliptic latitude between the moon and the sun. This method simply * calls CalendarAstronomer.moonAge, converts to degrees, * and adjusts the resultto be in the range [-180, 180]. * * @param time The time at which the moon's age is desired, * in millis since 1/1/1970.
*/ double moonAge(UDate time);
/** * Find the day number on which a particular month of the true/lunar * Islamic calendar starts. * * @param month The month in question, origin 0 from the Hijri epoch * * @return The day number on which the given month starts.
*/
int32_t trueMonthStart(int32_t month, UErrorCode& status) { if (U_FAILURE(status)) { return 0;
}
ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup);
int64_t start = CalendarCache::get(&gMonthCache, month, status);
if (U_SUCCESS(status) && start==0) { // Make a guess at when the month started, using the average length
UDate origin = HIJRA_MILLIS
+ uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay;
// moonAge will fail due to memory allocation error double age = moonAge(origin);
if (age >= 0) { // The month has already started do {
origin -= kOneDay;
age = moonAge(origin);
} while (age >= 0);
} else { // Preceding month has not ended yet. do {
origin += kOneDay;
age = moonAge(origin);
} while (age < 0);
}
start = ClockMath::floorDivideInt64( static_cast<int64_t>(static_cast<int64_t>(origin) - HIJRA_MILLIS), static_cast<int64_t>(kOneDay)) + 1;
CalendarCache::put(&gMonthCache, month, start, status);
} if(U_FAILURE(status)) {
start = 0;
} return start;
}
double moonAge(UDate time) { // Convert to degrees and normalize... double age = CalendarAstronomer(time).getMoonAge() * 180 / CalendarAstronomer::PI; if (age > 180) {
age = age - 360;
}
} // namepsace /** * Return the number of days in the given Islamic year * @draft ICU 2.4
*/
int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const {
UErrorCode status = U_ZERO_ERROR;
int32_t length = yearLength(extendedYear, status); if (U_FAILURE(status)) { // fallback to normal Islamic calendar length 355 day a year if we // encounter error and cannot propagate. return 355;
} return length;
}
//------------------------------------------------------------------------- // Functions for converting from field values to milliseconds.... //-------------------------------------------------------------------------
// Return JD of start of given month/year // Calendar says: // Get the Julian day of the day BEFORE the start of this year. // If useMonth is true, get the day before the start of the month. // Hence the -1 /** * @draft ICU 2.4
*/
int64_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
UBool /* useMonth */,
UErrorCode& status) const { if (U_FAILURE(status)) { return 0;
} // This may be called by Calendar::handleComputeJulianDay with months out of the range // 0..11. Need to handle that here since monthStart requires months in the range 0.11. if (month > 11) { if (uprv_add32_overflow(eyear, (month / 12), &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return 0;
}
month %= 12;
} elseif (month < 0) {
month++; if (uprv_add32_overflow(eyear, (month / 12) - 1, &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return 0;
}
month = (month % 12) + 11;
} return monthStart(eyear, month, status) + getEpoc() - 1;
}
//------------------------------------------------------------------------- // Functions for converting from milliseconds to field values //-------------------------------------------------------------------------
/** * @draft ICU 2.4
*/
int32_t IslamicCalendar::handleGetExtendedYear(UErrorCode& /* status */) { if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
} return internalGet(UCAL_YEAR, 1); // Default to year 1
}
/** * Override Calendar to compute several fields specific to the Islamic * calendar system. These are: * * <ul><li>ERA * <li>YEAR * <li>MONTH * <li>DAY_OF_MONTH * <li>DAY_OF_YEAR * <li>EXTENDED_YEAR</ul> * * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this * method is called. The getGregorianXxx() methods return Gregorian * calendar equivalents for the given Julian day. * @draft ICU 2.4
*/ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { if (U_FAILURE(status)) { return;
}
int32_t days = julianDay - getEpoc();
// Guess at the number of elapsed full months since the epoch
int32_t month = static_cast<int32_t>(uprv_floor(static_cast<double>(days) / CalendarAstronomer::SYNODIC_MONTH));
double age = moonAge(internalGetTime()); if ( days - startDate >= 25 && age > 0) { // If we're near the end of the month, assume next month and search backwards
month++;
}
// Find out the last time that the new moon was actually visible at this longitude // This returns midnight the night that the moon was visible at sunset. while ((startDate = trueMonthStart(month, status)) > days) { if (U_FAILURE(status)) { return;
} // If it was after the date in question, back up a month and try again
month--;
} if (U_FAILURE(status)) { return;
}
// Now figure out the day of the year.
int64_t dayOfYear = (days - monthStart(year, 0, status)) + 1; if (U_FAILURE(status)) { return;
} if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
/** * Return the day # on which the given year starts. Days are counted * from the Hijri epoch, origin 0.
*/
int64_t IslamicCivilCalendar::yearStart(int32_t year, UErrorCode& /* status */) const { return 354LL * (year-1LL) + ClockMath::floorDivideInt64(3 + 11LL * year, 30LL);
}
/** * Return the day # on which the given month starts. Days are counted * from the Hijri epoch, origin 0. * * @param year The hijri year * @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month, UErrorCode& /*status*/) const { // This does not handle months out of the range 0..11 returnstatic_cast<int64_t>(
uprv_ceil(29.5*month) + 354LL*(year-1LL) +
ClockMath::floorDivideInt64(
11LL*static_cast<int64_t>(year) + 3LL, 30LL));
}
/** * Return the length (in days) of the given month. * * @param year The hijri year * @param year The hijri month, 0-based * @draft ICU 2.4
*/
int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
UErrorCode& /* status */) const {
int32_t length = 29 + (month+1) % 2; if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
length++;
} return length;
}
/** * Return the number of days in the given Islamic year * @draft ICU 2.4
*/
int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear) const { return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
}
/** * Override Calendar to compute several fields specific to the Islamic * calendar system. These are: * * <ul><li>ERA * <li>YEAR * <li>MONTH * <li>DAY_OF_MONTH * <li>DAY_OF_YEAR * <li>EXTENDED_YEAR</ul> * * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this * method is called. The getGregorianXxx() methods return Gregorian * calendar equivalents for the given Julian day. * @draft ICU 2.4
*/ void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { if (U_FAILURE(status)) { return;
}
int32_t days = julianDay - getEpoc();
// Use the civil calendar approximation, which is just arithmetic
int64_t year =
ClockMath::floorDivideInt64(30LL * days + 10646LL, 10631LL);
int32_t month = static_cast<int32_t>(
uprv_ceil((days - 29 - yearStart(year, status)) / 29.5 )); if (U_FAILURE(status)) { return;
}
month = month<11?month:11;
int64_t dayOfMonth = (days - monthStart(year, month, status)) + 1; if (U_FAILURE(status)) { return;
} if (dayOfMonth > INT32_MAX || dayOfMonth < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
// Now figure out the day of the year.
int64_t dayOfYear = (days - monthStart(year, 0, status)) + 1; if (U_FAILURE(status)) { return;
} if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
/** * Return the day # on which the given year starts. Days are counted * from the Hijri epoch, origin 0.
*/
int64_t IslamicUmalquraCalendar::yearStart(int32_t year, UErrorCode& status) const { if (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END) { return IslamicCivilCalendar::yearStart(year, status);
}
year -= UMALQURA_YEAR_START; // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration
int64_t yrStartLinearEstimate = static_cast<int64_t>(
(354.36720 * static_cast<double>(year)) + 460322.05 + 0.5); // need a slight correction to some return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year];
}
/** * Return the day # on which the given month starts. Days are counted * from the Hijri epoch, origin 0. * * @param year The hijri year * @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
int64_t ms = yearStart(year, status); if (U_FAILURE(status)) { return 0;
} for(int i=0; i< month; i++){
ms+= handleGetMonthLength(year, i, status); if (U_FAILURE(status)) { return 0;
}
} return ms;
}
/** * Return the length (in days) of the given month. * * @param year The hijri year * @param year The hijri month, 0-based
*/
int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
UErrorCode& status) const { if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) { return IslamicCivilCalendar::handleGetMonthLength(extendedYear, month, status);
}
int32_t length = 29;
int32_t mask = static_cast<int32_t>(0x01 << (11 - month)); // set mask for bit corresponding to month
int32_t index = extendedYear - UMALQURA_YEAR_START; if ((UMALQURA_MONTHLENGTH[index] & mask) != 0) {
length++;
} return length;
}
int32_t IslamicUmalquraCalendar::yearLength(int32_t extendedYear, UErrorCode& status) const { if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) { return IslamicCivilCalendar::handleGetYearLength(extendedYear);
} int length = 0; for(int i=0; i<12; i++) {
length += handleGetMonthLength(extendedYear, i, status); if (U_FAILURE(status)) { return 0;
}
} return length;
}
/** * Return the number of days in the given Islamic year * @draft ICU 2.4
*/
int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const {
UErrorCode status = U_ZERO_ERROR;
int32_t length = yearLength(extendedYear, status); if (U_FAILURE(status)) { // fallback to normal Islamic calendar length 355 day a year if we // encounter error and cannot propagate. return 355;
} return length;
}
/** * Override Calendar to compute several fields specific to the Islamic * calendar system. These are: * * <ul><li>ERA * <li>YEAR * <li>MONTH * <li>DAY_OF_MONTH * <li>DAY_OF_YEAR * <li>EXTENDED_YEAR</ul> * * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this * method is called. The getGregorianXxx() methods return Gregorian * calendar equivalents for the given Julian day. * @draft ICU 2.4
*/ void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { if (U_FAILURE(status)) { return;
}
int64_t year;
int32_t month;
int32_t days = julianDay - getEpoc();
static int64_t kUmalquraStart = yearStart(UMALQURA_YEAR_START, status); if (U_FAILURE(status)) { return;
} if (days < kUmalquraStart) {
IslamicCivilCalendar::handleComputeFields(julianDay, status); return;
} // Estimate a value y which is closer to but not greater than the year. // It is the inverse function of the logic inside // IslamicUmalquraCalendar::yearStart().
year = ((static_cast<double>(days) - (460322.05 + 0.5)) / 354.36720) + UMALQURA_YEAR_START - 1;
month = 0;
int32_t d = 1; // need a slight correction to some while (d > 0) {
d = days - yearStart(++year, status) + 1;
int32_t length = yearLength(year, status); if (U_FAILURE(status)) { return;
} if (d == length) {
month = 11; break;
} if (d < length){
int32_t monthLen = handleGetMonthLength(year, month, status); for (month = 0;
d > monthLen;
monthLen = handleGetMonthLength(year, ++month, status)) { if (U_FAILURE(status)) { return;
}
d -= monthLen;
} break;
}
}
int32_t dayOfMonth = monthStart(year, month, status);
int32_t dayOfYear = monthStart(year, 0, status); if (U_FAILURE(status)) { return;
} if (uprv_mul32_overflow(dayOfMonth, -1, &dayOfMonth) ||
uprv_add32_overflow(dayOfMonth, days, &dayOfMonth) ||
uprv_add32_overflow(dayOfMonth, 1, &dayOfMonth) || // Now figure out the day of the year.
uprv_mul32_overflow(dayOfYear, -1, &dayOfYear) ||
uprv_add32_overflow(dayOfYear, days, &dayOfYear) ||
uprv_add32_overflow(dayOfYear, 1, &dayOfYear)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.