| /* 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 |
| |