int64_t ChineseCalendar::handleComputeMonthStartWithLeap(int32_t eyear, int32_t month, bool isLeapMonth, UErrorCode& status) const { if (U_FAILURE(status)) { return0;
} // If the month is out of range, adjust it into range, and // modify the extended year value accordingly. if (month < 0 || month > 11) { if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return0;
}
}
output.thisMoon = day - dayOfMonth + 1; // New moon (start of this month)
// Note throughout the following: Months 12 and 1 are never // followed by a leap month (D&R p. 185).
// Compute the adjusted month number m. This is zero-based // value from 0..11 in a non-leap year, and from 0..12 in a // leap year. if (hasLeapMonthBetweenWinterSolstices) { // (member variable) if (isLeapMonth) {
++month;
} else { // Check for a prior leap month. (In the // following, month 0 is the first month of the // year.) Month 0 is never followed by a leap // month, and we know month m is not a leap month. // moon1 will be the start of month 0 if there is // no leap month between month 0 and month m; // otherwise it will be the start of month 1. int prevMoon = output.thisMoon - static_cast<int>(CalendarAstronomer::SYNODIC_MONTH * (month - 0.5));
prevMoon = newMoonNear(timeZone, prevMoon, true, status); if (U_FAILURE(status)) { return output;
} if (isLeapMonthBetween(timeZone, prevMoon, output.thisMoon, status)) {
++month;
} if (U_FAILURE(status)) { return output;
}
}
} // Now do the standard roll computation on month, with the // allowed range of 0..n-1, where n is 12 or 13.
int32_t numberOfMonths = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year if (uprv_add32_overflow(amount, month, &amount)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return output;
}
output.newMoon = amount % numberOfMonths; if (output.newMoon < 0) {
output.newMoon += numberOfMonths;
}
output.month = month; return output;
}
} // namespace
/** *OverrideCalendartohandleleapmonthsproperly. *@stableICU2.8
*/ void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) { switch (field) { case UCAL_MONTH: case UCAL_ORDINAL_MONTH: if (amount != 0) { const Setting setting = getSetting(status);
int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
int32_t month = get(UCAL_MONTH, status); // 0-based month
int32_t dayOfMonth = get(UCAL_DAY_OF_MONTH, status); bool isLeapMonth = get(UCAL_IS_LEAP_MONTH, status) == 1; if (U_FAILURE(status)) break; struct RollMonthInfo r = rollMonth(
setting.zoneAstroCalc, amount, day, month, dayOfMonth, isLeapMonth,
hasLeapMonthBetweenWinterSolstices, status); if (U_FAILURE(status)) break; if (r.newMoon != r.month) {
offsetMonth(r.thisMoon, dayOfMonth, r.newMoon - r.month, status);
}
} break; default:
Calendar::roll(field, amount, status); break;
}
}
//------------------------------------------------------------------ // Support methods and constants //------------------------------------------------------------------
if (cacheValue == 0) { // In books December 15 is used, but it fails for some years // using our algorithms, e.g.: 1298 1391 1492 1553 1560. That // is, winterSolstice(1298) starts search at Dec 14 08:00:00 // PST 1298 with a final result of Dec 14 10:31:59 PST 1299. double ms = daysToMillis(timeZone, Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1), status); if (U_FAILURE(status)) { return0;
}
// Winter solstice is 270 degrees solar longitude aka Dongzhi double days = millisToDays(timeZone,
CalendarAstronomer(ms)
.getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true),
status); if (U_FAILURE(status)) { return0;
} if (days < INT32_MIN || days > INT32_MAX) {
status = U_ILLEGAL_ARGUMENT_ERROR; return0;
}
cacheValue = static_cast<int32_t>(days);
CalendarCache::put(setting.winterSolsticeCache, gyear, cacheValue, status);
} if(U_FAILURE(status)) {
cacheValue = 0;
} return cacheValue;
}
//------------------------------------------------------------------ // Time to fields //------------------------------------------------------------------
#ifdef U_DEBUG_CHNSECAL // This is only needed to debug the timeOfAngle divergence bug. // Remove this later. Liu 11/9/00 if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
U_DEBUG_CHNSECAL_MSG(( "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2
));
} #endif
while (newMoon2 >= newMoon1) { if (hasNoMajorSolarTerm(timeZone, newMoon2, status)) { returntrue;
}
newMoon2 = newMoonNear(timeZone, newMoon2 - SYNODIC_GAP, false, status); if (U_FAILURE(status)) { returnfalse;
}
} returnfalse;
}
/** *Computetheinformationabouttheyear. *@paramsettingsetting(timezoneandcaches)fortheAstrocalculation. *@paramgyeartheGregorianyearofthegivendate *@paramdaysdaysafterJanuary1,19700:00astronomicalbasezone *ofthedatetocomputefieldsfor *@returnTheMonthInforesult.
*/ struct MonthInfo computeMonthInfo( const icu::ChineseCalendar::Setting& setting,
int32_t gyear, int32_t days, UErrorCode& status) { struct MonthInfo output = {0, 0, 0, false, false}; if (U_FAILURE(status)) { return output;
} // Find the winter solstices before and after the target date. // These define the boundaries of this Chinese year, specifically, // the position of month 11, which always contains the solstice. // We want solsticeBefore <= date < solsticeAfter.
int32_t solsticeBefore;
int32_t solsticeAfter = winterSolstice(setting, gyear, status); if (U_FAILURE(status)) { return output;
} if (days < solsticeAfter) {
int32_t gprevious_year; if (uprv_add32_overflow(gyear, -1, &gprevious_year)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return output;
}
solsticeBefore = winterSolstice(setting, gprevious_year, status);
} else {
solsticeBefore = solsticeAfter;
int32_t gnext_year; if (uprv_add32_overflow(gyear, 1, &gnext_year)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return output;
}
solsticeAfter = winterSolstice(setting, gnext_year, status);
} if (!(solsticeBefore <= days && days < solsticeAfter)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} if (U_FAILURE(status)) { return output;
}
const TimeZone* timeZone = setting.zoneAstroCalc; // Find the start of the month after month 11. This will be either // the prior month 12 or leap month 11 (very rare). Also find the // start of the following month 11.
int32_t firstMoon = newMoonNear(timeZone, solsticeBefore + 1, true, status);
int32_t lastMoon = newMoonNear(timeZone, solsticeAfter + 1, false, status); if (U_FAILURE(status)) { return output;
}
output.thisMoon = newMoonNear(timeZone, days + 1, false, status); // Start of this month if (U_FAILURE(status)) { return output;
}
output.hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
// Extended year and cycle year is based on the epoch year
int32_t eyear;
int32_t cycle_year; if (uprv_add32_overflow(gyear, -CHINESE_EPOCH_YEAR, &eyear) ||
uprv_add32_overflow(gyear, -CYCLE_EPOCH, &cycle_year)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
} if (monthInfo.month < 11 ||
gmonth >= UCAL_JULY) { // forward to next year if (uprv_add32_overflow(eyear, 1, &eyear) ||
uprv_add32_overflow(cycle_year, 1, &cycle_year)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
}
int32_t dayOfMonth = days - monthInfo.thisMoon + 1;
// Days will be before the first new year we compute if this // date is in month 11, leap 11, 12. There is never a leap 12. // New year computations are cached so this should be cheap in // the long run.
int32_t theNewYear = newYear(setting, gyear, status); if (U_FAILURE(status)) { return;
} if (days < theNewYear) {
int32_t gprevious_year; if (uprv_add32_overflow(gyear, -1, &gprevious_year)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
theNewYear = newYear(setting, gprevious_year, status);
} if (U_FAILURE(status)) { return;
}
cycle++;
yearOfCycle++;
int32_t dayOfYear = days - theNewYear + 1;
int32_t minYear = this->handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM); if (eyear < minYear) { if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
eyear = minYear;
}
int32_t maxYear = this->handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM); if (maxYear < eyear) { if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
eyear = maxYear;
}
internalSet(UCAL_MONTH, monthInfo.month-1); // Convert from 1-based to 0-based
internalSet(UCAL_ORDINAL_MONTH, monthInfo.ordinalMonth); // Convert from 1-based to 0-based
internalSet(UCAL_IS_LEAP_MONTH, monthInfo.isLeapMonth?1:0);
//------------------------------------------------------------------ // Fields to time //------------------------------------------------------------------
// Move to the middle of the month before our target month. double value = newMoon;
value += (CalendarAstronomer::SYNODIC_MONTH *
(static_cast<double>(delta) - 0.5)); if (value < INT32_MIN || value > INT32_MAX) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
newMoon = static_cast<int32_t>(value);
// Search forward to the target month's new moon
newMoon = newMoonNear(setting.zoneAstroCalc, newMoon, true, status); if (U_FAILURE(status)) { return;
}
// Find the target dayOfMonth
int32_t jd; if (uprv_add32_overflow(newMoon, kEpochStartAsJulianDay - 1, &jd) ||
uprv_add32_overflow(jd, dayOfMonth, &jd)) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
// Pin the dayOfMonth. In this calendar all months are 29 or 30 days // so pinning just means handling dayOfMonth 30. if (dayOfMonth > 29) {
set(UCAL_JULIAN_DAY, jd-1); // TODO Fix this. We really shouldn't ever have to // explicitly call complete(). This is either a bug in // this method, in ChineseCalendar, or in // Calendar.getActualMaximum(). I suspect the last.
complete(status); if (U_FAILURE(status)) return; if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dayOfMonth) { if (U_FAILURE(status)) return;
set(UCAL_JULIAN_DAY, jd);
}
} else {
set(UCAL_JULIAN_DAY, jd);
}
}
constchar* ChineseCalendar::getTemporalMonthCode(UErrorCode &status) const { // We need to call get, not internalGet, to force the calculation // from UCAL_ORDINAL_MONTH.
int32_t is_leap = get(UCAL_IS_LEAP_MONTH, status); if (U_FAILURE(status)) return nullptr; if (is_leap != 0) {
int32_t month = get(UCAL_MONTH, status); if (U_FAILURE(status)) return nullptr; return gTemporalLeapMonthCodes[month];
} return Calendar::getTemporalMonthCode(status);
}
void
ChineseCalendar::setTemporalMonthCode(constchar* code, UErrorCode& status )
{ if (U_FAILURE(status)) return;
int32_t len = static_cast<int32_t>(uprv_strlen(code)); if (len != 4 || code[0] != 'M' || code[3] != 'L') {
set(UCAL_IS_LEAP_MONTH, 0); return Calendar::setTemporalMonthCode(code, status);
} for (int m = 0; gTemporalLeapMonthCodes[m] != nullptr; m++) { if (uprv_strcmp(code, gTemporalLeapMonthCodes[m]) == 0) {
set(UCAL_MONTH, m);
set(UCAL_IS_LEAP_MONTH, 1); return;
}
}
status = U_ILLEGAL_ARGUMENT_ERROR;
}
int32_t ChineseCalendar::internalGetMonth(UErrorCode& status) const { if (U_FAILURE(status)) { return0;
} if (resolveFields(kMonthPrecedence) == UCAL_MONTH) { return internalGet(UCAL_MONTH);
}
LocalPointer<Calendar> temp(this->clone());
temp->set(UCAL_MONTH, 0);
temp->set(UCAL_IS_LEAP_MONTH, 0);
temp->set(UCAL_DATE, 1); // Calculate the UCAL_MONTH and UCAL_IS_LEAP_MONTH by adding number of // months.
temp->roll(UCAL_MONTH, internalGet(UCAL_ORDINAL_MONTH), status); if (U_FAILURE(status)) { return0;
}
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.