2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright 2004-2004, Apple Computer, Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFCalendar.h>
29 #include <CoreFoundation/CFRuntime.h>
30 #include "CFInternal.h"
32 #include <unicode/ucal.h>
34 #define BUFFER_SIZE 512
38 CFStringRef _identifier
; // canonical identifier, never NULL
40 CFStringRef _localeID
;
45 static Boolean
__CFCalendarEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
46 CFCalendarRef calendar1
= (CFCalendarRef
)cf1
;
47 CFCalendarRef calendar2
= (CFCalendarRef
)cf2
;
48 return CFEqual(calendar1
->_identifier
, calendar2
->_identifier
);
51 static CFHashCode
__CFCalendarHash(CFTypeRef cf
) {
52 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
53 return CFHash(calendar
->_identifier
);
56 static CFStringRef
__CFCalendarCopyDescription(CFTypeRef cf
) {
57 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
58 return CFStringCreateWithFormat(CFGetAllocator(calendar
), NULL
, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf
, CFGetAllocator(calendar
), calendar
->_identifier
);
61 static void __CFCalendarDeallocate(CFTypeRef cf
) {
62 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
63 CFRelease(calendar
->_identifier
);
64 if (calendar
->_locale
) CFRelease(calendar
->_locale
);
65 if (calendar
->_localeID
) CFRelease(calendar
->_localeID
);
66 CFRelease(calendar
->_tz
);
67 if (calendar
->_cal
) ucal_close(calendar
->_cal
);
70 static CFTypeID __kCFCalendarTypeID
= _kCFRuntimeNotATypeID
;
72 static const CFRuntimeClass __CFCalendarClass
= {
77 __CFCalendarDeallocate
,
81 __CFCalendarCopyDescription
84 static void __CFCalendarInitialize(void) {
85 __kCFCalendarTypeID
= _CFRuntimeRegisterClass(&__CFCalendarClass
);
88 CFTypeID
CFCalendarGetTypeID(void) {
89 if (_kCFRuntimeNotATypeID
== __kCFCalendarTypeID
) __CFCalendarInitialize();
90 return __kCFCalendarTypeID
;
93 __private_extern__ UCalendar
*__CFCalendarCreateUCalendar(CFStringRef calendarID
, CFStringRef localeID
, CFTimeZoneRef tz
) {
95 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeID
);
96 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
97 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifier
, calendarID
);
98 localeID
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
99 CFRelease(mcomponents
);
100 CFRelease(components
);
103 char buffer
[BUFFER_SIZE
];
104 const char *cstr
= CFStringGetCStringPtr(localeID
, kCFStringEncodingASCII
);
106 if (CFStringGetCString(localeID
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
109 if (calendarID
) CFRelease(localeID
);
113 UChar ubuffer
[BUFFER_SIZE
];
114 CFStringRef tznam
= CFTimeZoneGetName(tz
);
115 CFIndex cnt
= CFStringGetLength(tznam
);
116 if (BUFFER_SIZE
< cnt
) cnt
= BUFFER_SIZE
;
117 CFStringGetCharacters(tznam
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
119 UErrorCode status
= U_ZERO_ERROR
;
120 UCalendar
*cal
= ucal_open(ubuffer
, cnt
, cstr
, UCAL_TRADITIONAL
, &status
);
121 if (calendarID
) CFRelease(localeID
);
125 static void __CFCalendarSetupCal(CFCalendarRef calendar
) {
126 calendar
->_cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
129 static void __CFCalendarZapCal(CFCalendarRef calendar
) {
130 ucal_close(calendar
->_cal
);
131 calendar
->_cal
= NULL
;
134 CFCalendarRef
CFCalendarCopyCurrent(void) {
135 CFLocaleRef locale
= CFLocaleCopyCurrent();
136 CFCalendarRef calID
= (CFCalendarRef
)CFLocaleGetValue(locale
, kCFLocaleCalendarIdentifier
);
138 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, (CFStringRef
)calID
);
139 CFCalendarSetLocale(calendar
, locale
);
146 CFCalendarRef
CFCalendarCreateWithIdentifier(CFAllocatorRef allocator
, CFStringRef identifier
) {
147 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
148 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
149 __CFGenericValidateType(identifier
, CFStringGetTypeID());
150 // return NULL until Chinese calendar is available
151 if (identifier
!= kCFGregorianCalendar
&& identifier
!= kCFBuddhistCalendar
&& identifier
!= kCFJapaneseCalendar
&& identifier
!= kCFIslamicCalendar
&& identifier
!= kCFIslamicCivilCalendar
&& identifier
!= kCFHebrewCalendar
) {
152 // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
153 if (CFEqual(kCFGregorianCalendar
, identifier
)) identifier
= kCFGregorianCalendar
;
154 else if (CFEqual(kCFBuddhistCalendar
, identifier
)) identifier
= kCFBuddhistCalendar
;
155 else if (CFEqual(kCFJapaneseCalendar
, identifier
)) identifier
= kCFJapaneseCalendar
;
156 else if (CFEqual(kCFIslamicCalendar
, identifier
)) identifier
= kCFIslamicCalendar
;
157 else if (CFEqual(kCFIslamicCivilCalendar
, identifier
)) identifier
= kCFIslamicCivilCalendar
;
158 else if (CFEqual(kCFHebrewCalendar
, identifier
)) identifier
= kCFHebrewCalendar
;
159 // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
162 struct __CFCalendar
*calendar
= NULL
;
163 uint32_t size
= sizeof(struct __CFCalendar
) - sizeof(CFRuntimeBase
);
164 calendar
= (struct __CFCalendar
*)_CFRuntimeCreateInstance(allocator
, CFCalendarGetTypeID(), size
, NULL
);
165 if (NULL
== calendar
) {
168 calendar
->_identifier
= (CFStringRef
)CFRetain(identifier
);
169 calendar
->_locale
= NULL
;
170 calendar
->_localeID
= CFLocaleGetIdentifier(CFLocaleGetSystem());
171 calendar
->_tz
= CFTimeZoneCopyDefault();
172 calendar
->_cal
= NULL
;
173 return (CFCalendarRef
)calendar
;
176 CFStringRef
CFCalendarGetIdentifier(CFCalendarRef calendar
) {
177 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef
, calendar
, "calendarIdentifier");
178 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
179 return calendar
->_identifier
;
182 CFLocaleRef
CFCalendarCopyLocale(CFCalendarRef calendar
) {
183 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef
, calendar
, "_copyLocale");
184 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
185 return (CFLocaleRef
)CFLocaleCreate(kCFAllocatorSystemDefault
, calendar
->_localeID
);
188 void CFCalendarSetLocale(CFCalendarRef calendar
, CFLocaleRef locale
) {
189 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setLocale:", locale
);
190 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
191 __CFGenericValidateType(locale
, CFLocaleGetTypeID());
192 CFStringRef localeID
= CFLocaleGetIdentifier(locale
);
193 if (localeID
!= calendar
->_localeID
) {
194 CFRelease(calendar
->_localeID
);
196 calendar
->_localeID
= localeID
;
197 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
201 CFTimeZoneRef
CFCalendarCopyTimeZone(CFCalendarRef calendar
) {
202 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef
, calendar
, "_copyTimeZone");
203 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
204 return (CFTimeZoneRef
)CFRetain(calendar
->_tz
);
207 void CFCalendarSetTimeZone(CFCalendarRef calendar
, CFTimeZoneRef tz
) {
208 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setTimeZone:", tz
);
209 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
210 if (tz
) __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
211 if (tz
!= calendar
->_tz
) {
212 CFRelease(calendar
->_tz
);
213 calendar
->_tz
= tz
? (CFTimeZoneRef
)CFRetain(tz
) : CFTimeZoneCopyDefault();
214 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
218 CFIndex
CFCalendarGetFirstWeekday(CFCalendarRef calendar
) {
219 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex
, calendar
, "firstWeekday");
220 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
221 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
222 if (calendar
->_cal
) {
223 return ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
228 void CFCalendarSetFirstWeekday(CFCalendarRef calendar
, CFIndex wkdy
) {
229 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setFirstWeekday:", wkdy
);
230 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
231 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
232 if (calendar
->_cal
) {
233 ucal_setAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
, wkdy
);
237 CFIndex
CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar
) {
238 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex
, calendar
, "minimumDaysInFirstWeek");
239 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
240 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
241 return calendar
->_cal
? ucal_getAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
) : -1;
244 void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar
, CFIndex mwd
) {
245 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setMinimumDaysInFirstWeek:", mwd
);
246 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
247 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
248 if (calendar
->_cal
) ucal_setAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, mwd
);
251 CFDateRef
CFCalendarCopyGregorianStartDate(CFCalendarRef calendar
) {
252 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFDateRef
, calendar
, "_gregorianStartDate");
253 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
254 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
255 UErrorCode status
= U_ZERO_ERROR
;
256 UDate udate
= calendar
->_cal
? ucal_getGregorianChange(calendar
->_cal
, &status
) : 0;
257 if (calendar
->_cal
&& U_SUCCESS(status
)) {
258 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
259 return CFDateCreate(CFGetAllocator(calendar
), at
);
264 void CFCalendarSetGregorianStartDate(CFCalendarRef calendar
, CFDateRef date
) {
265 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "_setGregorianStartDate:", date
);
266 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
267 if (date
) __CFGenericValidateType(date
, CFDateGetTypeID());
268 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
269 if (!calendar
->_cal
) return;
271 UErrorCode status
= U_ZERO_ERROR
;
272 UCalendar
*cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
273 UDate udate
= cal
? ucal_getGregorianChange(cal
, &status
) : 0;
274 if (cal
&& U_SUCCESS(status
)) {
275 status
= U_ZERO_ERROR
;
276 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
278 if (cal
) ucal_close(cal
);
280 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
281 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
282 UErrorCode status
= U_ZERO_ERROR
;
283 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
288 static UCalendarDateFields
__CFCalendarGetICUFieldCode(CFCalendarUnit unit
) {
290 case kCFCalendarUnitEra
: return UCAL_ERA
;
291 case kCFCalendarUnitYear
: return UCAL_YEAR
;
292 case kCFCalendarUnitMonth
: return UCAL_MONTH
;
293 case kCFCalendarUnitDay
: return UCAL_DAY_OF_MONTH
;
294 case kCFCalendarUnitHour
: return UCAL_HOUR_OF_DAY
;
295 case kCFCalendarUnitMinute
: return UCAL_MINUTE
;
296 case kCFCalendarUnitSecond
: return UCAL_SECOND
;
297 case kCFCalendarUnitWeek
: return UCAL_WEEK_OF_YEAR
;
298 case kCFCalendarUnitWeekday
: return UCAL_DAY_OF_WEEK
;
299 case kCFCalendarUnitWeekdayOrdinal
: return UCAL_DAY_OF_WEEK_IN_MONTH
;
301 return (UCalendarDateFields
)-1;
304 static UCalendarDateFields
__CFCalendarGetICUFieldCodeFromChar(char ch
) {
306 case 'G': return UCAL_ERA
;
307 case 'y': return UCAL_YEAR
;
308 case 'M': return UCAL_MONTH
;
309 case 'd': return UCAL_DAY_OF_MONTH
;
310 case 'h': return UCAL_HOUR
;
311 case 'H': return UCAL_HOUR_OF_DAY
;
312 case 'm': return UCAL_MINUTE
;
313 case 's': return UCAL_SECOND
;
314 case 'S': return UCAL_MILLISECOND
;
315 case 'w': return UCAL_WEEK_OF_YEAR
;
316 case 'W': return UCAL_WEEK_OF_MONTH
;
317 case 'E': return UCAL_DAY_OF_WEEK
;
318 case 'D': return UCAL_DAY_OF_YEAR
;
319 case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH
;
320 case 'a': return UCAL_AM_PM
;
321 case 'g': return UCAL_JULIAN_DAY
;
323 return (UCalendarDateFields
)-1;
326 static UCalendarDateFields
__CFCalendarGetCalendarUnitFromChar(char ch
) {
328 case 'G': return (UCalendarDateFields
)kCFCalendarUnitEra
;
329 case 'y': return (UCalendarDateFields
)kCFCalendarUnitYear
;
330 case 'M': return (UCalendarDateFields
)kCFCalendarUnitMonth
;
331 case 'd': return (UCalendarDateFields
)kCFCalendarUnitDay
;
332 case 'H': return (UCalendarDateFields
)kCFCalendarUnitHour
;
333 case 'm': return (UCalendarDateFields
)kCFCalendarUnitMinute
;
334 case 's': return (UCalendarDateFields
)kCFCalendarUnitSecond
;
335 case 'w': return (UCalendarDateFields
)kCFCalendarUnitWeek
;
336 case 'E': return (UCalendarDateFields
)kCFCalendarUnitWeekday
;
337 case 'F': return (UCalendarDateFields
)kCFCalendarUnitWeekdayOrdinal
;
339 return (UCalendarDateFields
)-1;
342 CFRange
CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
343 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_minimumRangeOfUnit:", unit
);
344 CFRange range
= {kCFNotFound
, kCFNotFound
};
345 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
346 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
347 if (calendar
->_cal
) {
348 ucal_clear(calendar
->_cal
);
349 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
350 UErrorCode status
= U_ZERO_ERROR
;
351 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_GREATEST_MINIMUM
, &status
);
352 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_LEAST_MAXIMUM
, &status
) - range
.location
+ 1;
353 if (UCAL_MONTH
== field
) range
.location
++;
354 if (100000 < range
.length
) range
.length
= 100000;
359 CFRange
CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
360 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_maximumRangeOfUnit:", unit
);
361 CFRange range
= {kCFNotFound
, kCFNotFound
};
362 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
363 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
364 if (calendar
->_cal
) {
365 ucal_clear(calendar
->_cal
);
366 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
367 UErrorCode status
= U_ZERO_ERROR
;
368 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MINIMUM
, &status
);
369 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MAXIMUM
, &status
) - range
.location
+ 1;
370 if (UCAL_MONTH
== field
) range
.location
++;
371 if (100000 < range
.length
) range
.length
= 100000;
376 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
) {
377 // Set UCalendar to first instant of unit prior to 'at'
378 UErrorCode status
= U_ZERO_ERROR
;
379 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
380 ucal_setMillis(calendar
->_cal
, udate
, &status
);
381 int target_era
= INT_MIN
;
382 switch (unit
) { // largest to smallest, we set the fields to their minimum value
383 case kCFCalendarUnitWeek
:
385 // reduce to first day of week, then reduce the rest of the day
386 int32_t goal
= ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
387 int32_t dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
388 while (dow
!= goal
) {
389 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
390 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
394 case kCFCalendarUnitEra
:
396 target_era
= ucal_get(calendar
->_cal
, UCAL_ERA
, &status
);
397 ucal_set(calendar
->_cal
, UCAL_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
399 case kCFCalendarUnitYear
:
400 ucal_set(calendar
->_cal
, UCAL_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
401 case kCFCalendarUnitMonth
:
402 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_DAY_OF_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
403 case kCFCalendarUnitWeekday
:
404 case kCFCalendarUnitDay
:
406 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, ucal_getLimit(calendar
->_cal
, UCAL_HOUR_OF_DAY
, UCAL_ACTUAL_MINIMUM
, &status
));
407 case kCFCalendarUnitHour
:
408 ucal_set(calendar
->_cal
, UCAL_MINUTE
, ucal_getLimit(calendar
->_cal
, UCAL_MINUTE
, UCAL_ACTUAL_MINIMUM
, &status
));
409 case kCFCalendarUnitMinute
:
410 ucal_set(calendar
->_cal
, UCAL_SECOND
, ucal_getLimit(calendar
->_cal
, UCAL_SECOND
, UCAL_ACTUAL_MINIMUM
, &status
));
411 case kCFCalendarUnitSecond
:
412 ucal_set(calendar
->_cal
, UCAL_MILLISECOND
, 0);
414 if (INT_MIN
!= target_era
&& ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
415 // In the Japanese calendar, and possibly others, eras don't necessarily
416 // start on the first day of a year, so the previous code may have backed
417 // up into the previous era, and we have to correct forward.
418 UDate bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
419 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
420 while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
421 bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
422 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
424 udate
= ucal_getMillis(calendar
->_cal
, &status
);
425 // target date is between bad_udate and udate
427 UDate test_udate
= (udate
+ bad_udate
) / 2;
428 ucal_setMillis(calendar
->_cal
, test_udate
, &status
);
429 if (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
430 bad_udate
= test_udate
;
434 if (fabs(udate
- bad_udate
) < 1000) break;
437 bad_udate
= floor((bad_udate
+ 1000) / 1000) * 1000;
438 ucal_setMillis(calendar
->_cal
, bad_udate
, &status
);
439 } while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
);
443 static Boolean
__validUnits(CFCalendarUnit smaller
, CFCalendarUnit bigger
) {
445 case kCFCalendarUnitEra
:
446 if (kCFCalendarUnitEra
== smaller
) return false;
447 if (kCFCalendarUnitWeekday
== smaller
) return false;
448 if (kCFCalendarUnitMinute
== smaller
) return false; // this causes CFIndex overflow in range.length
449 if (kCFCalendarUnitSecond
== smaller
) return false; // this causes CFIndex overflow in range.length
451 case kCFCalendarUnitYear
:
452 if (kCFCalendarUnitEra
== smaller
) return false;
453 if (kCFCalendarUnitYear
== smaller
) return false;
454 if (kCFCalendarUnitWeekday
== smaller
) return false;
456 case kCFCalendarUnitMonth
:
457 if (kCFCalendarUnitEra
== smaller
) return false;
458 if (kCFCalendarUnitYear
== smaller
) return false;
459 if (kCFCalendarUnitMonth
== smaller
) return false;
460 if (kCFCalendarUnitWeekday
== smaller
) return false;
462 case kCFCalendarUnitDay
:
463 if (kCFCalendarUnitHour
== smaller
) return true;
464 if (kCFCalendarUnitMinute
== smaller
) return true;
465 if (kCFCalendarUnitSecond
== smaller
) return true;
467 case kCFCalendarUnitHour
:
468 if (kCFCalendarUnitMinute
== smaller
) return true;
469 if (kCFCalendarUnitSecond
== smaller
) return true;
471 case kCFCalendarUnitMinute
:
472 if (kCFCalendarUnitSecond
== smaller
) return true;
474 case kCFCalendarUnitWeek
:
475 if (kCFCalendarUnitWeekday
== smaller
) return true;
476 if (kCFCalendarUnitDay
== smaller
) return true;
477 if (kCFCalendarUnitHour
== smaller
) return true;
478 if (kCFCalendarUnitMinute
== smaller
) return true;
479 if (kCFCalendarUnitSecond
== smaller
) return true;
481 case kCFCalendarUnitSecond
:
482 case kCFCalendarUnitWeekday
:
483 case kCFCalendarUnitWeekdayOrdinal
:
489 static CFRange
__CFCalendarGetRangeOfUnit1(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
490 CFRange range
= {kCFNotFound
, kCFNotFound
};
491 if (!__validUnits(smallerUnit
, biggerUnit
)) return range
;
492 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
493 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
494 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
495 if (calendar
->_cal
) {
497 ucal_clear(calendar
->_cal
);
498 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
499 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
500 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
501 UErrorCode status
= U_ZERO_ERROR
;
502 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
503 ucal_setMillis(calendar
->_cal
, udate
, &status
);
504 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
506 // Set calendar to first instant of big unit
507 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
508 UErrorCode status
= U_ZERO_ERROR
;
509 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
510 if (kCFCalendarUnitWeek
== biggerUnit
) {
511 range
.location
= ucal_get(calendar
->_cal
, smallField
, &status
);
512 if (kCFCalendarUnitMonth
== smallerUnit
) range
.location
++;
514 range
.location
= (kCFCalendarUnitHour
== smallerUnit
|| kCFCalendarUnitMinute
== smallerUnit
|| kCFCalendarUnitSecond
== smallerUnit
) ? 0 : 1;
516 // Set calendar to first instant of next value of big unit
517 if (UCAL_ERA
== bigField
) {
518 // ICU refuses to do the addition, probably because we are
519 // at the limit of UCAL_ERA. Use alternate strategy.
520 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
521 if (100000 < limit
) limit
= 100000;
522 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
524 ucal_add(calendar
->_cal
, bigField
, 1, &status
);
526 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
527 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
528 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
529 while (1 == range
.length
) {
530 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
531 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
535 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
536 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
537 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
541 UDate goal
= ucal_getMillis(calendar
->_cal
, &status
);
542 // Set calendar back to first instant of big unit
543 ucal_setMillis(calendar
->_cal
, start
, &status
);
544 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
545 // roll day forward to first 'dow'
546 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
547 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
549 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
550 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
552 start
= ucal_getMillis(calendar
->_cal
, &status
);
554 range
.location
= 1; // constant here works around ICU -- see 3948293
557 range
.length
= (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) ? 1 : 0;
558 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
559 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
560 Boolean divide
= false, alwaysDivide
= false;
561 while (curr
< goal
) {
562 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
563 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
564 if (curr
< newcurr
&& newcurr
<= goal
) {
565 range
.length
+= multiple
;
568 // Either newcurr is going backwards, or not making
569 // progress, or has overshot the goal; reset date
570 // and try smaller multiples.
571 ucal_setMillis(calendar
->_cal
, curr
, &status
);
573 // once we start overshooting the goal, the add at
574 // smaller multiples will succeed at most once for
575 // each multiple, so we reduce it every time through
577 if (goal
< newcurr
) alwaysDivide
= true;
580 multiple
= multiple
/ 2;
581 if (0 == multiple
) break;
582 divide
= alwaysDivide
;
589 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) __attribute__((noinline
));
590 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
591 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
592 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
593 CFRange range
= {kCFNotFound
, kCFNotFound
};
594 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
595 if (calendar
->_cal
) {
596 switch (smallerUnit
) {
597 case kCFCalendarUnitSecond
:
598 switch (biggerUnit
) {
599 case kCFCalendarUnitMinute
:
600 case kCFCalendarUnitHour
:
601 case kCFCalendarUnitDay
:
602 case kCFCalendarUnitWeekday
:
603 case kCFCalendarUnitWeek
:
604 case kCFCalendarUnitMonth
:
605 case kCFCalendarUnitYear
:
606 case kCFCalendarUnitEra
:
613 case kCFCalendarUnitMinute
:
614 switch (biggerUnit
) {
615 case kCFCalendarUnitHour
:
616 case kCFCalendarUnitDay
:
617 case kCFCalendarUnitWeekday
:
618 case kCFCalendarUnitWeek
:
619 case kCFCalendarUnitMonth
:
620 case kCFCalendarUnitYear
:
621 case kCFCalendarUnitEra
:
628 case kCFCalendarUnitHour
:
629 switch (biggerUnit
) {
630 case kCFCalendarUnitDay
:
631 case kCFCalendarUnitWeekday
:
632 case kCFCalendarUnitWeek
:
633 case kCFCalendarUnitMonth
:
634 case kCFCalendarUnitYear
:
635 case kCFCalendarUnitEra
:
642 case kCFCalendarUnitDay
:
643 switch (biggerUnit
) {
644 case kCFCalendarUnitWeek
:
645 case kCFCalendarUnitMonth
:
646 case kCFCalendarUnitYear
:
647 case kCFCalendarUnitEra
:
652 case kCFCalendarUnitWeekday
:
653 switch (biggerUnit
) {
654 case kCFCalendarUnitWeek
:
655 case kCFCalendarUnitMonth
:
656 case kCFCalendarUnitYear
:
657 case kCFCalendarUnitEra
:
662 case kCFCalendarUnitWeekdayOrdinal
:
663 switch (biggerUnit
) {
664 case kCFCalendarUnitMonth
:
665 case kCFCalendarUnitYear
:
666 case kCFCalendarUnitEra
:
671 case kCFCalendarUnitWeek
:
672 switch (biggerUnit
) {
673 case kCFCalendarUnitMonth
:
674 case kCFCalendarUnitYear
:
675 case kCFCalendarUnitEra
:
680 case kCFCalendarUnitMonth
:
681 switch (biggerUnit
) {
682 case kCFCalendarUnitYear
:
683 case kCFCalendarUnitEra
:
688 case kCFCalendarUnitYear
:
689 switch (biggerUnit
) {
690 case kCFCalendarUnitEra
:
695 case kCFCalendarUnitEra
:
702 ucal_clear(calendar
->_cal
);
703 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
704 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
705 UCalendarDateFields yearField
= __CFCalendarGetICUFieldCode(kCFCalendarUnitYear
);
706 UCalendarDateFields fieldToAdd
= smallField
;
707 if (kCFCalendarUnitWeekday
== smallerUnit
) {
708 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitDay
);
711 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
712 UErrorCode status
= U_ZERO_ERROR
;
713 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
714 ucal_setMillis(calendar
->_cal
, udate
, &status
);
715 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
716 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek
);
718 // Set calendar to first instant of big unit
719 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
720 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
721 UErrorCode status
= U_ZERO_ERROR
;
722 // roll day forward to first 'dow'
723 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
724 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
726 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
727 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
730 int32_t minSmallValue
= INT32_MAX
;
731 int32_t maxSmallValue
= INT32_MIN
;
732 UErrorCode status
= U_ZERO_ERROR
;
733 int32_t bigValue
= ucal_get(calendar
->_cal
, bigField
, &status
);
735 int32_t smallValue
= ucal_get(calendar
->_cal
, smallField
, &status
);
736 if (smallValue
< minSmallValue
) minSmallValue
= smallValue
;
737 if (smallValue
> maxSmallValue
) maxSmallValue
= smallValue
;
738 ucal_add(calendar
->_cal
, fieldToAdd
, 1, &status
);
739 if (bigValue
!= ucal_get(calendar
->_cal
, bigField
, &status
)) break;
740 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) break;
741 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
743 status
= U_ZERO_ERROR
;
744 range
.location
= minSmallValue
;
745 if (smallerUnit
== kCFCalendarUnitMonth
) range
.location
= 1;
746 range
.length
= maxSmallValue
- minSmallValue
+ 1;
747 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) range
.length
= 100000;
752 CFRange
CFCalendarGetRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
753 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
)) {
754 return __CFCalendarGetRangeOfUnit2(calendar
, smallerUnit
, biggerUnit
, at
);
756 return __CFCalendarGetRangeOfUnit1(calendar
, smallerUnit
, biggerUnit
, at
);
760 CFIndex
CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
761 CFIndex result
= kCFNotFound
;
762 if (!__validUnits(smallerUnit
, biggerUnit
)) return result
;
763 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex
, calendar
, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
764 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
765 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
766 if (calendar
->_cal
) {
767 UErrorCode status
= U_ZERO_ERROR
;
768 ucal_clear(calendar
->_cal
);
769 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
770 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
771 ucal_setMillis(calendar
->_cal
, udate
, &status
);
772 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
774 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
775 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
776 ucal_setMillis(calendar
->_cal
, udate
, &status
);
777 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_MONTH
, &status
);
780 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
781 // Set calendar to first instant of big unit
782 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
783 UDate curr
= ucal_getMillis(calendar
->_cal
, &status
);
784 UDate goal
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
786 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
787 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
788 Boolean divide
= false, alwaysDivide
= false;
789 while (curr
< goal
) {
790 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
791 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
792 if (curr
< newcurr
&& newcurr
<= goal
) {
796 // Either newcurr is going backwards, or not making
797 // progress, or has overshot the goal; reset date
798 // and try smaller multiples.
799 ucal_setMillis(calendar
->_cal
, curr
, &status
);
801 // once we start overshooting the goal, the add at
802 // smaller multiples will succeed at most once for
803 // each multiple, so we reduce it every time through
805 if (goal
< newcurr
) alwaysDivide
= true;
808 multiple
= multiple
/ 2;
809 if (0 == multiple
) break;
810 divide
= alwaysDivide
;
817 Boolean
_CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, int *vector
, int count
) {
818 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
819 if (calendar
->_cal
) {
820 UErrorCode status
= U_ZERO_ERROR
;
821 ucal_clear(calendar
->_cal
);
822 ucal_set(calendar
->_cal
, UCAL_YEAR
, 1);
823 ucal_set(calendar
->_cal
, UCAL_MONTH
, 0);
824 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1);
825 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, 0);
826 ucal_set(calendar
->_cal
, UCAL_MINUTE
, 0);
827 ucal_set(calendar
->_cal
, UCAL_SECOND
, 0);
828 const char *desc
= componentDesc
;
829 Boolean doWOY
= false;
832 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
833 if (UCAL_WEEK_OF_YEAR
== field
) {
839 desc
= componentDesc
;
842 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
844 if (UCAL_YEAR
== field
&& doWOY
) field
= UCAL_YEAR_WOY
;
845 if (UCAL_MONTH
== field
) value
--;
846 ucal_set(calendar
->_cal
, field
, value
);
851 UDate udate
= ucal_getMillis(calendar
->_cal
, &status
);
852 CFAbsoluteTime at
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
854 return U_SUCCESS(status
) ? true : false;
859 Boolean
_CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, int **vector
, int count
) {
860 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
861 if (calendar
->_cal
) {
862 UErrorCode status
= U_ZERO_ERROR
;
863 ucal_clear(calendar
->_cal
);
864 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
865 ucal_setMillis(calendar
->_cal
, udate
, &status
);
866 char ch
= *componentDesc
;
868 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
869 int value
= ucal_get(calendar
->_cal
, field
, &status
);
870 if (UCAL_MONTH
== field
) value
++;
876 return U_SUCCESS(status
) ? true : false;
881 Boolean
_CFCalendarAddComponentsV(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, int *vector
, int count
) {
882 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
883 if (calendar
->_cal
) {
884 UErrorCode status
= U_ZERO_ERROR
;
885 ucal_clear(calendar
->_cal
);
886 UDate udate
= floor((*atp
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
887 ucal_setMillis(calendar
->_cal
, udate
, &status
);
888 char ch
= *componentDesc
;
890 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
891 int amount
= *vector
;
892 if (options
& kCFCalendarComponentsWrap
) {
893 ucal_roll(calendar
->_cal
, field
, amount
, &status
);
895 ucal_add(calendar
->_cal
, field
, amount
, &status
);
901 udate
= ucal_getMillis(calendar
->_cal
, &status
);
902 *atp
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
903 return U_SUCCESS(status
) ? true : false;
908 Boolean
_CFCalendarGetComponentDifferenceV(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, int **vector
, int count
) {
909 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
910 if (calendar
->_cal
) {
911 UErrorCode status
= U_ZERO_ERROR
;
912 ucal_clear(calendar
->_cal
);
913 UDate curr
= floor((startingAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
914 UDate goal
= floor((resultAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
915 ucal_setMillis(calendar
->_cal
, curr
, &status
);
916 int direction
= (startingAT
<= resultAT
) ? 1 : -1;
917 char ch
= *componentDesc
;
919 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
920 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
921 int multiple
= direction
* (1 << multiple_table
[flsl(__CFCalendarGetCalendarUnitFromChar(ch
)) - 1]);
922 Boolean divide
= false, alwaysDivide
= false;
924 while ((direction
> 0 && curr
< goal
) || (direction
< 0 && goal
< curr
)) {
925 ucal_add(calendar
->_cal
, field
, multiple
, &status
);
926 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
927 if ((direction
> 0 && curr
< newcurr
&& newcurr
<= goal
) || (direction
< 0 && newcurr
< curr
&& goal
<= newcurr
)) {
931 // Either newcurr is going backwards, or not making
932 // progress, or has overshot the goal; reset date
933 // and try smaller multiples.
934 ucal_setMillis(calendar
->_cal
, curr
, &status
);
936 // once we start overshooting the goal, the add at
937 // smaller multiples will succeed at most once for
938 // each multiple, so we reduce it every time through
940 if ((direction
> 0 && goal
< newcurr
) || (direction
< 0 && newcurr
< goal
)) alwaysDivide
= true;
943 multiple
= multiple
/ 2;
944 if (0 == multiple
) break;
945 divide
= alwaysDivide
;
953 return U_SUCCESS(status
) ? true : false;
958 Boolean
CFCalendarComposeAbsoluteTime(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, ...) {
960 va_start(args
, componentDesc
);
961 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_composeAbsoluteTime:::", atp
, componentDesc
, args
);
962 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
963 int idx
, cnt
= strlen((char *)componentDesc
);
964 STACK_BUFFER_DECL(int, vector
, cnt
);
965 for (idx
= 0; idx
< cnt
; idx
++) {
966 int arg
= va_arg(args
, int);
970 return _CFCalendarComposeAbsoluteTimeV(calendar
, atp
, componentDesc
, vector
, cnt
);
973 Boolean
CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, ...) {
975 va_start(args
, componentDesc
);
976 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_decomposeAbsoluteTime:::", at
, componentDesc
, args
);
977 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
978 int idx
, cnt
= strlen((char *)componentDesc
);
979 STACK_BUFFER_DECL(int *, vector
, cnt
);
980 for (idx
= 0; idx
< cnt
; idx
++) {
981 int *arg
= va_arg(args
, int *);
985 return _CFCalendarDecomposeAbsoluteTimeV(calendar
, at
, componentDesc
, vector
, cnt
);
988 Boolean
CFCalendarAddComponents(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, ...) {
990 va_start(args
, componentDesc
);
991 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_addComponents::::", atp
, options
, componentDesc
, args
);
992 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
993 int idx
, cnt
= strlen((char *)componentDesc
);
994 STACK_BUFFER_DECL(int, vector
, cnt
);
995 for (idx
= 0; idx
< cnt
; idx
++) {
996 int arg
= va_arg(args
, int);
1000 return _CFCalendarAddComponentsV(calendar
, atp
, options
, componentDesc
, vector
, cnt
);
1003 Boolean
CFCalendarGetComponentDifference(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, ...) {
1005 va_start(args
, componentDesc
);
1006 CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean
, calendar
, "_diffComponents:::::", startingAT
, resultAT
, options
, componentDesc
, args
);
1007 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1008 int idx
, cnt
= strlen((char *)componentDesc
);
1009 STACK_BUFFER_DECL(int *, vector
, cnt
);
1010 for (idx
= 0; idx
< cnt
; idx
++) {
1011 int *arg
= va_arg(args
, int *);
1015 Boolean ret
= _CFCalendarGetComponentDifferenceV(calendar
, startingAT
, resultAT
, options
, componentDesc
, vector
, cnt
);
1019 Boolean
CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
, CFAbsoluteTime
*startp
, CFTimeInterval
*tip
) {
1020 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_rangeOfUnit:startTime:interval:forAT:", unit
, startp
, tip
, at
);
1021 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1022 if (kCFCalendarUnitWeekdayOrdinal
== unit
) return false;
1023 if (kCFCalendarUnitWeekday
== unit
) unit
= kCFCalendarUnitDay
;
1024 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
1025 if (calendar
->_cal
) {
1026 ucal_clear(calendar
->_cal
);
1027 __CFCalendarSetToFirstInstant(calendar
, unit
, at
);
1028 UErrorCode status
= U_ZERO_ERROR
;
1029 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
1030 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
1031 ucal_add(calendar
->_cal
, field
, 1, &status
);
1032 UDate end
= ucal_getMillis(calendar
->_cal
, &status
);
1033 if (end
== start
&& kCFCalendarUnitEra
== unit
) {
1034 // ICU refuses to do the addition, probably because we are
1035 // at the limit of UCAL_ERA. Use alternate strategy.
1036 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
1037 if (100000 < limit
) limit
= 100000;
1038 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
1039 end
= ucal_getMillis(calendar
->_cal
, &status
);
1041 if (U_SUCCESS(status
)) {
1042 if (startp
) *startp
= (double)start
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1043 if (tip
) *tip
= (double)(end
- start
) / 1000.0;