blob: 796200bd55b7a5826fb731880dbf7bcd7a7c3d26 [file] [log] [blame]
/*
* Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved.
*
* This source code is a modified version of the CoreFoundation sources released by Apple Inc. under
* the terms of the APSL version 2.0 (see below).
*
* For information about changes from the original Apple source release can be found by reviewing the
* source control system for the project at https://sourceforge.net/svn/?group_id=246198.
*
* The original license information is as follows:
*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* CFCalendar.c
Copyright 2004-2004, Apple Computer, Inc. All rights reserved.
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;
CFRelease(calendar->_identifier);
if (calendar->_locale) CFRelease(calendar->_locale);
if (calendar->_localeID) CFRelease(calendar->_localeID);
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
};
static void __CFCalendarInitialize(void) {
__kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass);
}
CFTypeID CFCalendarGetTypeID(void) {
if (_kCFRuntimeNotATypeID == __kCFCalendarTypeID) __CFCalendarInitialize();
return __kCFCalendarTypeID;
}
__private_extern__ 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_TRADITIONAL, &status);
if (calendarID) CFRelease(localeID);
return cal;
}
static void __CFCalendarSetupCal(CFCalendarRef calendar) {
calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
}
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;
}
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(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 = CFLocaleGetIdentifier(CFLocaleGetSystem());
calendar->_tz = CFTimeZoneCopyDefault();
calendar->_cal = NULL;
return (CFCalendarRef)calendar;
}
CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) {
CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef, calendar, "calendarIdentifier");
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
return calendar->_identifier;
}
CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) {
CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef, calendar, "_copyLocale");
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID);
}
void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) {
CF_OBJC_FUNCDISPATCH1(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_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef, calendar, "_copyTimeZone");
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
return (CFTimeZoneRef)CFRetain(calendar->_tz);
}
void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) {
CF_OBJC_FUNCDISPATCH1(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_FUNCDISPATCH0(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_FUNCDISPATCH1(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_FUNCDISPATCH0(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_FUNCDISPATCH1(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_FUNCDISPATCH0(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_FUNCDISPATCH1(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 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 '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 '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 UCalendarDateFields __CFCalendarGetCalendarUnitFromChar(char ch) {
switch (ch) {
case 'G': return (UCalendarDateFields)kCFCalendarUnitEra;
case 'y': return (UCalendarDateFields)kCFCalendarUnitYear;
case 'M': return (UCalendarDateFields)kCFCalendarUnitMonth;
case 'd': return (UCalendarDateFields)kCFCalendarUnitDay;
case 'H': return (UCalendarDateFields)kCFCalendarUnitHour;
case 'm': return (UCalendarDateFields)kCFCalendarUnitMinute;
case 's': return (UCalendarDateFields)kCFCalendarUnitSecond;
case 'w': return (UCalendarDateFields)kCFCalendarUnitWeek;
case 'E': return (UCalendarDateFields)kCFCalendarUnitWeekday;
case 'F': return (UCalendarDateFields)kCFCalendarUnitWeekdayOrdinal;
}
return (UCalendarDateFields)-1;
}
CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) {
CF_OBJC_FUNCDISPATCH1(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_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;
}
static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at) {
// Set UCalendar to first instant of unit prior to 'at'
UErrorCode status = U_ZERO_ERROR;
UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0);
ucal_setMillis(calendar->_cal, udate, &status);
int target_era = INT_MIN;
switch (unit) { // largest to smallest, we set the fields to their minimum value
case kCFCalendarUnitWeek:
{
// reduce to first day of week, then reduce the rest of the day
int32_t goal = ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
int32_t dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
while (dow != goal) {
ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status);
dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status);
}
goto day;
}
case kCFCalendarUnitEra:
{
target_era = ucal_get(calendar->_cal, UCAL_ERA, &status);
ucal_set(calendar->_cal, UCAL_YEAR, ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_ACTUAL_MINIMUM, &status));
}
case kCFCalendarUnitYear:
ucal_set(calendar->_cal, UCAL_MONTH, ucal_getLimit(calendar->_cal, UCAL_MONTH, UCAL_ACTUAL_MINIMUM, &status));
case kCFCalendarUnitMonth:
ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status));
case kCFCalendarUnitWeekday:
case kCFCalendarUnitDay:
day:;
ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status));
case kCFCalendarUnitHour:
ucal_set(calendar->_cal, UCAL_MINUTE, ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status));
case kCFCalendarUnitMinute:
ucal_set(calendar->_cal, UCAL_SECOND, ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status));
case kCFCalendarUnitSecond:
ucal_set(calendar->_cal, UCAL_MILLISECOND, 0);
}
if (INT_MIN != target_era && ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
// In the Japanese calendar, and possibly others, eras don't necessarily
// start on the first day of a year, so the previous code may have backed
// up into the previous era, and we have to correct forward.
UDate bad_udate = ucal_getMillis(calendar->_cal, &status);
ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
bad_udate = ucal_getMillis(calendar->_cal, &status);
ucal_add(calendar->_cal, UCAL_MONTH, 1, &status);
}
udate = ucal_getMillis(calendar->_cal, &status);
// target date is between bad_udate and udate
for (;;) {
UDate test_udate = (udate + bad_udate) / 2;
ucal_setMillis(calendar->_cal, test_udate, &status);
if (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) {
bad_udate = test_udate;
} else {
udate = test_udate;
}
if (fabs(udate - bad_udate) < 1000) break;
}
do {
bad_udate = floor((bad_udate + 1000) / 1000) * 1000;
ucal_setMillis(calendar->_cal, bad_udate, &status);
} while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era);
}
}
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 kCFCalendarUnitYear:
if (kCFCalendarUnitEra == smaller) return false;
if (kCFCalendarUnitYear == 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:
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 __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;
}
#if __GNUC__
static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline));
#endif
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;
}
CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) {
if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) {
return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at);
} else {
return __CFCalendarGetRangeOfUnit1(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_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;
}
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_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_composeAbsoluteTime:::", atp, componentDesc, args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = (int)strlen((char *)componentDesc);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
STACK_BUFFER_DECL(int, vector, cnt);
#else
int vector[256]; // Dynamic stack allocation is GNU specific
#endif
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_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_decomposeAbsoluteTime:::", at, componentDesc, args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = (int)strlen((char *)componentDesc);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
STACK_BUFFER_DECL(int *, vector, cnt);
#else
int* vector[256]; // Dynamic stack allocation is GNU specific
#endif
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_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_addComponents::::", atp, options, componentDesc, args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = (int)strlen((char *)componentDesc);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
STACK_BUFFER_DECL(int, vector, cnt);
#else
int vector[256]; // Dynamic stack allocation is GNU specific
#endif
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_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean, calendar, "_diffComponents:::::", startingAT, resultAT, options, componentDesc, args);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
int idx, cnt = (int)strlen((char *)componentDesc);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
STACK_BUFFER_DECL(int *, vector, cnt);
#else
int* vector[256]; // Dynamic stack allocation is GNU specific
#endif
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_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;
}
#undef BUFFER_SIZE