blob: 1c18bc782b6bd4d478bfef8ab8049af235577762 [file] [log] [blame]
/* CFCalendar.c
Copyright (c) 2004-2016, Apple Inc. and the Swift project authors
Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
Responsibility: Christopher Kane
*/
#include <CoreFoundation/CFCalendar.h>
#include <CoreFoundation/CFRuntime.h>
#include "CFInternal.h"
#include "CFPriv.h"
#include <unicode/ucal.h>
#define BUFFER_SIZE 512
struct __CFCalendar {
CFRuntimeBase _base;
CFStringRef _identifier; // canonical identifier, never NULL
CFLocaleRef _locale;
CFStringRef _localeID;
CFTimeZoneRef _tz;
UCalendar *_cal;
};
static Boolean __CFCalendarEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFCalendarRef calendar1 = (CFCalendarRef)cf1;
CFCalendarRef calendar2 = (CFCalendarRef)cf2;
return CFEqual(calendar1->_identifier, calendar2->_identifier);
}
static CFHashCode __CFCalendarHash(CFTypeRef cf) {
CFCalendarRef calendar = (CFCalendarRef)cf;
return CFHash(calendar->_identifier);
}
static CFStringRef __CFCalendarCopyDescription(CFTypeRef cf) {
CFCalendarRef calendar = (CFCalendarRef)cf;
return CFStringCreateWithFormat(CFGetAllocator(calendar), NULL, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf, CFGetAllocator(calendar), calendar->_identifier);
}
static void __CFCalendarDeallocate(CFTypeRef cf) {
CFCalendarRef calendar = (CFCalendarRef)cf;
if (calendar->_identifier) CFRelease(calendar->_identifier);
if (calendar->_locale) CFRelease(calendar->_locale);
if (calendar->_localeID) CFRelease(calendar->_localeID);
if (calendar->_identifier) CFRelease(calendar->_tz);
if (calendar->_cal) ucal_close(calendar->_cal);
}
static CFTypeID __kCFCalendarTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFCalendarClass = {
0,
"CFCalendar",
NULL, // init
NULL, // copy
__CFCalendarDeallocate,
__CFCalendarEqual,
__CFCalendarHash,
NULL, //
__CFCalendarCopyDescription
};
CFTypeID CFCalendarGetTypeID(void) {
static dispatch_once_t initOnce;
dispatch_once(&initOnce, ^{ __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass); });
return __kCFCalendarTypeID;
}
CF_PRIVATE UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) {
if (calendarID) {
CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeID);
CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, calendarID);
localeID = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
CFRelease(mcomponents);
CFRelease(components);
}
char buffer[BUFFER_SIZE];
const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII);
if (NULL == cstr) {
if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
}
if (NULL == cstr) {
if (calendarID) CFRelease(localeID);
return NULL;
}
UChar ubuffer[BUFFER_SIZE];
CFStringRef tznam = CFTimeZoneGetName(tz);
CFIndex cnt = CFStringGetLength(tznam);
if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE;
CFStringGetCharacters(tznam, CFRangeMake(0, cnt), (UniChar *)ubuffer);
UErrorCode status = U_ZERO_ERROR;
UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_DEFAULT, &status);
if (calendarID) CFRelease(localeID);
return cal;
}
static void __CFCalendarSetupCal(CFCalendarRef calendar) {
calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
}
#if DEPLOYMENT_RUNTIME_SWIFT
Boolean _CFCalendarIsWeekend(CFCalendarRef calendar, CFAbsoluteTime at) {
if (calendar->_cal == NULL) {
__CFCalendarSetupCal(calendar);
}
UDate udate = at * 1000.0;
UErrorCode status = U_ZERO_ERROR;
UBool result = ucal_isWeekend(calendar->_cal, udate, &status);
return result;
}
Boolean _CFCalendarGetNextWeekend(CFCalendarRef calendar, _CFCalendarWeekendRange *range) {
CFIndex weekdaysIndex[7];
memset(weekdaysIndex, '\0', sizeof(CFIndex)*7);
CFIndex firstWeekday = CFCalendarGetFirstWeekday(calendar);
weekdaysIndex[0] = firstWeekday;
for (CFIndex i = 1; i < 7; i++) {
weekdaysIndex[i] = (weekdaysIndex[i-1] % 7) + 1;
}
UCalendarWeekdayType weekdayTypes[7];
CFIndex onset = kCFNotFound;
CFIndex cease = kCFNotFound;
if (!calendar->_cal) __CFCalendarSetupCal(calendar);
if (!calendar->_cal) {
return false;
}
for (CFIndex i = 0; i < 7; i++) {
UErrorCode status = U_ZERO_ERROR;
weekdayTypes[i] = ucal_getDayOfWeekType(calendar->_cal, (UCalendarDaysOfWeek)weekdaysIndex[i], &status);
if (weekdayTypes[i] == UCAL_WEEKEND_ONSET) {
onset = weekdaysIndex[i];
} else if (weekdayTypes[i] == UCAL_WEEKEND_CEASE) {
cease = weekdaysIndex[i];
}
}
BOOL hasWeekend = NO;
for (CFIndex i = 0; i < 7; i++) {
if (weekdayTypes[i] == UCAL_WEEKEND || weekdayTypes[i] == UCAL_WEEKEND_ONSET || weekdayTypes[i] == UCAL_WEEKEND_CEASE) {
hasWeekend = YES;
break;
}
}
if (!hasWeekend) {
return false;
}
int32_t onsetTime = 0;
int32_t ceaseTime = 0;
if (onset != kCFNotFound) {
UErrorCode status = U_ZERO_ERROR;
onsetTime = ucal_getWeekendTransition(calendar->_cal, (UCalendarDaysOfWeek)onset, &status);
}
if (cease != kCFNotFound) {
UErrorCode status = U_ZERO_ERROR;
ceaseTime = ucal_getWeekendTransition(calendar->_cal, (UCalendarDaysOfWeek)cease, &status);
}
CFIndex weekendStart = kCFNotFound;
CFIndex weekendEnd = kCFNotFound;
if (onset != kCFNotFound) {
weekendStart = onset;
} else {
if (weekdayTypes[0] == UCAL_WEEKEND && weekdayTypes[6] == UCAL_WEEKEND) {
for (CFIndex i = 5; i >= 0; i--) {
if (weekdayTypes[i] != UCAL_WEEKEND) {
weekendStart = weekdaysIndex[i + 1];
break;
}
}
} else {
for (CFIndex i = 0; i < 7; i++) {
if (weekdayTypes[i] == UCAL_WEEKEND) {
weekendStart = weekdaysIndex[i];
break;
}
}
}
}
if (cease != kCFNotFound) {
weekendEnd = cease;
} else {
if (weekdayTypes[0] == UCAL_WEEKEND && weekdayTypes[6] == UCAL_WEEKEND) {
for (CFIndex i = 1; i < 7; i++) {
if (weekdayTypes[i] != UCAL_WEEKEND) {
weekendEnd = weekdaysIndex[i - 1];
break;
}
}
} else {
for (CFIndex i = 6; i >= 0; i--) {
if (weekdayTypes[i] == UCAL_WEEKEND) {
weekendEnd = weekdaysIndex[i];
break;
}
}
}
}
range->onsetTime = onsetTime / 1000.0;
range->ceaseTime = ceaseTime / 1000.0;
range->start = weekendStart;
range->end = weekendEnd;
return true;
}
#endif
static void __CFCalendarZapCal(CFCalendarRef calendar) {
ucal_close(calendar->_cal);
calendar->_cal = NULL;
}
CFCalendarRef CFCalendarCopyCurrent(void) {
CFLocaleRef locale = CFLocaleCopyCurrent();
CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier);
if (calID) {
CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID);
CFCalendarSetLocale(calendar, locale);
CFRelease(locale);
return calendar;
}
return NULL;
}
Boolean _CFCalendarInitWithIdentifier(CFCalendarRef calendar, CFStringRef identifier) {
if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFRepublicOfChinaCalendar && identifier != kCFPersianCalendar && identifier != kCFCalendarIdentifierCoptic && identifier != kCFCalendarIdentifierEthiopicAmeteMihret && identifier != kCFCalendarIdentifierEthiopicAmeteAlem && identifier != kCFChineseCalendar && identifier != kCFISO8601Calendar && identifier != kCFIslamicTabularCalendar && identifier != kCFIslamicUmmAlQuraCalendar) {
if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar;
else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar;
else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar;
else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar;
else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar;
else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar;
else if (CFEqual(kCFRepublicOfChinaCalendar, identifier)) identifier = kCFRepublicOfChinaCalendar;
else if (CFEqual(kCFPersianCalendar, identifier)) identifier = kCFPersianCalendar;
else if (CFEqual(kCFIndianCalendar, identifier)) identifier = kCFIndianCalendar;
else if (CFEqual(kCFCalendarIdentifierCoptic, identifier)) identifier = kCFCalendarIdentifierCoptic;
else if (CFEqual(kCFCalendarIdentifierEthiopicAmeteMihret, identifier)) identifier = kCFCalendarIdentifierEthiopicAmeteMihret;
else if (CFEqual(kCFCalendarIdentifierEthiopicAmeteAlem, identifier)) identifier = kCFCalendarIdentifierEthiopicAmeteAlem;
else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
else if (CFEqual(kCFISO8601Calendar, identifier)) identifier = kCFISO8601Calendar;
else if (CFEqual(kCFIslamicTabularCalendar, identifier)) identifier = kCFIslamicTabularCalendar;
else if (CFEqual(kCFIslamicUmmAlQuraCalendar, identifier)) identifier = kCFIslamicUmmAlQuraCalendar;
else return false;
}
calendar->_identifier = (CFStringRef)CFRetain(identifier);
calendar->_locale = NULL;
calendar->_localeID = CFRetain(CFLocaleGetIdentifier(CFLocaleGetSystem()));
calendar->_tz = CFTimeZoneCopyDefault();
calendar->_cal = NULL;
return true;
}
CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) {
if (allocator == NULL) allocator = __CFGetDefaultAllocator();
__CFGenericValidateType(allocator, CFAllocatorGetTypeID());
__CFGenericValidateType(identifier, CFStringGetTypeID());
// return NULL until Chinese calendar is available
if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) {
// if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar;
else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar;
else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar;
else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar;
else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar;
else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar;
else if (CFEqual(kCFISO8601Calendar, identifier)) identifier = kCFISO8601Calendar;
// else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
else return NULL;
}
struct __CFCalendar *calendar = NULL;
uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase);
calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL);
if (NULL == calendar) {
return NULL;
}
calendar->_identifier = (CFStringRef)CFRetain(identifier);
calendar->_locale = NULL;
calendar->_localeID = CFRetain(CFLocaleGetIdentifier(CFLocaleGetSystem()));
calendar->_tz = CFTimeZoneCopyDefault();
calendar->_cal = NULL;
return (CFCalendarRef)calendar;
}
CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFStringRef, calendar, calendarIdentifier);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
return calendar->_identifier;
}
CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFLocaleRef, calendar, _copyLocale);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID);
}
void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setLocale:locale);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
__CFGenericValidateType(locale, CFLocaleGetTypeID());
CFStringRef localeID = CFLocaleGetIdentifier(locale);
if (localeID != calendar->_localeID) {
CFRelease(calendar->_localeID);
CFRetain(localeID);
calendar->_localeID = localeID;
if (calendar->_cal) __CFCalendarZapCal(calendar);
}
}
CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFTimeZoneRef, calendar_copyTimeZone);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
return (CFTimeZoneRef)CFRetain(calendar->_tz);
}
void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar, setTimeZone:tz);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
if (tz != calendar->_tz) {
CFRelease(calendar->_tz);
calendar->_tz = tz ? (CFTimeZoneRef)CFRetain(tz) : CFTimeZoneCopyDefault();
if (calendar->_cal) __CFCalendarZapCal(calendar);
}
}
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;
}
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 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;
}
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);
}
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;
}
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);
}
}
static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) {
switch (unit) {
case kCFCalendarUnitEra: return UCAL_ERA;
case kCFCalendarUnitYear: return UCAL_YEAR;
case kCFCalendarUnitMonth: return UCAL_MONTH;
case kCFCalendarUnitDay: return UCAL_DAY_OF_MONTH;
case kCFCalendarUnitHour: return UCAL_HOUR_OF_DAY;
case kCFCalendarUnitMinute: return UCAL_MINUTE;
case kCFCalendarUnitSecond: return UCAL_SECOND;
case kCFCalendarUnitWeek: return UCAL_WEEK_OF_YEAR;
case kCFCalendarUnitWeekOfYear: return UCAL_WEEK_OF_YEAR;
case kCFCalendarUnitWeekOfMonth: return UCAL_WEEK_OF_MONTH;
case kCFCalendarUnitYearForWeekOfYear: return UCAL_YEAR_WOY;
case kCFCalendarUnitWeekday: return UCAL_DAY_OF_WEEK;
case kCFCalendarUnitWeekdayOrdinal: return UCAL_DAY_OF_WEEK_IN_MONTH;
}
return (UCalendarDateFields)-1;
}
static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) {
switch (ch) {
case 'G': return UCAL_ERA;
case 'y': return UCAL_YEAR;
case 'M': return UCAL_MONTH;
case 'l': return UCAL_IS_LEAP_MONTH;
case 'd': return UCAL_DAY_OF_MONTH;
case 'h': return UCAL_HOUR;
case 'H': return UCAL_HOUR_OF_DAY;
case 'm': return UCAL_MINUTE;
case 's': return UCAL_SECOND;
case 'S': return UCAL_MILLISECOND;
case 'w': return UCAL_WEEK_OF_YEAR;
case 'W': return UCAL_WEEK_OF_MONTH;
case 'Y': return UCAL_YEAR_WOY;
case 'E': return UCAL_DAY_OF_WEEK;
case 'D': return UCAL_DAY_OF_YEAR;
case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH;
case 'a': return UCAL_AM_PM;
case 'g': return UCAL_JULIAN_DAY;
}
return (UCalendarDateFields)-1;
}
static CFCalendarUnit __CFCalendarGetCalendarUnitFromChar(char ch) {
switch (ch) {
case 'G': return kCFCalendarUnitEra;
case 'y': return kCFCalendarUnitYear;
case 'M': return kCFCalendarUnitMonth;
case 'd': return kCFCalendarUnitDay;
case 'H': return kCFCalendarUnitHour;
case 'm': return kCFCalendarUnitMinute;
case 's': return kCFCalendarUnitSecond;
case 'w': return kCFCalendarUnitWeekOfYear;
case 'W': return kCFCalendarUnitWeekOfMonth;
case 'Y': return kCFCalendarUnitYearForWeekOfYear;
case 'E': return kCFCalendarUnitWeekday;
case 'F': return kCFCalendarUnitWeekdayOrdinal;
}
return (UCalendarDateFields)-1;
}
CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _minimumRangeOfUnit: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_GREATEST_MINIMUM, &status);
range.length = ucal_getLimit(calendar->_cal, field, UCAL_LEAST_MAXIMUM, &status) - range.location + 1;
if (UCAL_MONTH == field) range.location++;
if (100000 < range.length) range.length = 100000;
}
return range;
}
CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
CF_OBJC_FUNCDISPATCHV(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;
}
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 kCFCalendarUnitYearForWeekOfYear:;
ucal_set(calendar->_cal, UCAL_WEEK_OF_YEAR, ucal_getLimit(calendar->_cal, UCAL_WEEK_OF_YEAR, UCAL_ACTUAL_MINIMUM, &status));
case kCFCalendarUnitWeek:
case kCFCalendarUnitWeekOfMonth:;
case kCFCalendarUnitWeekOfYear:;
{
// 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);
}
}
static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) {
switch (bigger) {
case kCFCalendarUnitEra:
if (kCFCalendarUnitEra == smaller) return false;
if (kCFCalendarUnitWeekday == smaller) return false;
if (kCFCalendarUnitMinute == smaller) return false; // this causes CFIndex overflow in range.length
if (kCFCalendarUnitSecond == smaller) return false; // this causes CFIndex overflow in range.length
return true;
case kCFCalendarUnitYearForWeekOfYear:
case kCFCalendarUnitYear:
if (kCFCalendarUnitEra == smaller) return false;
if (kCFCalendarUnitYear == smaller) return false;
if (kCFCalendarUnitYearForWeekOfYear == smaller) return false;
if (kCFCalendarUnitWeekday == smaller) return false;
return true;
case kCFCalendarUnitMonth:
if (kCFCalendarUnitEra == smaller) return false;
if (kCFCalendarUnitYear == smaller) return false;
if (kCFCalendarUnitMonth == smaller) return false;
if (kCFCalendarUnitWeekday == smaller) return false;
return true;
case kCFCalendarUnitDay:
if (kCFCalendarUnitHour == smaller) return true;
if (kCFCalendarUnitMinute == smaller) return true;
if (kCFCalendarUnitSecond == smaller) return true;
return false;
case kCFCalendarUnitHour:
if (kCFCalendarUnitMinute == smaller) return true;
if (kCFCalendarUnitSecond == smaller) return true;
return false;
case kCFCalendarUnitMinute:
if (kCFCalendarUnitSecond == smaller) return true;
return false;
case kCFCalendarUnitWeek:
case kCFCalendarUnitWeekOfMonth:
case kCFCalendarUnitWeekOfYear:
if (kCFCalendarUnitWeekday == smaller) return true;
if (kCFCalendarUnitDay == smaller) return true;
if (kCFCalendarUnitHour == smaller) return true;
if (kCFCalendarUnitMinute == smaller) return true;
if (kCFCalendarUnitSecond == smaller) return true;
return false;
case kCFCalendarUnitSecond:
case kCFCalendarUnitWeekday:
case kCFCalendarUnitWeekdayOrdinal:
return false;
}
return false;
};
static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline));
static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _rangeOfUnit:smallerUnit inUnit:biggerUnit forAT: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;
}
CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at);
}
CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
CFIndex result = kCFNotFound;
if (!__validUnits(smallerUnit, biggerUnit)) return result;
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, _ordinalityOfUnit:smallerUnit inUnit:biggerUnit forAT: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;
}
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;
}
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 _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;
}
Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) {
va_list args;
va_start(args, componentDesc);
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _composeAbsoluteTime:atp :componentDesc :args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = strlen((char *)componentDesc);
STACK_BUFFER_DECL(int, vector, cnt);
for (idx = 0; idx < cnt; idx++) {
int arg = va_arg(args, int);
vector[idx] = arg;
}
va_end(args);
return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt);
}
Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) {
va_list args;
va_start(args, componentDesc);
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _decomposeAbsoluteTime:at :componentDesc :args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = strlen((char *)componentDesc);
STACK_BUFFER_DECL(int *, vector, cnt);
for (idx = 0; idx < cnt; idx++) {
int *arg = va_arg(args, int *);
vector[idx] = arg;
}
va_end(args);
return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt);
}
Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) {
va_list args;
va_start(args, componentDesc);
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _addComponents:atp :options :componentDesc :args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = strlen((char *)componentDesc);
STACK_BUFFER_DECL(int, vector, cnt);
for (idx = 0; idx < cnt; idx++) {
int arg = va_arg(args, int);
vector[idx] = arg;
}
va_end(args);
return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt);
}
Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) {
va_list args;
va_start(args, componentDesc);
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _diffComponents:startingAT :resultAT :options :componentDesc :args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = strlen((char *)componentDesc);
STACK_BUFFER_DECL(int *, vector, cnt);
for (idx = 0; idx < cnt; idx++) {
int *arg = va_arg(args, int *);
vector[idx] = arg;
}
va_end(args);
Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt);
return ret;
}
Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _rangeOfUnit:unit startTime:startp interval:tip forAT: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;
}
CF_PRIVATE CFCalendarRef _CFCalendarCopyCoWCurrentCalendar() {
return CFCalendarCopyCurrent();
}
CF_PRIVATE CFCalendarRef _CFCalendarCreateCoWWithIdentifier(CFStringRef identifier) {
return CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, identifier);
}
#undef BUFFER_SIZE