2 * Copyright (c) 2009 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
30 #include <CoreFoundation/CFCalendar.h>
31 #include <CoreFoundation/CFRuntime.h>
32 #include "CFInternal.h"
34 #include <unicode/ucal.h>
36 #define BUFFER_SIZE 512
40 CFStringRef _identifier
; // canonical identifier, never NULL
42 CFStringRef _localeID
;
47 static Boolean
__CFCalendarEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
48 CFCalendarRef calendar1
= (CFCalendarRef
)cf1
;
49 CFCalendarRef calendar2
= (CFCalendarRef
)cf2
;
50 return CFEqual(calendar1
->_identifier
, calendar2
->_identifier
);
53 static CFHashCode
__CFCalendarHash(CFTypeRef cf
) {
54 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
55 return CFHash(calendar
->_identifier
);
58 static CFStringRef
__CFCalendarCopyDescription(CFTypeRef cf
) {
59 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
60 return CFStringCreateWithFormat(CFGetAllocator(calendar
), NULL
, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf
, CFGetAllocator(calendar
), calendar
->_identifier
);
63 static void __CFCalendarDeallocate(CFTypeRef cf
) {
64 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
65 CFRelease(calendar
->_identifier
);
66 if (calendar
->_locale
) CFRelease(calendar
->_locale
);
67 if (calendar
->_localeID
) CFRelease(calendar
->_localeID
);
68 CFRelease(calendar
->_tz
);
69 if (calendar
->_cal
) ucal_close(calendar
->_cal
);
72 static CFTypeID __kCFCalendarTypeID
= _kCFRuntimeNotATypeID
;
74 static const CFRuntimeClass __CFCalendarClass
= {
79 __CFCalendarDeallocate
,
83 __CFCalendarCopyDescription
86 static void __CFCalendarInitialize(void) {
87 __kCFCalendarTypeID
= _CFRuntimeRegisterClass(&__CFCalendarClass
);
90 CFTypeID
CFCalendarGetTypeID(void) {
91 if (_kCFRuntimeNotATypeID
== __kCFCalendarTypeID
) __CFCalendarInitialize();
92 return __kCFCalendarTypeID
;
95 __private_extern__ UCalendar
*__CFCalendarCreateUCalendar(CFStringRef calendarID
, CFStringRef localeID
, CFTimeZoneRef tz
) {
97 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeID
);
98 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
99 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifier
, calendarID
);
100 localeID
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
101 CFRelease(mcomponents
);
102 CFRelease(components
);
105 char buffer
[BUFFER_SIZE
];
106 const char *cstr
= CFStringGetCStringPtr(localeID
, kCFStringEncodingASCII
);
108 if (CFStringGetCString(localeID
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
111 if (calendarID
) CFRelease(localeID
);
115 UChar ubuffer
[BUFFER_SIZE
];
116 CFStringRef tznam
= CFTimeZoneGetName(tz
);
117 CFIndex cnt
= CFStringGetLength(tznam
);
118 if (BUFFER_SIZE
< cnt
) cnt
= BUFFER_SIZE
;
119 CFStringGetCharacters(tznam
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
121 UErrorCode status
= U_ZERO_ERROR
;
122 UCalendar
*cal
= ucal_open(ubuffer
, cnt
, cstr
, UCAL_TRADITIONAL
, &status
);
123 if (calendarID
) CFRelease(localeID
);
127 static void __CFCalendarSetupCal(CFCalendarRef calendar
) {
128 calendar
->_cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
131 static void __CFCalendarZapCal(CFCalendarRef calendar
) {
132 ucal_close(calendar
->_cal
);
133 calendar
->_cal
= NULL
;
136 CFCalendarRef
CFCalendarCopyCurrent(void) {
137 CFLocaleRef locale
= CFLocaleCopyCurrent();
138 CFCalendarRef calID
= (CFCalendarRef
)CFLocaleGetValue(locale
, kCFLocaleCalendarIdentifier
);
140 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, (CFStringRef
)calID
);
141 CFCalendarSetLocale(calendar
, locale
);
148 CFCalendarRef
CFCalendarCreateWithIdentifier(CFAllocatorRef allocator
, CFStringRef identifier
) {
149 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
150 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
151 __CFGenericValidateType(identifier
, CFStringGetTypeID());
152 // return NULL until Chinese calendar is available
153 if (identifier
!= kCFGregorianCalendar
&& identifier
!= kCFBuddhistCalendar
&& identifier
!= kCFJapaneseCalendar
&& identifier
!= kCFIslamicCalendar
&& identifier
!= kCFIslamicCivilCalendar
&& identifier
!= kCFHebrewCalendar
) {
154 // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
155 if (CFEqual(kCFGregorianCalendar
, identifier
)) identifier
= kCFGregorianCalendar
;
156 else if (CFEqual(kCFBuddhistCalendar
, identifier
)) identifier
= kCFBuddhistCalendar
;
157 else if (CFEqual(kCFJapaneseCalendar
, identifier
)) identifier
= kCFJapaneseCalendar
;
158 else if (CFEqual(kCFIslamicCalendar
, identifier
)) identifier
= kCFIslamicCalendar
;
159 else if (CFEqual(kCFIslamicCivilCalendar
, identifier
)) identifier
= kCFIslamicCivilCalendar
;
160 else if (CFEqual(kCFHebrewCalendar
, identifier
)) identifier
= kCFHebrewCalendar
;
161 // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
164 struct __CFCalendar
*calendar
= NULL
;
165 uint32_t size
= sizeof(struct __CFCalendar
) - sizeof(CFRuntimeBase
);
166 calendar
= (struct __CFCalendar
*)_CFRuntimeCreateInstance(allocator
, CFCalendarGetTypeID(), size
, NULL
);
167 if (NULL
== calendar
) {
170 calendar
->_identifier
= (CFStringRef
)CFRetain(identifier
);
171 calendar
->_locale
= NULL
;
172 calendar
->_localeID
= CFLocaleGetIdentifier(CFLocaleGetSystem());
173 calendar
->_tz
= CFTimeZoneCopyDefault();
174 calendar
->_cal
= NULL
;
175 return (CFCalendarRef
)calendar
;
178 CFStringRef
CFCalendarGetIdentifier(CFCalendarRef calendar
) {
179 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef
, calendar
, "calendarIdentifier");
180 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
181 return calendar
->_identifier
;
184 CFLocaleRef
CFCalendarCopyLocale(CFCalendarRef calendar
) {
185 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef
, calendar
, "_copyLocale");
186 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
187 return (CFLocaleRef
)CFLocaleCreate(kCFAllocatorSystemDefault
, calendar
->_localeID
);
190 void CFCalendarSetLocale(CFCalendarRef calendar
, CFLocaleRef locale
) {
191 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setLocale:", locale
);
192 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
193 __CFGenericValidateType(locale
, CFLocaleGetTypeID());
194 CFStringRef localeID
= CFLocaleGetIdentifier(locale
);
195 if (localeID
!= calendar
->_localeID
) {
196 CFRelease(calendar
->_localeID
);
198 calendar
->_localeID
= localeID
;
199 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
203 CFTimeZoneRef
CFCalendarCopyTimeZone(CFCalendarRef calendar
) {
204 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef
, calendar
, "_copyTimeZone");
205 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
206 return (CFTimeZoneRef
)CFRetain(calendar
->_tz
);
209 void CFCalendarSetTimeZone(CFCalendarRef calendar
, CFTimeZoneRef tz
) {
210 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setTimeZone:", tz
);
211 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
212 if (tz
) __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
213 if (tz
!= calendar
->_tz
) {
214 CFRelease(calendar
->_tz
);
215 calendar
->_tz
= tz
? (CFTimeZoneRef
)CFRetain(tz
) : CFTimeZoneCopyDefault();
216 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
220 CFIndex
CFCalendarGetFirstWeekday(CFCalendarRef calendar
) {
221 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex
, calendar
, "firstWeekday");
222 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
223 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
224 if (calendar
->_cal
) {
225 return ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
230 void CFCalendarSetFirstWeekday(CFCalendarRef calendar
, CFIndex wkdy
) {
231 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setFirstWeekday:", wkdy
);
232 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
233 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
234 if (calendar
->_cal
) {
235 ucal_setAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
, wkdy
);
239 CFIndex
CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar
) {
240 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex
, calendar
, "minimumDaysInFirstWeek");
241 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
242 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
243 return calendar
->_cal
? ucal_getAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
) : -1;
246 void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar
, CFIndex mwd
) {
247 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setMinimumDaysInFirstWeek:", mwd
);
248 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
249 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
250 if (calendar
->_cal
) ucal_setAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, mwd
);
253 CFDateRef
CFCalendarCopyGregorianStartDate(CFCalendarRef calendar
) {
254 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFDateRef
, calendar
, "_gregorianStartDate");
255 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
256 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
257 UErrorCode status
= U_ZERO_ERROR
;
258 UDate udate
= calendar
->_cal
? ucal_getGregorianChange(calendar
->_cal
, &status
) : 0;
259 if (calendar
->_cal
&& U_SUCCESS(status
)) {
260 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
261 return CFDateCreate(CFGetAllocator(calendar
), at
);
266 void CFCalendarSetGregorianStartDate(CFCalendarRef calendar
, CFDateRef date
) {
267 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "_setGregorianStartDate:", date
);
268 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
269 if (date
) __CFGenericValidateType(date
, CFDateGetTypeID());
270 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
271 if (!calendar
->_cal
) return;
273 UErrorCode status
= U_ZERO_ERROR
;
274 UCalendar
*cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
275 UDate udate
= cal
? ucal_getGregorianChange(cal
, &status
) : 0;
276 if (cal
&& U_SUCCESS(status
)) {
277 status
= U_ZERO_ERROR
;
278 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
280 if (cal
) ucal_close(cal
);
282 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
283 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
284 UErrorCode status
= U_ZERO_ERROR
;
285 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
290 static UCalendarDateFields
__CFCalendarGetICUFieldCode(CFCalendarUnit unit
) {
292 case kCFCalendarUnitEra
: return UCAL_ERA
;
293 case kCFCalendarUnitYear
: return UCAL_YEAR
;
294 case kCFCalendarUnitMonth
: return UCAL_MONTH
;
295 case kCFCalendarUnitDay
: return UCAL_DAY_OF_MONTH
;
296 case kCFCalendarUnitHour
: return UCAL_HOUR_OF_DAY
;
297 case kCFCalendarUnitMinute
: return UCAL_MINUTE
;
298 case kCFCalendarUnitSecond
: return UCAL_SECOND
;
299 case kCFCalendarUnitWeek
: return UCAL_WEEK_OF_YEAR
;
300 case kCFCalendarUnitWeekday
: return UCAL_DAY_OF_WEEK
;
301 case kCFCalendarUnitWeekdayOrdinal
: return UCAL_DAY_OF_WEEK_IN_MONTH
;
303 return (UCalendarDateFields
)-1;
306 static UCalendarDateFields
__CFCalendarGetICUFieldCodeFromChar(char ch
) {
308 case 'G': return UCAL_ERA
;
309 case 'y': return UCAL_YEAR
;
310 case 'M': return UCAL_MONTH
;
311 case 'd': return UCAL_DAY_OF_MONTH
;
312 case 'h': return UCAL_HOUR
;
313 case 'H': return UCAL_HOUR_OF_DAY
;
314 case 'm': return UCAL_MINUTE
;
315 case 's': return UCAL_SECOND
;
316 case 'S': return UCAL_MILLISECOND
;
317 case 'w': return UCAL_WEEK_OF_YEAR
;
318 case 'W': return UCAL_WEEK_OF_MONTH
;
319 case 'E': return UCAL_DAY_OF_WEEK
;
320 case 'D': return UCAL_DAY_OF_YEAR
;
321 case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH
;
322 case 'a': return UCAL_AM_PM
;
323 case 'g': return UCAL_JULIAN_DAY
;
325 return (UCalendarDateFields
)-1;
328 static UCalendarDateFields
__CFCalendarGetCalendarUnitFromChar(char ch
) {
330 case 'G': return (UCalendarDateFields
)kCFCalendarUnitEra
;
331 case 'y': return (UCalendarDateFields
)kCFCalendarUnitYear
;
332 case 'M': return (UCalendarDateFields
)kCFCalendarUnitMonth
;
333 case 'd': return (UCalendarDateFields
)kCFCalendarUnitDay
;
334 case 'H': return (UCalendarDateFields
)kCFCalendarUnitHour
;
335 case 'm': return (UCalendarDateFields
)kCFCalendarUnitMinute
;
336 case 's': return (UCalendarDateFields
)kCFCalendarUnitSecond
;
337 case 'w': return (UCalendarDateFields
)kCFCalendarUnitWeek
;
338 case 'E': return (UCalendarDateFields
)kCFCalendarUnitWeekday
;
339 case 'F': return (UCalendarDateFields
)kCFCalendarUnitWeekdayOrdinal
;
341 return (UCalendarDateFields
)-1;
344 CFRange
CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
345 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_minimumRangeOfUnit:", unit
);
346 CFRange range
= {kCFNotFound
, kCFNotFound
};
347 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
348 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
349 if (calendar
->_cal
) {
350 ucal_clear(calendar
->_cal
);
351 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
352 UErrorCode status
= U_ZERO_ERROR
;
353 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_GREATEST_MINIMUM
, &status
);
354 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_LEAST_MAXIMUM
, &status
) - range
.location
+ 1;
355 if (UCAL_MONTH
== field
) range
.location
++;
356 if (100000 < range
.length
) range
.length
= 100000;
361 CFRange
CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
362 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_maximumRangeOfUnit:", unit
);
363 CFRange range
= {kCFNotFound
, kCFNotFound
};
364 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
365 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
366 if (calendar
->_cal
) {
367 ucal_clear(calendar
->_cal
);
368 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
369 UErrorCode status
= U_ZERO_ERROR
;
370 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MINIMUM
, &status
);
371 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MAXIMUM
, &status
) - range
.location
+ 1;
372 if (UCAL_MONTH
== field
) range
.location
++;
373 if (100000 < range
.length
) range
.length
= 100000;
378 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
) {
379 // Set UCalendar to first instant of unit prior to 'at'
380 UErrorCode status
= U_ZERO_ERROR
;
381 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
382 ucal_setMillis(calendar
->_cal
, udate
, &status
);
383 int target_era
= INT_MIN
;
384 switch (unit
) { // largest to smallest, we set the fields to their minimum value
385 case kCFCalendarUnitWeek
:
387 // reduce to first day of week, then reduce the rest of the day
388 int32_t goal
= ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
389 int32_t dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
390 while (dow
!= goal
) {
391 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
392 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
396 case kCFCalendarUnitEra
:
398 target_era
= ucal_get(calendar
->_cal
, UCAL_ERA
, &status
);
399 ucal_set(calendar
->_cal
, UCAL_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
401 case kCFCalendarUnitYear
:
402 ucal_set(calendar
->_cal
, UCAL_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
403 case kCFCalendarUnitMonth
:
404 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_DAY_OF_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
405 case kCFCalendarUnitWeekday
:
406 case kCFCalendarUnitDay
:
408 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, ucal_getLimit(calendar
->_cal
, UCAL_HOUR_OF_DAY
, UCAL_ACTUAL_MINIMUM
, &status
));
409 case kCFCalendarUnitHour
:
410 ucal_set(calendar
->_cal
, UCAL_MINUTE
, ucal_getLimit(calendar
->_cal
, UCAL_MINUTE
, UCAL_ACTUAL_MINIMUM
, &status
));
411 case kCFCalendarUnitMinute
:
412 ucal_set(calendar
->_cal
, UCAL_SECOND
, ucal_getLimit(calendar
->_cal
, UCAL_SECOND
, UCAL_ACTUAL_MINIMUM
, &status
));
413 case kCFCalendarUnitSecond
:
414 ucal_set(calendar
->_cal
, UCAL_MILLISECOND
, 0);
416 if (INT_MIN
!= target_era
&& ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
417 // In the Japanese calendar, and possibly others, eras don't necessarily
418 // start on the first day of a year, so the previous code may have backed
419 // up into the previous era, and we have to correct forward.
420 UDate bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
421 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
422 while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
423 bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
424 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
426 udate
= ucal_getMillis(calendar
->_cal
, &status
);
427 // target date is between bad_udate and udate
429 UDate test_udate
= (udate
+ bad_udate
) / 2;
430 ucal_setMillis(calendar
->_cal
, test_udate
, &status
);
431 if (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
432 bad_udate
= test_udate
;
436 if (fabs(udate
- bad_udate
) < 1000) break;
439 bad_udate
= floor((bad_udate
+ 1000) / 1000) * 1000;
440 ucal_setMillis(calendar
->_cal
, bad_udate
, &status
);
441 } while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
);
445 static Boolean
__validUnits(CFCalendarUnit smaller
, CFCalendarUnit bigger
) {
447 case kCFCalendarUnitEra
:
448 if (kCFCalendarUnitEra
== smaller
) return false;
449 if (kCFCalendarUnitWeekday
== smaller
) return false;
450 if (kCFCalendarUnitMinute
== smaller
) return false; // this causes CFIndex overflow in range.length
451 if (kCFCalendarUnitSecond
== smaller
) return false; // this causes CFIndex overflow in range.length
453 case kCFCalendarUnitYear
:
454 if (kCFCalendarUnitEra
== smaller
) return false;
455 if (kCFCalendarUnitYear
== smaller
) return false;
456 if (kCFCalendarUnitWeekday
== smaller
) return false;
458 case kCFCalendarUnitMonth
:
459 if (kCFCalendarUnitEra
== smaller
) return false;
460 if (kCFCalendarUnitYear
== smaller
) return false;
461 if (kCFCalendarUnitMonth
== smaller
) return false;
462 if (kCFCalendarUnitWeekday
== smaller
) return false;
464 case kCFCalendarUnitDay
:
465 if (kCFCalendarUnitHour
== smaller
) return true;
466 if (kCFCalendarUnitMinute
== smaller
) return true;
467 if (kCFCalendarUnitSecond
== smaller
) return true;
469 case kCFCalendarUnitHour
:
470 if (kCFCalendarUnitMinute
== smaller
) return true;
471 if (kCFCalendarUnitSecond
== smaller
) return true;
473 case kCFCalendarUnitMinute
:
474 if (kCFCalendarUnitSecond
== smaller
) return true;
476 case kCFCalendarUnitWeek
:
477 if (kCFCalendarUnitWeekday
== smaller
) return true;
478 if (kCFCalendarUnitDay
== smaller
) return true;
479 if (kCFCalendarUnitHour
== smaller
) return true;
480 if (kCFCalendarUnitMinute
== smaller
) return true;
481 if (kCFCalendarUnitSecond
== smaller
) return true;
483 case kCFCalendarUnitSecond
:
484 case kCFCalendarUnitWeekday
:
485 case kCFCalendarUnitWeekdayOrdinal
:
491 static CFRange
__CFCalendarGetRangeOfUnit1(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
492 CFRange range
= {kCFNotFound
, kCFNotFound
};
493 if (!__validUnits(smallerUnit
, biggerUnit
)) return range
;
494 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
495 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
496 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
497 if (calendar
->_cal
) {
499 ucal_clear(calendar
->_cal
);
500 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
501 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
502 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
503 UErrorCode status
= U_ZERO_ERROR
;
504 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
505 ucal_setMillis(calendar
->_cal
, udate
, &status
);
506 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
508 // Set calendar to first instant of big unit
509 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
510 UErrorCode status
= U_ZERO_ERROR
;
511 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
512 if (kCFCalendarUnitWeek
== biggerUnit
) {
513 range
.location
= ucal_get(calendar
->_cal
, smallField
, &status
);
514 if (kCFCalendarUnitMonth
== smallerUnit
) range
.location
++;
516 range
.location
= (kCFCalendarUnitHour
== smallerUnit
|| kCFCalendarUnitMinute
== smallerUnit
|| kCFCalendarUnitSecond
== smallerUnit
) ? 0 : 1;
518 // Set calendar to first instant of next value of big unit
519 if (UCAL_ERA
== bigField
) {
520 // ICU refuses to do the addition, probably because we are
521 // at the limit of UCAL_ERA. Use alternate strategy.
522 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
523 if (100000 < limit
) limit
= 100000;
524 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
526 ucal_add(calendar
->_cal
, bigField
, 1, &status
);
528 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
529 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
530 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
531 while (1 == range
.length
) {
532 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
533 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
537 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
538 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
539 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
543 UDate goal
= ucal_getMillis(calendar
->_cal
, &status
);
544 // Set calendar back to first instant of big unit
545 ucal_setMillis(calendar
->_cal
, start
, &status
);
546 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
547 // roll day forward to first 'dow'
548 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
549 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
551 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
552 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
554 start
= ucal_getMillis(calendar
->_cal
, &status
);
556 range
.location
= 1; // constant here works around ICU -- see 3948293
559 range
.length
= (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) ? 1 : 0;
560 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
561 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
562 Boolean divide
= false, alwaysDivide
= false;
563 while (curr
< goal
) {
564 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
565 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
566 if (curr
< newcurr
&& newcurr
<= goal
) {
567 range
.length
+= multiple
;
570 // Either newcurr is going backwards, or not making
571 // progress, or has overshot the goal; reset date
572 // and try smaller multiples.
573 ucal_setMillis(calendar
->_cal
, curr
, &status
);
575 // once we start overshooting the goal, the add at
576 // smaller multiples will succeed at most once for
577 // each multiple, so we reduce it every time through
579 if (goal
< newcurr
) alwaysDivide
= true;
582 multiple
= multiple
/ 2;
583 if (0 == multiple
) break;
584 divide
= alwaysDivide
;
591 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) __attribute__((noinline
));
592 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
593 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
594 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
595 CFRange range
= {kCFNotFound
, kCFNotFound
};
596 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
597 if (calendar
->_cal
) {
598 switch (smallerUnit
) {
599 case kCFCalendarUnitSecond
:
600 switch (biggerUnit
) {
601 case kCFCalendarUnitMinute
:
602 case kCFCalendarUnitHour
:
603 case kCFCalendarUnitDay
:
604 case kCFCalendarUnitWeekday
:
605 case kCFCalendarUnitWeek
:
606 case kCFCalendarUnitMonth
:
607 case kCFCalendarUnitYear
:
608 case kCFCalendarUnitEra
:
615 case kCFCalendarUnitMinute
:
616 switch (biggerUnit
) {
617 case kCFCalendarUnitHour
:
618 case kCFCalendarUnitDay
:
619 case kCFCalendarUnitWeekday
:
620 case kCFCalendarUnitWeek
:
621 case kCFCalendarUnitMonth
:
622 case kCFCalendarUnitYear
:
623 case kCFCalendarUnitEra
:
630 case kCFCalendarUnitHour
:
631 switch (biggerUnit
) {
632 case kCFCalendarUnitDay
:
633 case kCFCalendarUnitWeekday
:
634 case kCFCalendarUnitWeek
:
635 case kCFCalendarUnitMonth
:
636 case kCFCalendarUnitYear
:
637 case kCFCalendarUnitEra
:
644 case kCFCalendarUnitDay
:
645 switch (biggerUnit
) {
646 case kCFCalendarUnitWeek
:
647 case kCFCalendarUnitMonth
:
648 case kCFCalendarUnitYear
:
649 case kCFCalendarUnitEra
:
654 case kCFCalendarUnitWeekday
:
655 switch (biggerUnit
) {
656 case kCFCalendarUnitWeek
:
657 case kCFCalendarUnitMonth
:
658 case kCFCalendarUnitYear
:
659 case kCFCalendarUnitEra
:
664 case kCFCalendarUnitWeekdayOrdinal
:
665 switch (biggerUnit
) {
666 case kCFCalendarUnitMonth
:
667 case kCFCalendarUnitYear
:
668 case kCFCalendarUnitEra
:
673 case kCFCalendarUnitWeek
:
674 switch (biggerUnit
) {
675 case kCFCalendarUnitMonth
:
676 case kCFCalendarUnitYear
:
677 case kCFCalendarUnitEra
:
682 case kCFCalendarUnitMonth
:
683 switch (biggerUnit
) {
684 case kCFCalendarUnitYear
:
685 case kCFCalendarUnitEra
:
690 case kCFCalendarUnitYear
:
691 switch (biggerUnit
) {
692 case kCFCalendarUnitEra
:
697 case kCFCalendarUnitEra
:
704 ucal_clear(calendar
->_cal
);
705 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
706 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
707 UCalendarDateFields yearField
= __CFCalendarGetICUFieldCode(kCFCalendarUnitYear
);
708 UCalendarDateFields fieldToAdd
= smallField
;
709 if (kCFCalendarUnitWeekday
== smallerUnit
) {
710 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitDay
);
713 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
714 UErrorCode status
= U_ZERO_ERROR
;
715 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
716 ucal_setMillis(calendar
->_cal
, udate
, &status
);
717 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
718 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek
);
720 // Set calendar to first instant of big unit
721 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
722 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
723 UErrorCode status
= U_ZERO_ERROR
;
724 // roll day forward to first 'dow'
725 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
726 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
728 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
729 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
732 int32_t minSmallValue
= INT32_MAX
;
733 int32_t maxSmallValue
= INT32_MIN
;
734 UErrorCode status
= U_ZERO_ERROR
;
735 int32_t bigValue
= ucal_get(calendar
->_cal
, bigField
, &status
);
737 int32_t smallValue
= ucal_get(calendar
->_cal
, smallField
, &status
);
738 if (smallValue
< minSmallValue
) minSmallValue
= smallValue
;
739 if (smallValue
> maxSmallValue
) maxSmallValue
= smallValue
;
740 ucal_add(calendar
->_cal
, fieldToAdd
, 1, &status
);
741 if (bigValue
!= ucal_get(calendar
->_cal
, bigField
, &status
)) break;
742 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) break;
743 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
745 status
= U_ZERO_ERROR
;
746 range
.location
= minSmallValue
;
747 if (smallerUnit
== kCFCalendarUnitMonth
) range
.location
= 1;
748 range
.length
= maxSmallValue
- minSmallValue
+ 1;
749 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) range
.length
= 100000;
754 CFRange
CFCalendarGetRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
755 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
)) {
756 return __CFCalendarGetRangeOfUnit2(calendar
, smallerUnit
, biggerUnit
, at
);
758 return __CFCalendarGetRangeOfUnit1(calendar
, smallerUnit
, biggerUnit
, at
);
762 CFIndex
CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
763 CFIndex result
= kCFNotFound
;
764 if (!__validUnits(smallerUnit
, biggerUnit
)) return result
;
765 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex
, calendar
, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
766 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
767 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
768 if (calendar
->_cal
) {
769 UErrorCode status
= U_ZERO_ERROR
;
770 ucal_clear(calendar
->_cal
);
771 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
772 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
773 ucal_setMillis(calendar
->_cal
, udate
, &status
);
774 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
776 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
777 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
778 ucal_setMillis(calendar
->_cal
, udate
, &status
);
779 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_MONTH
, &status
);
782 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
783 // Set calendar to first instant of big unit
784 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
785 UDate curr
= ucal_getMillis(calendar
->_cal
, &status
);
786 UDate goal
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
788 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
789 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
790 Boolean divide
= false, alwaysDivide
= false;
791 while (curr
< goal
) {
792 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
793 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
794 if (curr
< newcurr
&& newcurr
<= goal
) {
798 // Either newcurr is going backwards, or not making
799 // progress, or has overshot the goal; reset date
800 // and try smaller multiples.
801 ucal_setMillis(calendar
->_cal
, curr
, &status
);
803 // once we start overshooting the goal, the add at
804 // smaller multiples will succeed at most once for
805 // each multiple, so we reduce it every time through
807 if (goal
< newcurr
) alwaysDivide
= true;
810 multiple
= multiple
/ 2;
811 if (0 == multiple
) break;
812 divide
= alwaysDivide
;
819 Boolean
_CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, int *vector
, int count
) {
820 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
821 if (calendar
->_cal
) {
822 UErrorCode status
= U_ZERO_ERROR
;
823 ucal_clear(calendar
->_cal
);
824 ucal_set(calendar
->_cal
, UCAL_YEAR
, 1);
825 ucal_set(calendar
->_cal
, UCAL_MONTH
, 0);
826 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1);
827 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, 0);
828 ucal_set(calendar
->_cal
, UCAL_MINUTE
, 0);
829 ucal_set(calendar
->_cal
, UCAL_SECOND
, 0);
830 const char *desc
= componentDesc
;
831 Boolean doWOY
= false;
834 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
835 if (UCAL_WEEK_OF_YEAR
== field
) {
841 desc
= componentDesc
;
844 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
846 if (UCAL_YEAR
== field
&& doWOY
) field
= UCAL_YEAR_WOY
;
847 if (UCAL_MONTH
== field
) value
--;
848 ucal_set(calendar
->_cal
, field
, value
);
853 UDate udate
= ucal_getMillis(calendar
->_cal
, &status
);
854 CFAbsoluteTime at
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
856 return U_SUCCESS(status
) ? true : false;
861 Boolean
_CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, int **vector
, int count
) {
862 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
863 if (calendar
->_cal
) {
864 UErrorCode status
= U_ZERO_ERROR
;
865 ucal_clear(calendar
->_cal
);
866 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
867 ucal_setMillis(calendar
->_cal
, udate
, &status
);
868 char ch
= *componentDesc
;
870 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
871 int value
= ucal_get(calendar
->_cal
, field
, &status
);
872 if (UCAL_MONTH
== field
) value
++;
878 return U_SUCCESS(status
) ? true : false;
883 Boolean
_CFCalendarAddComponentsV(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, int *vector
, int count
) {
884 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
885 if (calendar
->_cal
) {
886 UErrorCode status
= U_ZERO_ERROR
;
887 ucal_clear(calendar
->_cal
);
888 UDate udate
= floor((*atp
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
889 ucal_setMillis(calendar
->_cal
, udate
, &status
);
890 char ch
= *componentDesc
;
892 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
893 int amount
= *vector
;
894 if (options
& kCFCalendarComponentsWrap
) {
895 ucal_roll(calendar
->_cal
, field
, amount
, &status
);
897 ucal_add(calendar
->_cal
, field
, amount
, &status
);
903 udate
= ucal_getMillis(calendar
->_cal
, &status
);
904 *atp
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
905 return U_SUCCESS(status
) ? true : false;
910 Boolean
_CFCalendarGetComponentDifferenceV(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, int **vector
, int count
) {
911 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
912 if (calendar
->_cal
) {
913 UErrorCode status
= U_ZERO_ERROR
;
914 ucal_clear(calendar
->_cal
);
915 UDate curr
= floor((startingAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
916 UDate goal
= floor((resultAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
917 ucal_setMillis(calendar
->_cal
, curr
, &status
);
918 int direction
= (startingAT
<= resultAT
) ? 1 : -1;
919 char ch
= *componentDesc
;
921 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
922 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
923 int multiple
= direction
* (1 << multiple_table
[flsl(__CFCalendarGetCalendarUnitFromChar(ch
)) - 1]);
924 Boolean divide
= false, alwaysDivide
= false;
926 while ((direction
> 0 && curr
< goal
) || (direction
< 0 && goal
< curr
)) {
927 ucal_add(calendar
->_cal
, field
, multiple
, &status
);
928 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
929 if ((direction
> 0 && curr
< newcurr
&& newcurr
<= goal
) || (direction
< 0 && newcurr
< curr
&& goal
<= newcurr
)) {
933 // Either newcurr is going backwards, or not making
934 // progress, or has overshot the goal; reset date
935 // and try smaller multiples.
936 ucal_setMillis(calendar
->_cal
, curr
, &status
);
938 // once we start overshooting the goal, the add at
939 // smaller multiples will succeed at most once for
940 // each multiple, so we reduce it every time through
942 if ((direction
> 0 && goal
< newcurr
) || (direction
< 0 && newcurr
< goal
)) alwaysDivide
= true;
945 multiple
= multiple
/ 2;
946 if (0 == multiple
) break;
947 divide
= alwaysDivide
;
955 return U_SUCCESS(status
) ? true : false;
960 Boolean
CFCalendarComposeAbsoluteTime(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, ...) {
962 va_start(args
, componentDesc
);
963 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_composeAbsoluteTime:::", atp
, componentDesc
, args
);
964 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
965 int idx
, cnt
= strlen((char *)componentDesc
);
966 STACK_BUFFER_DECL(int, vector
, cnt
);
967 for (idx
= 0; idx
< cnt
; idx
++) {
968 int arg
= va_arg(args
, int);
972 return _CFCalendarComposeAbsoluteTimeV(calendar
, atp
, componentDesc
, vector
, cnt
);
975 Boolean
CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, ...) {
977 va_start(args
, componentDesc
);
978 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_decomposeAbsoluteTime:::", at
, componentDesc
, args
);
979 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
980 int idx
, cnt
= strlen((char *)componentDesc
);
981 STACK_BUFFER_DECL(int *, vector
, cnt
);
982 for (idx
= 0; idx
< cnt
; idx
++) {
983 int *arg
= va_arg(args
, int *);
987 return _CFCalendarDecomposeAbsoluteTimeV(calendar
, at
, componentDesc
, vector
, cnt
);
990 Boolean
CFCalendarAddComponents(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, ...) {
992 va_start(args
, componentDesc
);
993 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_addComponents::::", atp
, options
, componentDesc
, args
);
994 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
995 int idx
, cnt
= strlen((char *)componentDesc
);
996 STACK_BUFFER_DECL(int, vector
, cnt
);
997 for (idx
= 0; idx
< cnt
; idx
++) {
998 int arg
= va_arg(args
, int);
1002 return _CFCalendarAddComponentsV(calendar
, atp
, options
, componentDesc
, vector
, cnt
);
1005 Boolean
CFCalendarGetComponentDifference(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, ...) {
1007 va_start(args
, componentDesc
);
1008 CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean
, calendar
, "_diffComponents:::::", startingAT
, resultAT
, options
, componentDesc
, args
);
1009 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1010 int idx
, cnt
= strlen((char *)componentDesc
);
1011 STACK_BUFFER_DECL(int *, vector
, cnt
);
1012 for (idx
= 0; idx
< cnt
; idx
++) {
1013 int *arg
= va_arg(args
, int *);
1017 Boolean ret
= _CFCalendarGetComponentDifferenceV(calendar
, startingAT
, resultAT
, options
, componentDesc
, vector
, cnt
);
1021 Boolean
CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
, CFAbsoluteTime
*startp
, CFTimeInterval
*tip
) {
1022 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_rangeOfUnit:startTime:interval:forAT:", unit
, startp
, tip
, at
);
1023 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1024 if (kCFCalendarUnitWeekdayOrdinal
== unit
) return false;
1025 if (kCFCalendarUnitWeekday
== unit
) unit
= kCFCalendarUnitDay
;
1026 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
1027 if (calendar
->_cal
) {
1028 ucal_clear(calendar
->_cal
);
1029 __CFCalendarSetToFirstInstant(calendar
, unit
, at
);
1030 UErrorCode status
= U_ZERO_ERROR
;
1031 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
1032 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
1033 ucal_add(calendar
->_cal
, field
, 1, &status
);
1034 UDate end
= ucal_getMillis(calendar
->_cal
, &status
);
1035 if (end
== start
&& kCFCalendarUnitEra
== unit
) {
1036 // ICU refuses to do the addition, probably because we are
1037 // at the limit of UCAL_ERA. Use alternate strategy.
1038 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
1039 if (100000 < limit
) limit
= 100000;
1040 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
1041 end
= ucal_getMillis(calendar
->_cal
, &status
);
1043 if (U_SUCCESS(status
)) {
1044 if (startp
) *startp
= (double)start
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1045 if (tip
) *tip
= (double)(end
- start
) / 1000.0;