2 * Copyright (c) 2010 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@
25 Copyright 2004-2004, Apple Computer, Inc. All rights reserved.
26 Responsibility: Christopher Kane
31 #include <CoreFoundation/CFCalendar.h>
32 #include <CoreFoundation/CFRuntime.h>
33 #include "CFInternal.h"
35 #include <unicode/ucal.h>
37 #define BUFFER_SIZE 512
41 CFStringRef _identifier
; // canonical identifier, never NULL
43 CFStringRef _localeID
;
48 static Boolean
__CFCalendarEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
49 CFCalendarRef calendar1
= (CFCalendarRef
)cf1
;
50 CFCalendarRef calendar2
= (CFCalendarRef
)cf2
;
51 return CFEqual(calendar1
->_identifier
, calendar2
->_identifier
);
54 static CFHashCode
__CFCalendarHash(CFTypeRef cf
) {
55 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
56 return CFHash(calendar
->_identifier
);
59 static CFStringRef
__CFCalendarCopyDescription(CFTypeRef cf
) {
60 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
61 return CFStringCreateWithFormat(CFGetAllocator(calendar
), NULL
, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf
, CFGetAllocator(calendar
), calendar
->_identifier
);
64 static void __CFCalendarDeallocate(CFTypeRef cf
) {
65 CFCalendarRef calendar
= (CFCalendarRef
)cf
;
66 CFRelease(calendar
->_identifier
);
67 if (calendar
->_locale
) CFRelease(calendar
->_locale
);
68 if (calendar
->_localeID
) CFRelease(calendar
->_localeID
);
69 CFRelease(calendar
->_tz
);
70 if (calendar
->_cal
) ucal_close(calendar
->_cal
);
73 static CFTypeID __kCFCalendarTypeID
= _kCFRuntimeNotATypeID
;
75 static const CFRuntimeClass __CFCalendarClass
= {
80 __CFCalendarDeallocate
,
84 __CFCalendarCopyDescription
87 static void __CFCalendarInitialize(void) {
88 __kCFCalendarTypeID
= _CFRuntimeRegisterClass(&__CFCalendarClass
);
91 CFTypeID
CFCalendarGetTypeID(void) {
92 if (_kCFRuntimeNotATypeID
== __kCFCalendarTypeID
) __CFCalendarInitialize();
93 return __kCFCalendarTypeID
;
96 __private_extern__ UCalendar
*__CFCalendarCreateUCalendar(CFStringRef calendarID
, CFStringRef localeID
, CFTimeZoneRef tz
) {
98 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeID
);
99 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
100 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifier
, calendarID
);
101 localeID
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
102 CFRelease(mcomponents
);
103 CFRelease(components
);
106 char buffer
[BUFFER_SIZE
];
107 const char *cstr
= CFStringGetCStringPtr(localeID
, kCFStringEncodingASCII
);
109 if (CFStringGetCString(localeID
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
112 if (calendarID
) CFRelease(localeID
);
116 UChar ubuffer
[BUFFER_SIZE
];
117 CFStringRef tznam
= CFTimeZoneGetName(tz
);
118 CFIndex cnt
= CFStringGetLength(tznam
);
119 if (BUFFER_SIZE
< cnt
) cnt
= BUFFER_SIZE
;
120 CFStringGetCharacters(tznam
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
122 UErrorCode status
= U_ZERO_ERROR
;
123 UCalendar
*cal
= ucal_open(ubuffer
, cnt
, cstr
, UCAL_TRADITIONAL
, &status
);
124 if (calendarID
) CFRelease(localeID
);
128 static void __CFCalendarSetupCal(CFCalendarRef calendar
) {
129 calendar
->_cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
132 static void __CFCalendarZapCal(CFCalendarRef calendar
) {
133 ucal_close(calendar
->_cal
);
134 calendar
->_cal
= NULL
;
137 CFCalendarRef
CFCalendarCopyCurrent(void) {
138 CFLocaleRef locale
= CFLocaleCopyCurrent();
139 CFCalendarRef calID
= (CFCalendarRef
)CFLocaleGetValue(locale
, kCFLocaleCalendarIdentifier
);
141 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, (CFStringRef
)calID
);
142 CFCalendarSetLocale(calendar
, locale
);
149 CFCalendarRef
CFCalendarCreateWithIdentifier(CFAllocatorRef allocator
, CFStringRef identifier
) {
150 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
151 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
152 __CFGenericValidateType(identifier
, CFStringGetTypeID());
153 // return NULL until Chinese calendar is available
154 if (identifier
!= kCFGregorianCalendar
&& identifier
!= kCFBuddhistCalendar
&& identifier
!= kCFJapaneseCalendar
&& identifier
!= kCFIslamicCalendar
&& identifier
!= kCFIslamicCivilCalendar
&& identifier
!= kCFHebrewCalendar
) {
155 // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
156 if (CFEqual(kCFGregorianCalendar
, identifier
)) identifier
= kCFGregorianCalendar
;
157 else if (CFEqual(kCFBuddhistCalendar
, identifier
)) identifier
= kCFBuddhistCalendar
;
158 else if (CFEqual(kCFJapaneseCalendar
, identifier
)) identifier
= kCFJapaneseCalendar
;
159 else if (CFEqual(kCFIslamicCalendar
, identifier
)) identifier
= kCFIslamicCalendar
;
160 else if (CFEqual(kCFIslamicCivilCalendar
, identifier
)) identifier
= kCFIslamicCivilCalendar
;
161 else if (CFEqual(kCFHebrewCalendar
, identifier
)) identifier
= kCFHebrewCalendar
;
162 // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
165 struct __CFCalendar
*calendar
= NULL
;
166 uint32_t size
= sizeof(struct __CFCalendar
) - sizeof(CFRuntimeBase
);
167 calendar
= (struct __CFCalendar
*)_CFRuntimeCreateInstance(allocator
, CFCalendarGetTypeID(), size
, NULL
);
168 if (NULL
== calendar
) {
171 calendar
->_identifier
= (CFStringRef
)CFRetain(identifier
);
172 calendar
->_locale
= NULL
;
173 calendar
->_localeID
= CFLocaleGetIdentifier(CFLocaleGetSystem());
174 calendar
->_tz
= CFTimeZoneCopyDefault();
175 calendar
->_cal
= NULL
;
176 return (CFCalendarRef
)calendar
;
179 CFStringRef
CFCalendarGetIdentifier(CFCalendarRef calendar
) {
180 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef
, calendar
, "calendarIdentifier");
181 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
182 return calendar
->_identifier
;
185 CFLocaleRef
CFCalendarCopyLocale(CFCalendarRef calendar
) {
186 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef
, calendar
, "_copyLocale");
187 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
188 return (CFLocaleRef
)CFLocaleCreate(kCFAllocatorSystemDefault
, calendar
->_localeID
);
191 void CFCalendarSetLocale(CFCalendarRef calendar
, CFLocaleRef locale
) {
192 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setLocale:", locale
);
193 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
194 __CFGenericValidateType(locale
, CFLocaleGetTypeID());
195 CFStringRef localeID
= CFLocaleGetIdentifier(locale
);
196 if (localeID
!= calendar
->_localeID
) {
197 CFRelease(calendar
->_localeID
);
199 calendar
->_localeID
= localeID
;
200 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
204 CFTimeZoneRef
CFCalendarCopyTimeZone(CFCalendarRef calendar
) {
205 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef
, calendar
, "_copyTimeZone");
206 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
207 return (CFTimeZoneRef
)CFRetain(calendar
->_tz
);
210 void CFCalendarSetTimeZone(CFCalendarRef calendar
, CFTimeZoneRef tz
) {
211 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setTimeZone:", tz
);
212 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
213 if (tz
) __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
214 if (tz
!= calendar
->_tz
) {
215 CFRelease(calendar
->_tz
);
216 calendar
->_tz
= tz
? (CFTimeZoneRef
)CFRetain(tz
) : CFTimeZoneCopyDefault();
217 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
221 CFIndex
CFCalendarGetFirstWeekday(CFCalendarRef calendar
) {
222 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex
, calendar
, "firstWeekday");
223 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
224 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
225 if (calendar
->_cal
) {
226 return ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
231 void CFCalendarSetFirstWeekday(CFCalendarRef calendar
, CFIndex wkdy
) {
232 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setFirstWeekday:", wkdy
);
233 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
234 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
235 if (calendar
->_cal
) {
236 ucal_setAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
, wkdy
);
240 CFIndex
CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar
) {
241 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex
, calendar
, "minimumDaysInFirstWeek");
242 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
243 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
244 return calendar
->_cal
? ucal_getAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
) : -1;
247 void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar
, CFIndex mwd
) {
248 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "setMinimumDaysInFirstWeek:", mwd
);
249 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
250 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
251 if (calendar
->_cal
) ucal_setAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, mwd
);
254 CFDateRef
CFCalendarCopyGregorianStartDate(CFCalendarRef calendar
) {
255 CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFDateRef
, calendar
, "_gregorianStartDate");
256 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
257 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
258 UErrorCode status
= U_ZERO_ERROR
;
259 UDate udate
= calendar
->_cal
? ucal_getGregorianChange(calendar
->_cal
, &status
) : 0;
260 if (calendar
->_cal
&& U_SUCCESS(status
)) {
261 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
262 return CFDateCreate(CFGetAllocator(calendar
), at
);
267 void CFCalendarSetGregorianStartDate(CFCalendarRef calendar
, CFDateRef date
) {
268 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar
, "_setGregorianStartDate:", date
);
269 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
270 if (date
) __CFGenericValidateType(date
, CFDateGetTypeID());
271 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
272 if (!calendar
->_cal
) return;
274 UErrorCode status
= U_ZERO_ERROR
;
275 UCalendar
*cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
276 UDate udate
= cal
? ucal_getGregorianChange(cal
, &status
) : 0;
277 if (cal
&& U_SUCCESS(status
)) {
278 status
= U_ZERO_ERROR
;
279 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
281 if (cal
) ucal_close(cal
);
283 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
284 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
285 UErrorCode status
= U_ZERO_ERROR
;
286 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
291 static UCalendarDateFields
__CFCalendarGetICUFieldCode(CFCalendarUnit unit
) {
293 case kCFCalendarUnitEra
: return UCAL_ERA
;
294 case kCFCalendarUnitYear
: return UCAL_YEAR
;
295 case kCFCalendarUnitMonth
: return UCAL_MONTH
;
296 case kCFCalendarUnitDay
: return UCAL_DAY_OF_MONTH
;
297 case kCFCalendarUnitHour
: return UCAL_HOUR_OF_DAY
;
298 case kCFCalendarUnitMinute
: return UCAL_MINUTE
;
299 case kCFCalendarUnitSecond
: return UCAL_SECOND
;
300 case kCFCalendarUnitWeek
: return UCAL_WEEK_OF_YEAR
;
301 case kCFCalendarUnitWeekday
: return UCAL_DAY_OF_WEEK
;
302 case kCFCalendarUnitWeekdayOrdinal
: return UCAL_DAY_OF_WEEK_IN_MONTH
;
304 return (UCalendarDateFields
)-1;
307 static UCalendarDateFields
__CFCalendarGetICUFieldCodeFromChar(char ch
) {
309 case 'G': return UCAL_ERA
;
310 case 'y': return UCAL_YEAR
;
311 case 'M': return UCAL_MONTH
;
312 case 'd': return UCAL_DAY_OF_MONTH
;
313 case 'h': return UCAL_HOUR
;
314 case 'H': return UCAL_HOUR_OF_DAY
;
315 case 'm': return UCAL_MINUTE
;
316 case 's': return UCAL_SECOND
;
317 case 'S': return UCAL_MILLISECOND
;
318 case 'w': return UCAL_WEEK_OF_YEAR
;
319 case 'W': return UCAL_WEEK_OF_MONTH
;
320 case 'E': return UCAL_DAY_OF_WEEK
;
321 case 'D': return UCAL_DAY_OF_YEAR
;
322 case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH
;
323 case 'a': return UCAL_AM_PM
;
324 case 'g': return UCAL_JULIAN_DAY
;
326 return (UCalendarDateFields
)-1;
329 static UCalendarDateFields
__CFCalendarGetCalendarUnitFromChar(char ch
) {
331 case 'G': return (UCalendarDateFields
)kCFCalendarUnitEra
;
332 case 'y': return (UCalendarDateFields
)kCFCalendarUnitYear
;
333 case 'M': return (UCalendarDateFields
)kCFCalendarUnitMonth
;
334 case 'd': return (UCalendarDateFields
)kCFCalendarUnitDay
;
335 case 'H': return (UCalendarDateFields
)kCFCalendarUnitHour
;
336 case 'm': return (UCalendarDateFields
)kCFCalendarUnitMinute
;
337 case 's': return (UCalendarDateFields
)kCFCalendarUnitSecond
;
338 case 'w': return (UCalendarDateFields
)kCFCalendarUnitWeek
;
339 case 'E': return (UCalendarDateFields
)kCFCalendarUnitWeekday
;
340 case 'F': return (UCalendarDateFields
)kCFCalendarUnitWeekdayOrdinal
;
342 return (UCalendarDateFields
)-1;
345 CFRange
CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
346 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_minimumRangeOfUnit:", unit
);
347 CFRange range
= {kCFNotFound
, kCFNotFound
};
348 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
349 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
350 if (calendar
->_cal
) {
351 ucal_clear(calendar
->_cal
);
352 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
353 UErrorCode status
= U_ZERO_ERROR
;
354 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_GREATEST_MINIMUM
, &status
);
355 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_LEAST_MAXIMUM
, &status
) - range
.location
+ 1;
356 if (UCAL_MONTH
== field
) range
.location
++;
357 if (100000 < range
.length
) range
.length
= 100000;
362 CFRange
CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
363 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_maximumRangeOfUnit:", unit
);
364 CFRange range
= {kCFNotFound
, kCFNotFound
};
365 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
366 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
367 if (calendar
->_cal
) {
368 ucal_clear(calendar
->_cal
);
369 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
370 UErrorCode status
= U_ZERO_ERROR
;
371 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MINIMUM
, &status
);
372 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MAXIMUM
, &status
) - range
.location
+ 1;
373 if (UCAL_MONTH
== field
) range
.location
++;
374 if (100000 < range
.length
) range
.length
= 100000;
379 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
) {
380 // Set UCalendar to first instant of unit prior to 'at'
381 UErrorCode status
= U_ZERO_ERROR
;
382 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
383 ucal_setMillis(calendar
->_cal
, udate
, &status
);
384 int target_era
= INT_MIN
;
385 switch (unit
) { // largest to smallest, we set the fields to their minimum value
386 case kCFCalendarUnitWeek
:
388 // reduce to first day of week, then reduce the rest of the day
389 int32_t goal
= ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
390 int32_t dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
391 while (dow
!= goal
) {
392 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
393 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
397 case kCFCalendarUnitEra
:
399 target_era
= ucal_get(calendar
->_cal
, UCAL_ERA
, &status
);
400 ucal_set(calendar
->_cal
, UCAL_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
402 case kCFCalendarUnitYear
:
403 ucal_set(calendar
->_cal
, UCAL_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
404 case kCFCalendarUnitMonth
:
405 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_DAY_OF_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
406 case kCFCalendarUnitWeekday
:
407 case kCFCalendarUnitDay
:
409 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, ucal_getLimit(calendar
->_cal
, UCAL_HOUR_OF_DAY
, UCAL_ACTUAL_MINIMUM
, &status
));
410 case kCFCalendarUnitHour
:
411 ucal_set(calendar
->_cal
, UCAL_MINUTE
, ucal_getLimit(calendar
->_cal
, UCAL_MINUTE
, UCAL_ACTUAL_MINIMUM
, &status
));
412 case kCFCalendarUnitMinute
:
413 ucal_set(calendar
->_cal
, UCAL_SECOND
, ucal_getLimit(calendar
->_cal
, UCAL_SECOND
, UCAL_ACTUAL_MINIMUM
, &status
));
414 case kCFCalendarUnitSecond
:
415 ucal_set(calendar
->_cal
, UCAL_MILLISECOND
, 0);
417 if (INT_MIN
!= target_era
&& ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
418 // In the Japanese calendar, and possibly others, eras don't necessarily
419 // start on the first day of a year, so the previous code may have backed
420 // up into the previous era, and we have to correct forward.
421 UDate bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
422 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
423 while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
424 bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
425 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
427 udate
= ucal_getMillis(calendar
->_cal
, &status
);
428 // target date is between bad_udate and udate
430 UDate test_udate
= (udate
+ bad_udate
) / 2;
431 ucal_setMillis(calendar
->_cal
, test_udate
, &status
);
432 if (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
433 bad_udate
= test_udate
;
437 if (fabs(udate
- bad_udate
) < 1000) break;
440 bad_udate
= floor((bad_udate
+ 1000) / 1000) * 1000;
441 ucal_setMillis(calendar
->_cal
, bad_udate
, &status
);
442 } while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
);
446 static Boolean
__validUnits(CFCalendarUnit smaller
, CFCalendarUnit bigger
) {
448 case kCFCalendarUnitEra
:
449 if (kCFCalendarUnitEra
== smaller
) return false;
450 if (kCFCalendarUnitWeekday
== smaller
) return false;
451 if (kCFCalendarUnitMinute
== smaller
) return false; // this causes CFIndex overflow in range.length
452 if (kCFCalendarUnitSecond
== smaller
) return false; // this causes CFIndex overflow in range.length
454 case kCFCalendarUnitYear
:
455 if (kCFCalendarUnitEra
== smaller
) return false;
456 if (kCFCalendarUnitYear
== smaller
) return false;
457 if (kCFCalendarUnitWeekday
== smaller
) return false;
459 case kCFCalendarUnitMonth
:
460 if (kCFCalendarUnitEra
== smaller
) return false;
461 if (kCFCalendarUnitYear
== smaller
) return false;
462 if (kCFCalendarUnitMonth
== smaller
) return false;
463 if (kCFCalendarUnitWeekday
== smaller
) return false;
465 case kCFCalendarUnitDay
:
466 if (kCFCalendarUnitHour
== smaller
) return true;
467 if (kCFCalendarUnitMinute
== smaller
) return true;
468 if (kCFCalendarUnitSecond
== smaller
) return true;
470 case kCFCalendarUnitHour
:
471 if (kCFCalendarUnitMinute
== smaller
) return true;
472 if (kCFCalendarUnitSecond
== smaller
) return true;
474 case kCFCalendarUnitMinute
:
475 if (kCFCalendarUnitSecond
== smaller
) return true;
477 case kCFCalendarUnitWeek
:
478 if (kCFCalendarUnitWeekday
== smaller
) return true;
479 if (kCFCalendarUnitDay
== smaller
) return true;
480 if (kCFCalendarUnitHour
== smaller
) return true;
481 if (kCFCalendarUnitMinute
== smaller
) return true;
482 if (kCFCalendarUnitSecond
== smaller
) return true;
484 case kCFCalendarUnitSecond
:
485 case kCFCalendarUnitWeekday
:
486 case kCFCalendarUnitWeekdayOrdinal
:
492 static CFRange
__CFCalendarGetRangeOfUnit1(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
493 CFRange range
= {kCFNotFound
, kCFNotFound
};
494 if (!__validUnits(smallerUnit
, biggerUnit
)) return range
;
495 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
496 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
497 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
498 if (calendar
->_cal
) {
500 ucal_clear(calendar
->_cal
);
501 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
502 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
503 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
504 UErrorCode status
= U_ZERO_ERROR
;
505 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
506 ucal_setMillis(calendar
->_cal
, udate
, &status
);
507 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
509 // Set calendar to first instant of big unit
510 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
511 UErrorCode status
= U_ZERO_ERROR
;
512 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
513 if (kCFCalendarUnitWeek
== biggerUnit
) {
514 range
.location
= ucal_get(calendar
->_cal
, smallField
, &status
);
515 if (kCFCalendarUnitMonth
== smallerUnit
) range
.location
++;
517 range
.location
= (kCFCalendarUnitHour
== smallerUnit
|| kCFCalendarUnitMinute
== smallerUnit
|| kCFCalendarUnitSecond
== smallerUnit
) ? 0 : 1;
519 // Set calendar to first instant of next value of big unit
520 if (UCAL_ERA
== bigField
) {
521 // ICU refuses to do the addition, probably because we are
522 // at the limit of UCAL_ERA. Use alternate strategy.
523 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
524 if (100000 < limit
) limit
= 100000;
525 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
527 ucal_add(calendar
->_cal
, bigField
, 1, &status
);
529 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
530 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
531 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
532 while (1 == range
.length
) {
533 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
534 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
538 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
539 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
540 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
544 UDate goal
= ucal_getMillis(calendar
->_cal
, &status
);
545 // Set calendar back to first instant of big unit
546 ucal_setMillis(calendar
->_cal
, start
, &status
);
547 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
548 // roll day forward to first 'dow'
549 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
550 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
552 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
553 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
555 start
= ucal_getMillis(calendar
->_cal
, &status
);
557 range
.location
= 1; // constant here works around ICU -- see 3948293
560 range
.length
= (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) ? 1 : 0;
561 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
562 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
563 Boolean divide
= false, alwaysDivide
= false;
564 while (curr
< goal
) {
565 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
566 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
567 if (curr
< newcurr
&& newcurr
<= goal
) {
568 range
.length
+= multiple
;
571 // Either newcurr is going backwards, or not making
572 // progress, or has overshot the goal; reset date
573 // and try smaller multiples.
574 ucal_setMillis(calendar
->_cal
, curr
, &status
);
576 // once we start overshooting the goal, the add at
577 // smaller multiples will succeed at most once for
578 // each multiple, so we reduce it every time through
580 if (goal
< newcurr
) alwaysDivide
= true;
583 multiple
= multiple
/ 2;
584 if (0 == multiple
) break;
585 divide
= alwaysDivide
;
592 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) __attribute__((noinline
));
593 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
594 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
595 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
596 CFRange range
= {kCFNotFound
, kCFNotFound
};
597 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
598 if (calendar
->_cal
) {
599 switch (smallerUnit
) {
600 case kCFCalendarUnitSecond
:
601 switch (biggerUnit
) {
602 case kCFCalendarUnitMinute
:
603 case kCFCalendarUnitHour
:
604 case kCFCalendarUnitDay
:
605 case kCFCalendarUnitWeekday
:
606 case kCFCalendarUnitWeek
:
607 case kCFCalendarUnitMonth
:
608 case kCFCalendarUnitYear
:
609 case kCFCalendarUnitEra
:
616 case kCFCalendarUnitMinute
:
617 switch (biggerUnit
) {
618 case kCFCalendarUnitHour
:
619 case kCFCalendarUnitDay
:
620 case kCFCalendarUnitWeekday
:
621 case kCFCalendarUnitWeek
:
622 case kCFCalendarUnitMonth
:
623 case kCFCalendarUnitYear
:
624 case kCFCalendarUnitEra
:
631 case kCFCalendarUnitHour
:
632 switch (biggerUnit
) {
633 case kCFCalendarUnitDay
:
634 case kCFCalendarUnitWeekday
:
635 case kCFCalendarUnitWeek
:
636 case kCFCalendarUnitMonth
:
637 case kCFCalendarUnitYear
:
638 case kCFCalendarUnitEra
:
645 case kCFCalendarUnitDay
:
646 switch (biggerUnit
) {
647 case kCFCalendarUnitWeek
:
648 case kCFCalendarUnitMonth
:
649 case kCFCalendarUnitYear
:
650 case kCFCalendarUnitEra
:
655 case kCFCalendarUnitWeekday
:
656 switch (biggerUnit
) {
657 case kCFCalendarUnitWeek
:
658 case kCFCalendarUnitMonth
:
659 case kCFCalendarUnitYear
:
660 case kCFCalendarUnitEra
:
665 case kCFCalendarUnitWeekdayOrdinal
:
666 switch (biggerUnit
) {
667 case kCFCalendarUnitMonth
:
668 case kCFCalendarUnitYear
:
669 case kCFCalendarUnitEra
:
674 case kCFCalendarUnitWeek
:
675 switch (biggerUnit
) {
676 case kCFCalendarUnitMonth
:
677 case kCFCalendarUnitYear
:
678 case kCFCalendarUnitEra
:
683 case kCFCalendarUnitMonth
:
684 switch (biggerUnit
) {
685 case kCFCalendarUnitYear
:
686 case kCFCalendarUnitEra
:
691 case kCFCalendarUnitYear
:
692 switch (biggerUnit
) {
693 case kCFCalendarUnitEra
:
698 case kCFCalendarUnitEra
:
705 ucal_clear(calendar
->_cal
);
706 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
707 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
708 UCalendarDateFields yearField
= __CFCalendarGetICUFieldCode(kCFCalendarUnitYear
);
709 UCalendarDateFields fieldToAdd
= smallField
;
710 if (kCFCalendarUnitWeekday
== smallerUnit
) {
711 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitDay
);
714 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
715 UErrorCode status
= U_ZERO_ERROR
;
716 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
717 ucal_setMillis(calendar
->_cal
, udate
, &status
);
718 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
719 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek
);
721 // Set calendar to first instant of big unit
722 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
723 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
724 UErrorCode status
= U_ZERO_ERROR
;
725 // roll day forward to first 'dow'
726 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
727 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
729 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
730 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
733 int32_t minSmallValue
= INT32_MAX
;
734 int32_t maxSmallValue
= INT32_MIN
;
735 UErrorCode status
= U_ZERO_ERROR
;
736 int32_t bigValue
= ucal_get(calendar
->_cal
, bigField
, &status
);
738 int32_t smallValue
= ucal_get(calendar
->_cal
, smallField
, &status
);
739 if (smallValue
< minSmallValue
) minSmallValue
= smallValue
;
740 if (smallValue
> maxSmallValue
) maxSmallValue
= smallValue
;
741 ucal_add(calendar
->_cal
, fieldToAdd
, 1, &status
);
742 if (bigValue
!= ucal_get(calendar
->_cal
, bigField
, &status
)) break;
743 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) break;
744 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
746 status
= U_ZERO_ERROR
;
747 range
.location
= minSmallValue
;
748 if (smallerUnit
== kCFCalendarUnitMonth
) range
.location
= 1;
749 range
.length
= maxSmallValue
- minSmallValue
+ 1;
750 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) range
.length
= 100000;
755 CFRange
CFCalendarGetRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
756 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
)) {
757 return __CFCalendarGetRangeOfUnit2(calendar
, smallerUnit
, biggerUnit
, at
);
759 return __CFCalendarGetRangeOfUnit1(calendar
, smallerUnit
, biggerUnit
, at
);
763 CFIndex
CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
764 CFIndex result
= kCFNotFound
;
765 if (!__validUnits(smallerUnit
, biggerUnit
)) return result
;
766 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex
, calendar
, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
767 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
768 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
769 if (calendar
->_cal
) {
770 UErrorCode status
= U_ZERO_ERROR
;
771 ucal_clear(calendar
->_cal
);
772 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
773 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
774 ucal_setMillis(calendar
->_cal
, udate
, &status
);
775 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
777 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
778 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
779 ucal_setMillis(calendar
->_cal
, udate
, &status
);
780 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_MONTH
, &status
);
783 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
784 // Set calendar to first instant of big unit
785 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
786 UDate curr
= ucal_getMillis(calendar
->_cal
, &status
);
787 UDate goal
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
789 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
790 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
791 Boolean divide
= false, alwaysDivide
= false;
792 while (curr
< goal
) {
793 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
794 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
795 if (curr
< newcurr
&& newcurr
<= goal
) {
799 // Either newcurr is going backwards, or not making
800 // progress, or has overshot the goal; reset date
801 // and try smaller multiples.
802 ucal_setMillis(calendar
->_cal
, curr
, &status
);
804 // once we start overshooting the goal, the add at
805 // smaller multiples will succeed at most once for
806 // each multiple, so we reduce it every time through
808 if (goal
< newcurr
) alwaysDivide
= true;
811 multiple
= multiple
/ 2;
812 if (0 == multiple
) break;
813 divide
= alwaysDivide
;
820 Boolean
_CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, int *vector
, int count
) {
821 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
822 if (calendar
->_cal
) {
823 UErrorCode status
= U_ZERO_ERROR
;
824 ucal_clear(calendar
->_cal
);
825 ucal_set(calendar
->_cal
, UCAL_YEAR
, 1);
826 ucal_set(calendar
->_cal
, UCAL_MONTH
, 0);
827 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1);
828 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, 0);
829 ucal_set(calendar
->_cal
, UCAL_MINUTE
, 0);
830 ucal_set(calendar
->_cal
, UCAL_SECOND
, 0);
831 const char *desc
= componentDesc
;
832 Boolean doWOY
= false;
835 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
836 if (UCAL_WEEK_OF_YEAR
== field
) {
842 desc
= componentDesc
;
845 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
847 if (UCAL_YEAR
== field
&& doWOY
) field
= UCAL_YEAR_WOY
;
848 if (UCAL_MONTH
== field
) value
--;
849 ucal_set(calendar
->_cal
, field
, value
);
854 UDate udate
= ucal_getMillis(calendar
->_cal
, &status
);
855 CFAbsoluteTime at
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
857 return U_SUCCESS(status
) ? true : false;
862 Boolean
_CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, int **vector
, int count
) {
863 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
864 if (calendar
->_cal
) {
865 UErrorCode status
= U_ZERO_ERROR
;
866 ucal_clear(calendar
->_cal
);
867 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
868 ucal_setMillis(calendar
->_cal
, udate
, &status
);
869 char ch
= *componentDesc
;
871 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
872 int value
= ucal_get(calendar
->_cal
, field
, &status
);
873 if (UCAL_MONTH
== field
) value
++;
879 return U_SUCCESS(status
) ? true : false;
884 Boolean
_CFCalendarAddComponentsV(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, int *vector
, int count
) {
885 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
886 if (calendar
->_cal
) {
887 UErrorCode status
= U_ZERO_ERROR
;
888 ucal_clear(calendar
->_cal
);
889 UDate udate
= floor((*atp
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
890 ucal_setMillis(calendar
->_cal
, udate
, &status
);
891 char ch
= *componentDesc
;
893 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
894 int amount
= *vector
;
895 if (options
& kCFCalendarComponentsWrap
) {
896 ucal_roll(calendar
->_cal
, field
, amount
, &status
);
898 ucal_add(calendar
->_cal
, field
, amount
, &status
);
904 udate
= ucal_getMillis(calendar
->_cal
, &status
);
905 *atp
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
906 return U_SUCCESS(status
) ? true : false;
911 Boolean
_CFCalendarGetComponentDifferenceV(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, int **vector
, int count
) {
912 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
913 if (calendar
->_cal
) {
914 UErrorCode status
= U_ZERO_ERROR
;
915 ucal_clear(calendar
->_cal
);
916 UDate curr
= floor((startingAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
917 UDate goal
= floor((resultAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
918 ucal_setMillis(calendar
->_cal
, curr
, &status
);
919 int direction
= (startingAT
<= resultAT
) ? 1 : -1;
920 char ch
= *componentDesc
;
922 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
923 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
924 int multiple
= direction
* (1 << multiple_table
[flsl(__CFCalendarGetCalendarUnitFromChar(ch
)) - 1]);
925 Boolean divide
= false, alwaysDivide
= false;
927 while ((direction
> 0 && curr
< goal
) || (direction
< 0 && goal
< curr
)) {
928 ucal_add(calendar
->_cal
, field
, multiple
, &status
);
929 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
930 if ((direction
> 0 && curr
< newcurr
&& newcurr
<= goal
) || (direction
< 0 && newcurr
< curr
&& goal
<= newcurr
)) {
934 // Either newcurr is going backwards, or not making
935 // progress, or has overshot the goal; reset date
936 // and try smaller multiples.
937 ucal_setMillis(calendar
->_cal
, curr
, &status
);
939 // once we start overshooting the goal, the add at
940 // smaller multiples will succeed at most once for
941 // each multiple, so we reduce it every time through
943 if ((direction
> 0 && goal
< newcurr
) || (direction
< 0 && newcurr
< goal
)) alwaysDivide
= true;
946 multiple
= multiple
/ 2;
947 if (0 == multiple
) break;
948 divide
= alwaysDivide
;
956 return U_SUCCESS(status
) ? true : false;
961 Boolean
CFCalendarComposeAbsoluteTime(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, ...) {
963 va_start(args
, componentDesc
);
964 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_composeAbsoluteTime:::", atp
, componentDesc
, args
);
965 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
966 int idx
, cnt
= strlen((char *)componentDesc
);
967 STACK_BUFFER_DECL(int, vector
, cnt
);
968 for (idx
= 0; idx
< cnt
; idx
++) {
969 int arg
= va_arg(args
, int);
973 return _CFCalendarComposeAbsoluteTimeV(calendar
, atp
, componentDesc
, vector
, cnt
);
976 Boolean
CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, ...) {
978 va_start(args
, componentDesc
);
979 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_decomposeAbsoluteTime:::", at
, componentDesc
, args
);
980 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
981 int idx
, cnt
= strlen((char *)componentDesc
);
982 STACK_BUFFER_DECL(int *, vector
, cnt
);
983 for (idx
= 0; idx
< cnt
; idx
++) {
984 int *arg
= va_arg(args
, int *);
988 return _CFCalendarDecomposeAbsoluteTimeV(calendar
, at
, componentDesc
, vector
, cnt
);
991 Boolean
CFCalendarAddComponents(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, ...) {
993 va_start(args
, componentDesc
);
994 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_addComponents::::", atp
, options
, componentDesc
, args
);
995 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
996 int idx
, cnt
= strlen((char *)componentDesc
);
997 STACK_BUFFER_DECL(int, vector
, cnt
);
998 for (idx
= 0; idx
< cnt
; idx
++) {
999 int arg
= va_arg(args
, int);
1003 return _CFCalendarAddComponentsV(calendar
, atp
, options
, componentDesc
, vector
, cnt
);
1006 Boolean
CFCalendarGetComponentDifference(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, ...) {
1008 va_start(args
, componentDesc
);
1009 CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean
, calendar
, "_diffComponents:::::", startingAT
, resultAT
, options
, componentDesc
, args
);
1010 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1011 int idx
, cnt
= strlen((char *)componentDesc
);
1012 STACK_BUFFER_DECL(int *, vector
, cnt
);
1013 for (idx
= 0; idx
< cnt
; idx
++) {
1014 int *arg
= va_arg(args
, int *);
1018 Boolean ret
= _CFCalendarGetComponentDifferenceV(calendar
, startingAT
, resultAT
, options
, componentDesc
, vector
, cnt
);
1022 Boolean
CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
, CFAbsoluteTime
*startp
, CFTimeInterval
*tip
) {
1023 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_rangeOfUnit:startTime:interval:forAT:", unit
, startp
, tip
, at
);
1024 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1025 if (kCFCalendarUnitWeekdayOrdinal
== unit
) return false;
1026 if (kCFCalendarUnitWeekday
== unit
) unit
= kCFCalendarUnitDay
;
1027 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
1028 if (calendar
->_cal
) {
1029 ucal_clear(calendar
->_cal
);
1030 __CFCalendarSetToFirstInstant(calendar
, unit
, at
);
1031 UErrorCode status
= U_ZERO_ERROR
;
1032 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
1033 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
1034 ucal_add(calendar
->_cal
, field
, 1, &status
);
1035 UDate end
= ucal_getMillis(calendar
->_cal
, &status
);
1036 if (end
== start
&& kCFCalendarUnitEra
== unit
) {
1037 // ICU refuses to do the addition, probably because we are
1038 // at the limit of UCAL_ERA. Use alternate strategy.
1039 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
1040 if (100000 < limit
) limit
= 100000;
1041 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
1042 end
= ucal_getMillis(calendar
->_cal
, &status
);
1044 if (U_SUCCESS(status
)) {
1045 if (startp
) *startp
= (double)start
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1046 if (tip
) *tip
= (double)(end
- start
) / 1000.0;