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; }
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; }
void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) { CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setFirstWeekday:wkdy); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { ucal_setAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy); } }
CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) { CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, firstWeekday); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK); } return -1; }
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; }
CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar) { CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFDateRef, calendar, _gregorianStartDate); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); UErrorCode status = U_ZERO_ERROR; UDate udate = calendar->_cal ? ucal_getGregorianChange(calendar->_cal, &status) : 0; if (calendar->_cal && U_SUCCESS(status)) { CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; return CFDateCreate(CFGetAllocator(calendar), at); } return NULL; }
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; }
CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_maximumRangeOfUnit:", unit); CFRange range = {kCFNotFound, kCFNotFound}; __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { ucal_clear(calendar->_cal); UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); UErrorCode status = U_ZERO_ERROR; range.location = ucal_getLimit(calendar->_cal, field, UCAL_MINIMUM, &status); range.length = ucal_getLimit(calendar->_cal, field, UCAL_MAXIMUM, &status) - range.location + 1; if (UCAL_MONTH == field) range.location++; if (100000 < range.length) range.length = 100000; } return range; }
Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int *vector, int count) { if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) { UErrorCode status = U_ZERO_ERROR; ucal_clear(calendar->_cal); ucal_set(calendar->_cal, UCAL_YEAR, 1); ucal_set(calendar->_cal, UCAL_MONTH, 0); ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, 1); ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, 0); ucal_set(calendar->_cal, UCAL_MINUTE, 0); ucal_set(calendar->_cal, UCAL_SECOND, 0); const char *desc = componentDesc; Boolean doWOY = false; char ch = *desc; while (ch) { UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); if (UCAL_WEEK_OF_YEAR == field) { doWOY = true; } desc++; ch = *desc; } desc = componentDesc; ch = *desc; while (ch) { UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); int value = *vector; if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY; if (UCAL_MONTH == field) value--; ucal_set(calendar->_cal, field, value); vector++; desc++; ch = *desc; } UDate udate = ucal_getMillis(calendar->_cal, &status); CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; if (atp) *atp = at; return U_SUCCESS(status) ? true : false; } return false; }
Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, 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((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); ucal_setMillis(calendar->_cal, udate, &status); char ch = *componentDesc; while (ch) { UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); int value = ucal_get(calendar->_cal, field, &status); if (UCAL_MONTH == field) value++; *(*vector) = value; vector++; componentDesc++; ch = *componentDesc; } return U_SUCCESS(status) ? true : false; } return false; }
void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date) { CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, _setGregorianStartDate:date); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (date) __CFGenericValidateType(date, CFDateGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (!calendar->_cal) return; if (!date) { UErrorCode status = U_ZERO_ERROR; UCalendar *cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); UDate udate = cal ? ucal_getGregorianChange(cal, &status) : 0; if (cal && U_SUCCESS(status)) { status = U_ZERO_ERROR; if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status); } if (cal) ucal_close(cal); } else { CFAbsoluteTime at = CFDateGetAbsoluteTime(date); UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; UErrorCode status = U_ZERO_ERROR; if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status); } }
void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) { CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setMinimumDaysInFirstWeek:mwd); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); if (calendar->_cal) ucal_setAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd); }
CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) { CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, minimumDaysInFirstWeek); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); if (!calendar->_cal) __CFCalendarSetupCal(calendar); return calendar->_cal ? ucal_getAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK) : -1; }
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; }