Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) { CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_rangeOfUnit:startTime:interval:forAT:", unit, startp, tip, at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (kCFCalendarUnitWeekdayOrdinal == unit) return false; if (kCFCalendarUnitWeekday == unit) unit = kCFCalendarUnitDay; if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { ucal_clear(calendar->_cal); __CFCalendarSetToFirstInstant(calendar, unit, at); UErrorCode status = U_ZERO_ERROR; UDate start = ucal_getMillis(calendar->_cal, &status); UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); ucal_add(calendar->_cal, field, 1, &status); UDate end = ucal_getMillis(calendar->_cal, &status); if (end == start && kCFCalendarUnitEra == unit) { // ICU refuses to do the addition, probably because we are // at the limit of UCAL_ERA. Use alternate strategy. CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); if (100000 < limit) limit = 100000; ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); end = ucal_getMillis(calendar->_cal, &status); } if (U_SUCCESS(status)) { if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970; if (tip) *tip = (double)(end - start) / 1000.0; return true; } } return false; }
Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int *vector, int count) { if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { UErrorCode status = U_ZERO_ERROR; ucal_clear(calendar->_cal); UDate udate = floor((*atp + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); char ch = *componentDesc; while (ch) { UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); int amount = *vector; if (options & kCFCalendarComponentsWrap) { ucal_roll(calendar->_cal, field, amount, &status); } else { ucal_add(calendar->_cal, field, amount, &status); } vector++; componentDesc++; ch = *componentDesc; } udate = ucal_getMillis(calendar->_cal, &status); *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; return U_SUCCESS(status) ? true : false; } return false; }
CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { CFIndex result = kCFNotFound; if (!__validUnits(smallerUnit, biggerUnit)) return result; CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex, calendar, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { UErrorCode status = U_ZERO_ERROR; ucal_clear(calendar->_cal); if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); return val; } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_MONTH, &status); return val; } UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); // Set calendar to first instant of big unit __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); UDate curr = ucal_getMillis(calendar->_cal, &status); UDate goal = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); result = 1; const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); Boolean divide = false, alwaysDivide = false; while (curr < goal) { ucal_add(calendar->_cal, smallField, multiple, &status); UDate newcurr = ucal_getMillis(calendar->_cal, &status); if (curr < newcurr && newcurr <= goal) { result += multiple; curr = newcurr; } else { // Either newcurr is going backwards, or not making // progress, or has overshot the goal; reset date // and try smaller multiples. ucal_setMillis(calendar->_cal, curr, &status); divide = true; // once we start overshooting the goal, the add at // smaller multiples will succeed at most once for // each multiple, so we reduce it every time through // the loop. if (goal < newcurr) alwaysDivide = true; } if (divide) { multiple = multiple / 2; if (0 == multiple) break; divide = alwaysDivide; } } } return result; }
/** * call-seq: * calendar.add(field, int_amount) * * Add a specified signed amount to a particular field in a UCalendar. * * c.add(:week_of_year, 2) */ VALUE icu4r_cal_add(VALUE obj, VALUE field, VALUE amount) { UErrorCode status = U_ZERO_ERROR; int date_field; Check_Type(field, T_SYMBOL); Check_Type(amount, T_FIXNUM); date_field = icu4r_get_cal_field_int(field); ucal_add(UCALENDAR(obj), date_field, FIX2INT(amount) , &status); ICU_RAISE(status); return Qnil; }
Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int **vector, int count) { if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { UErrorCode status = U_ZERO_ERROR; ucal_clear(calendar->_cal); UDate curr = floor((startingAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); UDate goal = floor((resultAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, curr, &status); int direction = (startingAT <= resultAT) ? 1 : -1; char ch = *componentDesc; while (ch) { UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]); Boolean divide = false, alwaysDivide = false; int result = 0; while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) { ucal_add(calendar->_cal, field, multiple, &status); UDate newcurr = ucal_getMillis(calendar->_cal, &status); if ((direction > 0 && curr < newcurr && newcurr <= goal) || (direction < 0 && newcurr < curr && goal <= newcurr)) { result += multiple; curr = newcurr; } else { // Either newcurr is going backwards, or not making // progress, or has overshot the goal; reset date // and try smaller multiples. ucal_setMillis(calendar->_cal, curr, &status); divide = true; // once we start overshooting the goal, the add at // smaller multiples will succeed at most once for // each multiple, so we reduce it every time through // the loop. if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true; } if (divide) { multiple = multiple / 2; if (0 == multiple) break; divide = alwaysDivide; } } *(*vector) = result; vector++; componentDesc++; ch = *componentDesc; } return U_SUCCESS(status) ? true : false; } return false; }
/* Function: GetJapaneseEraInfo Gets the starting Gregorian date of the specified Japanese Era. */ int32_t GlobalizationNative_GetJapaneseEraStartDate(int32_t era, int32_t* startYear, int32_t* startMonth, int32_t* startDay) { *startYear = -1; *startMonth = -1; *startDay = -1; UErrorCode err = U_ZERO_ERROR; UCalendar* pCal = ucal_open(NULL, 0, JAPANESE_LOCALE_AND_CALENDAR, UCAL_TRADITIONAL, &err); if (U_FAILURE(err)) return FALSE; ucal_set(pCal, UCAL_ERA, era); ucal_set(pCal, UCAL_YEAR, 1); // UCAL_EXTENDED_YEAR is the gregorian year for the JapaneseCalendar *startYear = ucal_get(pCal, UCAL_EXTENDED_YEAR, &err); if (U_FAILURE(err)) { ucal_close(pCal); return FALSE; } // set the date to Jan 1 ucal_set(pCal, UCAL_MONTH, 0); ucal_set(pCal, UCAL_DATE, 1); int32_t currentEra; for (int i = 0; U_SUCCESS(err) && i <= 12; i++) { currentEra = ucal_get(pCal, UCAL_ERA, &err); if (currentEra == era) { for (int i = 0; U_SUCCESS(err) && i < 31; i++) { // subtract 1 day at a time until we get out of the specified Era ucal_add(pCal, UCAL_DATE, -1, &err); currentEra = ucal_get(pCal, UCAL_ERA, &err); if (U_SUCCESS(err) && currentEra != era) { // add back 1 day to get back into the specified Era ucal_add(pCal, UCAL_DATE, 1, &err); *startMonth = ucal_get(pCal, UCAL_MONTH, &err) + 1; // ICU Calendar months are 0-based, but .NET is 1-based *startDay = ucal_get(pCal, UCAL_DATE, &err); ucal_close(pCal); return UErrorCodeToBool(err); } } } // add 1 month at a time until we get into the specified Era ucal_add(pCal, UCAL_MONTH, 1, &err); } ucal_close(pCal); return FALSE; }
// This function from ICU 4.2 static int32_t dateFieldDifference(UCalendar* cal, UDate targetMs, UCalendarDateFields field, UErrorCode& status) { UDate startMs, ms; if (U_FAILURE(status)) return 0; int32_t min = 0; startMs = ucal_getMillis(cal, &status); if (U_FAILURE(status)) return 0; // Always add from the start millis. This accomodates // operations like adding years from February 29, 2000 up to // February 29, 2004. If 1, 1, 1, 1 is added to the year // field, the DOM gets pinned to 28 and stays there, giving an // incorrect DOM difference of 1. We have to add 1, reset, 2, // reset, 3, reset, 4. if (startMs < targetMs) { int32_t max = 1; // Find a value that is too large while (U_SUCCESS(status)) { ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, max, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return max; } else if (ms > targetMs) { break; } else { min = max; max <<= 1; if (max < 0) { // Field difference too large to fit into int32_t status = U_ILLEGAL_ARGUMENT_ERROR; } } } // Do a binary search while ((max - min) > 1 && U_SUCCESS(status)) { int32_t t = (min + max) / 2; ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, t, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return t; } else if (ms > targetMs) { max = t; } else { min = t; } } } else if (startMs > targetMs) { int32_t max = -1; // Find a value that is too small while (U_SUCCESS(status)) { ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, max, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return max; } else if (ms < targetMs) { break; } else { min = max; max <<= 1; if (max == 0) { // Field difference too large to fit into int32_t status = U_ILLEGAL_ARGUMENT_ERROR; } } } // Do a binary search while ((min - max) > 1 && U_SUCCESS(status)) { int32_t t = (min + max) / 2; ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, t, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return t; } else if (ms < targetMs) { max = t; } else { min = t; } } } // Set calendar to end point ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, min, &status); /* Test for buffer overflows */ if(U_FAILURE(status)) { return 0; } return min; }
static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); CFRange range = {kCFNotFound, kCFNotFound}; if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { switch (smallerUnit) { case kCFCalendarUnitSecond: switch (biggerUnit) { case kCFCalendarUnitMinute: case kCFCalendarUnitHour: case kCFCalendarUnitDay: case kCFCalendarUnitWeekday: case kCFCalendarUnitWeek: case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: // goto calculate; range.location = 0; range.length = 60; break; } break; case kCFCalendarUnitMinute: switch (biggerUnit) { case kCFCalendarUnitHour: case kCFCalendarUnitDay: case kCFCalendarUnitWeekday: case kCFCalendarUnitWeek: case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: // goto calculate; range.location = 0; range.length = 60; break; } break; case kCFCalendarUnitHour: switch (biggerUnit) { case kCFCalendarUnitDay: case kCFCalendarUnitWeekday: case kCFCalendarUnitWeek: case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: // goto calculate; range.location = 0; range.length = 24; break; } break; case kCFCalendarUnitDay: switch (biggerUnit) { case kCFCalendarUnitWeek: case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: goto calculate; break; } break; case kCFCalendarUnitWeekday: switch (biggerUnit) { case kCFCalendarUnitWeek: case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: goto calculate; break; } break; case kCFCalendarUnitWeekdayOrdinal: switch (biggerUnit) { case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: goto calculate; break; } break; case kCFCalendarUnitWeek: switch (biggerUnit) { case kCFCalendarUnitMonth: case kCFCalendarUnitYear: case kCFCalendarUnitEra: goto calculate; break; } break; case kCFCalendarUnitMonth: switch (biggerUnit) { case kCFCalendarUnitYear: case kCFCalendarUnitEra: goto calculate; break; } break; case kCFCalendarUnitYear: switch (biggerUnit) { case kCFCalendarUnitEra: goto calculate; break; } break; case kCFCalendarUnitEra: break; } } return range; calculate:; ucal_clear(calendar->_cal); UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit); UCalendarDateFields yearField = __CFCalendarGetICUFieldCode(kCFCalendarUnitYear); UCalendarDateFields fieldToAdd = smallField; if (kCFCalendarUnitWeekday == smallerUnit) { fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitDay); } int32_t dow = -1; if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { UErrorCode status = U_ZERO_ERROR; UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek); } // Set calendar to first instant of big unit __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { UErrorCode status = U_ZERO_ERROR; // roll day forward to first 'dow' while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) { ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); } while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); } } int32_t minSmallValue = INT32_MAX; int32_t maxSmallValue = INT32_MIN; UErrorCode status = U_ZERO_ERROR; int32_t bigValue = ucal_get(calendar->_cal, bigField, &status); for (;;) { int32_t smallValue = ucal_get(calendar->_cal, smallField, &status); if (smallValue < minSmallValue) minSmallValue = smallValue; if (smallValue > maxSmallValue) maxSmallValue = smallValue; ucal_add(calendar->_cal, fieldToAdd, 1, &status); if (bigValue != ucal_get(calendar->_cal, bigField, &status)) break; if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) break; // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time } status = U_ZERO_ERROR; range.location = minSmallValue; if (smallerUnit == kCFCalendarUnitMonth) range.location = 1; range.length = maxSmallValue - minSmallValue + 1; if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) range.length = 100000; return range; }
static CFRange __CFCalendarGetRangeOfUnit1(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { CFRange range = {kCFNotFound, kCFNotFound}; if (!__validUnits(smallerUnit, biggerUnit)) return range; CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { int32_t dow = -1; ucal_clear(calendar->_cal); UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit); if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { UErrorCode status = U_ZERO_ERROR; UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); } // Set calendar to first instant of big unit __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); UErrorCode status = U_ZERO_ERROR; UDate start = ucal_getMillis(calendar->_cal, &status); if (kCFCalendarUnitWeek == biggerUnit) { range.location = ucal_get(calendar->_cal, smallField, &status); if (kCFCalendarUnitMonth == smallerUnit) range.location++; } else { range.location = (kCFCalendarUnitHour == smallerUnit || kCFCalendarUnitMinute == smallerUnit || kCFCalendarUnitSecond == smallerUnit) ? 0 : 1; } // Set calendar to first instant of next value of big unit if (UCAL_ERA == bigField) { // ICU refuses to do the addition, probably because we are // at the limit of UCAL_ERA. Use alternate strategy. CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); if (100000 < limit) limit = 100000; ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); } else { ucal_add(calendar->_cal, bigField, 1, &status); } if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { ucal_add(calendar->_cal, UCAL_SECOND, -1, &status); range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); while (1 == range.length) { ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status); range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); } range.location = 1; return range; } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { ucal_add(calendar->_cal, UCAL_SECOND, -1, &status); range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); range.location = 1; return range; } UDate goal = ucal_getMillis(calendar->_cal, &status); // Set calendar back to first instant of big unit ucal_setMillis(calendar->_cal, start, &status); if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { // roll day forward to first 'dow' while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) { ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); } while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); } start = ucal_getMillis(calendar->_cal, &status); goal -= 1000; range.location = 1; // constant here works around ICU -- see 3948293 } UDate curr = start; range.length = (kCFCalendarUnitWeekdayOrdinal == smallerUnit) ? 1 : 0; const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); Boolean divide = false, alwaysDivide = false; while (curr < goal) { ucal_add(calendar->_cal, smallField, multiple, &status); UDate newcurr = ucal_getMillis(calendar->_cal, &status); if (curr < newcurr && newcurr <= goal) { range.length += multiple; curr = newcurr; } else { // Either newcurr is going backwards, or not making // progress, or has overshot the goal; reset date // and try smaller multiples. ucal_setMillis(calendar->_cal, curr, &status); divide = true; // once we start overshooting the goal, the add at // smaller multiples will succeed at most once for // each multiple, so we reduce it every time through // the loop. if (goal < newcurr) alwaysDivide = true; } if (divide) { multiple = multiple / 2; if (0 == multiple) break; divide = alwaysDivide; } } } return range; }
static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at) { // Set UCalendar to first instant of unit prior to 'at' UErrorCode status = U_ZERO_ERROR; UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); int target_era = INT_MIN; switch (unit) { // largest to smallest, we set the fields to their minimum value case kCFCalendarUnitWeek: { // reduce to first day of week, then reduce the rest of the day int32_t goal = ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK); int32_t dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); while (dow != goal) { ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status); dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); } goto day; } case kCFCalendarUnitEra: { target_era = ucal_get(calendar->_cal, UCAL_ERA, &status); ucal_set(calendar->_cal, UCAL_YEAR, ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_ACTUAL_MINIMUM, &status)); } case kCFCalendarUnitYear: ucal_set(calendar->_cal, UCAL_MONTH, ucal_getLimit(calendar->_cal, UCAL_MONTH, UCAL_ACTUAL_MINIMUM, &status)); case kCFCalendarUnitMonth: ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status)); case kCFCalendarUnitWeekday: case kCFCalendarUnitDay: day:; ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status)); case kCFCalendarUnitHour: ucal_set(calendar->_cal, UCAL_MINUTE, ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status)); case kCFCalendarUnitMinute: ucal_set(calendar->_cal, UCAL_SECOND, ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status)); case kCFCalendarUnitSecond: ucal_set(calendar->_cal, UCAL_MILLISECOND, 0); } if (INT_MIN != target_era && ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { // In the Japanese calendar, and possibly others, eras don't necessarily // start on the first day of a year, so the previous code may have backed // up into the previous era, and we have to correct forward. UDate bad_udate = ucal_getMillis(calendar->_cal, &status); ucal_add(calendar->_cal, UCAL_MONTH, 1, &status); while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { bad_udate = ucal_getMillis(calendar->_cal, &status); ucal_add(calendar->_cal, UCAL_MONTH, 1, &status); } udate = ucal_getMillis(calendar->_cal, &status); // target date is between bad_udate and udate for (;;) { UDate test_udate = (udate + bad_udate) / 2; ucal_setMillis(calendar->_cal, test_udate, &status); if (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { bad_udate = test_udate; } else { udate = test_udate; } if (fabs(udate - bad_udate) < 1000) break; } do { bad_udate = floor((bad_udate + 1000) / 1000) * 1000; ucal_setMillis(calendar->_cal, bad_udate, &status); } while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era); } }
/* This function is from ICU 4.2. */ static int32_t dateFieldDifference(UCalendar* cal, UDate targetMs, UCalendarDateFields field, UErrorCode& status) { UDate startMs, ms; if (U_FAILURE(status)) return 0; int32_t min = 0; startMs = ucal_getMillis(cal, &status); if (U_FAILURE(status)) return 0; /* Always add from the start millis. This accomodates operations like adding years from February 29, 2000 up to February 29, 2004. If 1, 1, 1, 1 is added to the year field, the DOM gets pinned to 28 and stays there, giving an incorrect DOM difference of 1. We have to add 1, reset, 2, reset, 3, reset, 4. */ if (startMs < targetMs) { int32_t max = 1; /* Find a value that is too large */ while (U_SUCCESS(status)) { ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, max, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return max; } else if (ms > targetMs) { break; } else { min = max; max <<= 1; if (max < 0) { /* Field difference too large to fit into int32_t */ status = U_UNSUPPORTED_ERROR; } } } /* Do a binary search */ while ((max - min) > 1 && U_SUCCESS(status)) { int32_t t = (min + max) / 2; ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, t, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return t; } else if (ms > targetMs) { max = t; } else { min = t; } } /* It is an bad type for this algorithm. */ if (ms < startMs) status = U_UNSUPPORTED_ERROR; } else if (startMs > targetMs) { int32_t max = -1; /* Find a value that is too small */ while (U_SUCCESS(status)) { ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, max, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return max; } else if (ms < targetMs) { break; } else { min = max; max <<= 1; if (max == 0) { /* Field difference too large to fit into int32_t */ status = U_UNSUPPORTED_ERROR; } } } /* Do a binary search */ while ((min - max) > 1 && U_SUCCESS(status)) { int32_t t = (min + max) / 2; ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, t, &status); ms = ucal_getMillis(cal, &status); if (ms == targetMs) { return t; } else if (ms < targetMs) { max = t; } else { min = t; } } /* It is an bad type for this algorithm. For example, i18n_date:difference(i18n_date:new(-2000,1,1), i18n_date:new(2000,1,1),year). */ if (U_SUCCESS(status) && (ms > startMs)) status = U_UNSUPPORTED_ERROR; } /* Set calendar to end point */ ucal_setMillis(cal, startMs, &status); ucal_add(cal, field, min, &status); /* Test for buffer overflows */ if(U_FAILURE(status)) { return 0; } return min; }
/* Print out a calendar for c's current year */ static void print_year(UCalendar *c, UChar *days [], UChar *months [], UBool useLongNames, int32_t fdow, UErrorCode *status) { int32_t width, pad, i, j; int32_t lens [DAY_COUNT]; UNumberFormat *nfmt; UDateFormat *dfmt; UChar s [BUF_SIZE]; const UChar pat [] = { 0x0079, 0x0079, 0x0079, 0x0079 }; int32_t len = 4; UCalendar *left_cal, *right_cal; int32_t left_day, right_day; int32_t left_firstday, right_firstday, left_current, right_current; int32_t left_month, right_month; if(U_FAILURE(*status)) return; /* Alias */ left_cal = c; /* ========== Generate the header containing the year (only) */ /* Open a formatter with a month and year only pattern */ dfmt = udat_open(UDAT_IGNORE,UDAT_IGNORE,NULL,NULL,0,pat, len, status); /* Format the date */ udat_format(dfmt, ucal_getMillis(left_cal, status), s, BUF_SIZE, 0, status); /* ========== Get the month and day names */ get_days(dfmt, days, useLongNames, fdow, status); get_months(dfmt, months, useLongNames, status); /* ========== Print the header, centered */ /* Calculate widths for justification */ width = 6; /* 6 spaces, 1 between each day name */ for(i = 0; i < DAY_COUNT; ++i) { lens[i] = u_strlen(days[i]); width += lens[i]; } /* width is the width for 1 calendar; we are displaying in 2 cols with MARGIN_WIDTH spaces between months */ /* Print the header, centered among the day names */ pad = 2 * width + MARGIN_WIDTH - u_strlen(s); indent(pad / 2, stdout); uprint(s, stdout, status); putc('\n', stdout); putc('\n', stdout); /* Generate a copy of the calendar to use */ right_cal = ucal_open(0, -1, uloc_getDefault(), UCAL_TRADITIONAL, status); ucal_setMillis(right_cal, ucal_getMillis(left_cal, status), status); /* Open the formatter */ nfmt = unum_open(UNUM_DECIMAL,NULL, 0,NULL,NULL, status); /* ========== Calculate and display the months, two at a time */ for(i = 0; i < MONTH_COUNT - 1; i += 2) { /* Print the month names for the two current months */ pad = width - u_strlen(months[i]); indent(pad / 2, stdout); uprint(months[i], stdout, status); indent(pad / 2 + MARGIN_WIDTH, stdout); pad = width - u_strlen(months[i + 1]); indent(pad / 2, stdout); uprint(months[i + 1], stdout, status); putc('\n', stdout); /* Print the day names, twice */ print_days(days, stdout, status); indent(MARGIN_WIDTH, stdout); print_days(days, stdout, status); putc('\n', stdout); /* Setup the two calendars */ ucal_set(left_cal, UCAL_MONTH, i); ucal_set(left_cal, UCAL_DATE, 1); ucal_set(right_cal, UCAL_MONTH, i + 1); ucal_set(right_cal, UCAL_DATE, 1); left_firstday = ucal_get(left_cal, UCAL_DAY_OF_WEEK, status); right_firstday = ucal_get(right_cal, UCAL_DAY_OF_WEEK, status); /* The day of the week for the first day of the month is based on 1-based days of the week. However, the days were reordered when placed in the days array. Account for this here by offsetting by the first day of the week for the locale, which is also 1-based. */ /* We need to mod by DAY_COUNT since fdow can be > firstday. IE, if fdow = 2 = Monday (like in France) and the first day of the month is a 1 = Sunday, we want firstday to be 6, not -1 */ left_firstday += (DAY_COUNT - fdow); left_firstday %= DAY_COUNT; right_firstday += (DAY_COUNT - fdow); right_firstday %= DAY_COUNT; left_current = left_firstday; right_current = right_firstday; left_day = ucal_get(left_cal, UCAL_DATE, status); right_day = ucal_get(right_cal, UCAL_DATE, status); left_month = ucal_get(left_cal, UCAL_MONTH, status); right_month = ucal_get(right_cal, UCAL_MONTH, status); /* Finally, print out the days */ while(left_month == i || right_month == i + 1) { /* If the left month is finished printing, but the right month still has days to be printed, indent the width of the days strings and reset the left calendar's current day to 0 */ if(left_month != i && right_month == i + 1) { indent(width + 1, stdout); left_current = 0; } while(left_month == i) { /* If the day is the first, indent the correct number of spaces for the first week */ if(left_day == 1) { for(j = 0; j < left_current; ++j) indent(lens[j] + 1, stdout); } /* Format the current day string */ unum_format(nfmt, left_day, s, BUF_SIZE, 0, status); /* Calculate the justification and indent */ pad = lens[left_current] - u_strlen(s); indent(pad, stdout); /* Print the day number out, followed by a space */ uprint(s, stdout, status); putc(' ', stdout); /* Update the current day */ ++left_current; left_current %= DAY_COUNT; /* Go to the next day */ ucal_add(left_cal, UCAL_DATE, 1, status); left_day = ucal_get(left_cal, UCAL_DATE, status); /* Determine the month */ left_month = ucal_get(left_cal, UCAL_MONTH, status); /* If we're at day 0 (first day of the week), break and go to the next month */ if(left_current == 0) { break; } }; /* If the current day isn't 0, indent to make up for missing days at the end of the month */ if(left_current != 0) { for(j = left_current; j < DAY_COUNT; ++j) indent(lens[j] + 1, stdout); } /* Indent between the two months */ indent(MARGIN_WIDTH, stdout); while(right_month == i + 1) { /* If the day is the first, indent the correct number of spaces for the first week */ if(right_day == 1) { for(j = 0; j < right_current; ++j) indent(lens[j] + 1, stdout); } /* Format the current day string */ unum_format(nfmt, right_day, s, BUF_SIZE, 0, status); /* Calculate the justification and indent */ pad = lens[right_current] - u_strlen(s); indent(pad, stdout); /* Print the day number out, followed by a space */ uprint(s, stdout, status); putc(' ', stdout); /* Update the current day */ ++right_current; right_current %= DAY_COUNT; /* Go to the next day */ ucal_add(right_cal, UCAL_DATE, 1, status); right_day = ucal_get(right_cal, UCAL_DATE, status); /* Determine the month */ right_month = ucal_get(right_cal, UCAL_MONTH, status); /* If we're at day 0 (first day of the week), break out */ if(right_current == 0) { break; } }; /* Output a newline */ putc('\n', stdout); } /* Output a trailing newline */ putc('\n', stdout); } /* Clean up */ free_months(months); free_days(days); udat_close(dfmt); unum_close(nfmt); ucal_close(right_cal); }
/* Print out a calendar for c's current month */ static void print_month(UCalendar *c, UChar *days [], UBool useLongNames, int32_t fdow, UErrorCode *status) { int32_t width, pad, i, day; int32_t lens [DAY_COUNT]; int32_t firstday, current; UNumberFormat *nfmt; UDateFormat *dfmt; UChar s [BUF_SIZE]; const UChar *pat = (useLongNames ? sLongPat : sShortPat); int32_t len = (useLongNames ? 9 : 8); if(U_FAILURE(*status)) return; /* ========== Generate the header containing the month and year */ /* Open a formatter with a month and year only pattern */ dfmt = udat_open(UDAT_IGNORE,UDAT_IGNORE,NULL,NULL,0,pat, len,status); /* Format the date */ udat_format(dfmt, ucal_getMillis(c, status), s, BUF_SIZE, 0, status); /* ========== Get the day names */ get_days(dfmt, days, useLongNames, fdow, status); /* ========== Print the header */ /* Calculate widths for justification */ width = 6; /* 6 spaces, 1 between each day name */ for(i = 0; i < DAY_COUNT; ++i) { lens[i] = u_strlen(days[i]); width += lens[i]; } /* Print the header, centered among the day names */ pad = width - u_strlen(s); indent(pad / 2, stdout); uprint(s, stdout, status); putc('\n', stdout); /* ========== Print the day names */ print_days(days, stdout, status); putc('\n', stdout); /* ========== Print the calendar */ /* Get the first of the month */ ucal_set(c, UCAL_DATE, 1); firstday = ucal_get(c, UCAL_DAY_OF_WEEK, status); /* The day of the week for the first day of the month is based on 1-based days of the week, which were also reordered when placed in the days array. Account for this here by offsetting by the first day of the week for the locale, which is also 1-based. */ firstday -= fdow; /* Open the formatter */ nfmt = unum_open(UNUM_DECIMAL, NULL,0,NULL,NULL, status); /* Indent the correct number of spaces for the first week */ current = firstday; if(current < 0) { current += 7; } for(i = 0; i < current; ++i) indent(lens[i] + 1, stdout); /* Finally, print out the days */ day = ucal_get(c, UCAL_DATE, status); do { /* Format the current day string */ unum_format(nfmt, day, s, BUF_SIZE, 0, status); /* Calculate the justification and indent */ pad = lens[current] - u_strlen(s); indent(pad, stdout); /* Print the day number out, followed by a space */ uprint(s, stdout, status); putc(' ', stdout); /* Update the current day */ ++current; current %= DAY_COUNT; /* If we're at day 0 (first day of the week), insert a newline */ if(current == 0) { putc('\n', stdout); } /* Go to the next day */ ucal_add(c, UCAL_DATE, 1, status); day = ucal_get(c, UCAL_DATE, status); } while(day != 1); /* Output a trailing newline */ putc('\n', stdout); /* Clean up */ free_days(days); unum_close(nfmt); udat_close(dfmt); }