2 * Copyright (c) 2011 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 (c) 2004-2011, Apple Inc. All rights reserved.
26 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 __private_extern__
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_DEFAULT
, &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 kCFCalendarUnitWeekOfYear
: return UCAL_WEEK_OF_YEAR
;
301 case kCFCalendarUnitWeekOfMonth
: return UCAL_WEEK_OF_MONTH
;
302 case kCFCalendarUnitYearForWeekOfYear
: return UCAL_YEAR_WOY
;
303 case kCFCalendarUnitWeekday
: return UCAL_DAY_OF_WEEK
;
304 case kCFCalendarUnitWeekdayOrdinal
: return UCAL_DAY_OF_WEEK_IN_MONTH
;
306 return (UCalendarDateFields
)-1;
309 static UCalendarDateFields
__CFCalendarGetICUFieldCodeFromChar(char ch
) {
311 case 'G': return UCAL_ERA
;
312 case 'y': return UCAL_YEAR
;
313 case 'M': return UCAL_MONTH
;
314 case 'd': return UCAL_DAY_OF_MONTH
;
315 case 'h': return UCAL_HOUR
;
316 case 'H': return UCAL_HOUR_OF_DAY
;
317 case 'm': return UCAL_MINUTE
;
318 case 's': return UCAL_SECOND
;
319 case 'S': return UCAL_MILLISECOND
;
320 case 'w': return UCAL_WEEK_OF_YEAR
;
321 case 'W': return UCAL_WEEK_OF_MONTH
;
322 case 'Y': return UCAL_YEAR_WOY
;
323 case 'E': return UCAL_DAY_OF_WEEK
;
324 case 'D': return UCAL_DAY_OF_YEAR
;
325 case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH
;
326 case 'a': return UCAL_AM_PM
;
327 case 'g': return UCAL_JULIAN_DAY
;
329 return (UCalendarDateFields
)-1;
332 static CFCalendarUnit
__CFCalendarGetCalendarUnitFromChar(char ch
) {
334 case 'G': return kCFCalendarUnitEra
;
335 case 'y': return kCFCalendarUnitYear
;
336 case 'M': return kCFCalendarUnitMonth
;
337 case 'd': return kCFCalendarUnitDay
;
338 case 'H': return kCFCalendarUnitHour
;
339 case 'm': return kCFCalendarUnitMinute
;
340 case 's': return kCFCalendarUnitSecond
;
341 case 'w': return kCFCalendarUnitWeekOfYear
;
342 case 'W': return kCFCalendarUnitWeekOfMonth
;
343 case 'Y': return kCFCalendarUnitYearForWeekOfYear
;
344 case 'E': return kCFCalendarUnitWeekday
;
345 case 'F': return kCFCalendarUnitWeekdayOrdinal
;
347 return (UCalendarDateFields
)-1;
350 CFRange
CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
351 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_minimumRangeOfUnit:", unit
);
352 CFRange range
= {kCFNotFound
, kCFNotFound
};
353 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
354 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
355 if (calendar
->_cal
) {
356 ucal_clear(calendar
->_cal
);
357 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
358 UErrorCode status
= U_ZERO_ERROR
;
359 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_GREATEST_MINIMUM
, &status
);
360 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_LEAST_MAXIMUM
, &status
) - range
.location
+ 1;
361 if (UCAL_MONTH
== field
) range
.location
++;
362 if (100000 < range
.length
) range
.length
= 100000;
367 CFRange
CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
368 CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange
, calendar
, "_maximumRangeOfUnit:", unit
);
369 CFRange range
= {kCFNotFound
, kCFNotFound
};
370 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
371 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
372 if (calendar
->_cal
) {
373 ucal_clear(calendar
->_cal
);
374 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
375 UErrorCode status
= U_ZERO_ERROR
;
376 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MINIMUM
, &status
);
377 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MAXIMUM
, &status
) - range
.location
+ 1;
378 if (UCAL_MONTH
== field
) range
.location
++;
379 if (100000 < range
.length
) range
.length
= 100000;
384 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
) {
385 // Set UCalendar to first instant of unit prior to 'at'
386 UErrorCode status
= U_ZERO_ERROR
;
387 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
388 ucal_setMillis(calendar
->_cal
, udate
, &status
);
389 int target_era
= INT_MIN
;
390 switch (unit
) { // largest to smallest, we set the fields to their minimum value
391 case kCFCalendarUnitYearForWeekOfYear
:;
392 ucal_set(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
393 case kCFCalendarUnitWeek
:
394 case kCFCalendarUnitWeekOfMonth
:;
395 case kCFCalendarUnitWeekOfYear
:;
397 // reduce to first day of week, then reduce the rest of the day
398 int32_t goal
= ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
399 int32_t dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
400 while (dow
!= goal
) {
401 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
402 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
406 case kCFCalendarUnitEra
:
408 target_era
= ucal_get(calendar
->_cal
, UCAL_ERA
, &status
);
409 ucal_set(calendar
->_cal
, UCAL_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
411 case kCFCalendarUnitYear
:
412 ucal_set(calendar
->_cal
, UCAL_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
413 case kCFCalendarUnitMonth
:
414 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_DAY_OF_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
415 case kCFCalendarUnitWeekday
:
416 case kCFCalendarUnitDay
:
418 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, ucal_getLimit(calendar
->_cal
, UCAL_HOUR_OF_DAY
, UCAL_ACTUAL_MINIMUM
, &status
));
419 case kCFCalendarUnitHour
:
420 ucal_set(calendar
->_cal
, UCAL_MINUTE
, ucal_getLimit(calendar
->_cal
, UCAL_MINUTE
, UCAL_ACTUAL_MINIMUM
, &status
));
421 case kCFCalendarUnitMinute
:
422 ucal_set(calendar
->_cal
, UCAL_SECOND
, ucal_getLimit(calendar
->_cal
, UCAL_SECOND
, UCAL_ACTUAL_MINIMUM
, &status
));
423 case kCFCalendarUnitSecond
:
424 ucal_set(calendar
->_cal
, UCAL_MILLISECOND
, 0);
426 if (INT_MIN
!= target_era
&& ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
427 // In the Japanese calendar, and possibly others, eras don't necessarily
428 // start on the first day of a year, so the previous code may have backed
429 // up into the previous era, and we have to correct forward.
430 UDate bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
431 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
432 while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
433 bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
434 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
436 udate
= ucal_getMillis(calendar
->_cal
, &status
);
437 // target date is between bad_udate and udate
439 UDate test_udate
= (udate
+ bad_udate
) / 2;
440 ucal_setMillis(calendar
->_cal
, test_udate
, &status
);
441 if (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
442 bad_udate
= test_udate
;
446 if (fabs(udate
- bad_udate
) < 1000) break;
449 bad_udate
= floor((bad_udate
+ 1000) / 1000) * 1000;
450 ucal_setMillis(calendar
->_cal
, bad_udate
, &status
);
451 } while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
);
455 static Boolean
__validUnits(CFCalendarUnit smaller
, CFCalendarUnit bigger
) {
457 case kCFCalendarUnitEra
:
458 if (kCFCalendarUnitEra
== smaller
) return false;
459 if (kCFCalendarUnitWeekday
== smaller
) return false;
460 if (kCFCalendarUnitMinute
== smaller
) return false; // this causes CFIndex overflow in range.length
461 if (kCFCalendarUnitSecond
== smaller
) return false; // this causes CFIndex overflow in range.length
463 case kCFCalendarUnitYearForWeekOfYear
:
464 case kCFCalendarUnitYear
:
465 if (kCFCalendarUnitEra
== smaller
) return false;
466 if (kCFCalendarUnitYear
== smaller
) return false;
467 if (kCFCalendarUnitYearForWeekOfYear
== smaller
) return false;
468 if (kCFCalendarUnitWeekday
== smaller
) return false;
470 case kCFCalendarUnitMonth
:
471 if (kCFCalendarUnitEra
== smaller
) return false;
472 if (kCFCalendarUnitYear
== smaller
) return false;
473 if (kCFCalendarUnitMonth
== smaller
) return false;
474 if (kCFCalendarUnitWeekday
== smaller
) return false;
476 case kCFCalendarUnitDay
:
477 if (kCFCalendarUnitHour
== smaller
) return true;
478 if (kCFCalendarUnitMinute
== smaller
) return true;
479 if (kCFCalendarUnitSecond
== smaller
) return true;
481 case kCFCalendarUnitHour
:
482 if (kCFCalendarUnitMinute
== smaller
) return true;
483 if (kCFCalendarUnitSecond
== smaller
) return true;
485 case kCFCalendarUnitMinute
:
486 if (kCFCalendarUnitSecond
== smaller
) return true;
488 case kCFCalendarUnitWeek
:
489 case kCFCalendarUnitWeekOfMonth
:
490 case kCFCalendarUnitWeekOfYear
:
491 if (kCFCalendarUnitWeekday
== smaller
) return true;
492 if (kCFCalendarUnitDay
== smaller
) return true;
493 if (kCFCalendarUnitHour
== smaller
) return true;
494 if (kCFCalendarUnitMinute
== smaller
) return true;
495 if (kCFCalendarUnitSecond
== smaller
) return true;
497 case kCFCalendarUnitSecond
:
498 case kCFCalendarUnitWeekday
:
499 case kCFCalendarUnitWeekdayOrdinal
:
505 static CFRange
__CFCalendarGetRangeOfUnit1(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
506 CFRange range
= {kCFNotFound
, kCFNotFound
};
507 if (!__validUnits(smallerUnit
, biggerUnit
)) return range
;
508 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
509 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
510 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
511 if (calendar
->_cal
) {
513 ucal_clear(calendar
->_cal
);
514 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
515 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
516 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
517 UErrorCode status
= U_ZERO_ERROR
;
518 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
519 ucal_setMillis(calendar
->_cal
, udate
, &status
);
520 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
522 // Set calendar to first instant of big unit
523 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
524 UErrorCode status
= U_ZERO_ERROR
;
525 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
526 if (kCFCalendarUnitWeek
== biggerUnit
) {
527 range
.location
= ucal_get(calendar
->_cal
, smallField
, &status
);
528 if (kCFCalendarUnitMonth
== smallerUnit
) range
.location
++;
530 range
.location
= (kCFCalendarUnitHour
== smallerUnit
|| kCFCalendarUnitMinute
== smallerUnit
|| kCFCalendarUnitSecond
== smallerUnit
) ? 0 : 1;
532 // Set calendar to first instant of next value of big unit
533 if (UCAL_ERA
== bigField
) {
534 // ICU refuses to do the addition, probably because we are
535 // at the limit of UCAL_ERA. Use alternate strategy.
536 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
537 if (100000 < limit
) limit
= 100000;
538 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
540 ucal_add(calendar
->_cal
, bigField
, 1, &status
);
542 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
543 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
544 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
545 while (1 == range
.length
) {
546 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
547 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
551 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
552 ucal_add(calendar
->_cal
, UCAL_SECOND
, -1, &status
);
553 range
.length
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
557 UDate goal
= ucal_getMillis(calendar
->_cal
, &status
);
558 // Set calendar back to first instant of big unit
559 ucal_setMillis(calendar
->_cal
, start
, &status
);
560 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
561 // roll day forward to first 'dow'
562 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
563 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
565 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
566 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
568 start
= ucal_getMillis(calendar
->_cal
, &status
);
570 range
.location
= 1; // constant here works around ICU -- see 3948293
573 range
.length
= (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) ? 1 : 0;
574 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
575 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
576 Boolean divide
= false, alwaysDivide
= false;
577 while (curr
< goal
) {
578 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
579 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
580 if (curr
< newcurr
&& newcurr
<= goal
) {
581 range
.length
+= multiple
;
584 // Either newcurr is going backwards, or not making
585 // progress, or has overshot the goal; reset date
586 // and try smaller multiples.
587 ucal_setMillis(calendar
->_cal
, curr
, &status
);
589 // once we start overshooting the goal, the add at
590 // smaller multiples will succeed at most once for
591 // each multiple, so we reduce it every time through
593 if (goal
< newcurr
) alwaysDivide
= true;
596 multiple
= multiple
/ 2;
597 if (0 == multiple
) break;
598 divide
= alwaysDivide
;
605 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) __attribute__((noinline
));
606 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
607 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange
, calendar
, "_rangeOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
608 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
609 CFRange range
= {kCFNotFound
, kCFNotFound
};
610 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
611 if (calendar
->_cal
) {
612 switch (smallerUnit
) {
613 case kCFCalendarUnitSecond
:
614 switch (biggerUnit
) {
615 case kCFCalendarUnitMinute
:
616 case kCFCalendarUnitHour
:
617 case kCFCalendarUnitDay
:
618 case kCFCalendarUnitWeekday
:
619 case kCFCalendarUnitWeek
:
620 case kCFCalendarUnitMonth
:
621 case kCFCalendarUnitYear
:
622 case kCFCalendarUnitEra
:
629 case kCFCalendarUnitMinute
:
630 switch (biggerUnit
) {
631 case kCFCalendarUnitHour
:
632 case kCFCalendarUnitDay
:
633 case kCFCalendarUnitWeekday
:
634 case kCFCalendarUnitWeek
:
635 case kCFCalendarUnitMonth
:
636 case kCFCalendarUnitYear
:
637 case kCFCalendarUnitEra
:
644 case kCFCalendarUnitHour
:
645 switch (biggerUnit
) {
646 case kCFCalendarUnitDay
:
647 case kCFCalendarUnitWeekday
:
648 case kCFCalendarUnitWeek
:
649 case kCFCalendarUnitMonth
:
650 case kCFCalendarUnitYear
:
651 case kCFCalendarUnitEra
:
658 case kCFCalendarUnitDay
:
659 switch (biggerUnit
) {
660 case kCFCalendarUnitWeek
:
661 case kCFCalendarUnitMonth
:
662 case kCFCalendarUnitYear
:
663 case kCFCalendarUnitEra
:
668 case kCFCalendarUnitWeekday
:
669 switch (biggerUnit
) {
670 case kCFCalendarUnitWeek
:
671 case kCFCalendarUnitMonth
:
672 case kCFCalendarUnitYear
:
673 case kCFCalendarUnitEra
:
678 case kCFCalendarUnitWeekdayOrdinal
:
679 switch (biggerUnit
) {
680 case kCFCalendarUnitMonth
:
681 case kCFCalendarUnitYear
:
682 case kCFCalendarUnitEra
:
687 case kCFCalendarUnitWeek
:
688 switch (biggerUnit
) {
689 case kCFCalendarUnitMonth
:
690 case kCFCalendarUnitYear
:
691 case kCFCalendarUnitEra
:
696 case kCFCalendarUnitMonth
:
697 switch (biggerUnit
) {
698 case kCFCalendarUnitYear
:
699 case kCFCalendarUnitEra
:
704 case kCFCalendarUnitYear
:
705 switch (biggerUnit
) {
706 case kCFCalendarUnitEra
:
711 case kCFCalendarUnitEra
:
718 ucal_clear(calendar
->_cal
);
719 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
720 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
721 UCalendarDateFields yearField
= __CFCalendarGetICUFieldCode(kCFCalendarUnitYear
);
722 UCalendarDateFields fieldToAdd
= smallField
;
723 if (kCFCalendarUnitWeekday
== smallerUnit
) {
724 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitDay
);
727 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
728 UErrorCode status
= U_ZERO_ERROR
;
729 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
730 ucal_setMillis(calendar
->_cal
, udate
, &status
);
731 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
732 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek
);
734 // Set calendar to first instant of big unit
735 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
736 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
737 UErrorCode status
= U_ZERO_ERROR
;
738 // roll day forward to first 'dow'
739 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
740 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
742 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
743 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
746 int32_t minSmallValue
= INT32_MAX
;
747 int32_t maxSmallValue
= INT32_MIN
;
748 UErrorCode status
= U_ZERO_ERROR
;
749 int32_t bigValue
= ucal_get(calendar
->_cal
, bigField
, &status
);
751 int32_t smallValue
= ucal_get(calendar
->_cal
, smallField
, &status
);
752 if (smallValue
< minSmallValue
) minSmallValue
= smallValue
;
753 if (smallValue
> maxSmallValue
) maxSmallValue
= smallValue
;
754 ucal_add(calendar
->_cal
, fieldToAdd
, 1, &status
);
755 if (bigValue
!= ucal_get(calendar
->_cal
, bigField
, &status
)) break;
756 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) break;
757 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
759 status
= U_ZERO_ERROR
;
760 range
.location
= minSmallValue
;
761 if (smallerUnit
== kCFCalendarUnitMonth
) range
.location
= 1;
762 range
.length
= maxSmallValue
- minSmallValue
+ 1;
763 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) range
.length
= 100000;
768 CFRange
CFCalendarGetRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
769 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
)) {
770 return __CFCalendarGetRangeOfUnit2(calendar
, smallerUnit
, biggerUnit
, at
);
772 return __CFCalendarGetRangeOfUnit1(calendar
, smallerUnit
, biggerUnit
, at
);
776 CFIndex
CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
777 CFIndex result
= kCFNotFound
;
778 if (!__validUnits(smallerUnit
, biggerUnit
)) return result
;
779 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex
, calendar
, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit
, biggerUnit
, at
);
780 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
781 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
782 if (calendar
->_cal
) {
783 UErrorCode status
= U_ZERO_ERROR
;
784 ucal_clear(calendar
->_cal
);
785 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
786 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
787 ucal_setMillis(calendar
->_cal
, udate
, &status
);
788 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
790 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
791 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
792 ucal_setMillis(calendar
->_cal
, udate
, &status
);
793 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_MONTH
, &status
);
796 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
797 // Set calendar to first instant of big unit
798 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
799 UDate curr
= ucal_getMillis(calendar
->_cal
, &status
);
800 UDate goal
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
802 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
803 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
804 Boolean divide
= false, alwaysDivide
= false;
805 while (curr
< goal
) {
806 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
807 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
808 if (curr
< newcurr
&& newcurr
<= goal
) {
812 // Either newcurr is going backwards, or not making
813 // progress, or has overshot the goal; reset date
814 // and try smaller multiples.
815 ucal_setMillis(calendar
->_cal
, curr
, &status
);
817 // once we start overshooting the goal, the add at
818 // smaller multiples will succeed at most once for
819 // each multiple, so we reduce it every time through
821 if (goal
< newcurr
) alwaysDivide
= true;
824 multiple
= multiple
/ 2;
825 if (0 == multiple
) break;
826 divide
= alwaysDivide
;
833 Boolean
_CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, int *vector
, int count
) {
834 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
835 if (calendar
->_cal
) {
836 UErrorCode status
= U_ZERO_ERROR
;
837 ucal_clear(calendar
->_cal
);
838 ucal_set(calendar
->_cal
, UCAL_YEAR
, 1);
839 ucal_set(calendar
->_cal
, UCAL_MONTH
, 0);
840 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1);
841 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, 0);
842 ucal_set(calendar
->_cal
, UCAL_MINUTE
, 0);
843 ucal_set(calendar
->_cal
, UCAL_SECOND
, 0);
844 const char *desc
= componentDesc
;
845 Boolean doWOY
= false;
848 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
849 if (UCAL_WEEK_OF_YEAR
== field
) {
855 desc
= componentDesc
;
858 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
860 if (UCAL_YEAR
== field
&& doWOY
) field
= UCAL_YEAR_WOY
;
861 if (UCAL_MONTH
== field
) value
--;
862 ucal_set(calendar
->_cal
, field
, value
);
867 UDate udate
= ucal_getMillis(calendar
->_cal
, &status
);
868 CFAbsoluteTime at
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
870 return U_SUCCESS(status
) ? true : false;
875 Boolean
_CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, int **vector
, int count
) {
876 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
877 if (calendar
->_cal
) {
878 UErrorCode status
= U_ZERO_ERROR
;
879 ucal_clear(calendar
->_cal
);
880 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
881 ucal_setMillis(calendar
->_cal
, udate
, &status
);
882 char ch
= *componentDesc
;
884 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
885 int value
= ucal_get(calendar
->_cal
, field
, &status
);
886 if (UCAL_MONTH
== field
) value
++;
892 return U_SUCCESS(status
) ? true : false;
897 Boolean
_CFCalendarAddComponentsV(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, int *vector
, int count
) {
898 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
899 if (calendar
->_cal
) {
900 UErrorCode status
= U_ZERO_ERROR
;
901 ucal_clear(calendar
->_cal
);
902 UDate udate
= floor((*atp
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
903 ucal_setMillis(calendar
->_cal
, udate
, &status
);
904 char ch
= *componentDesc
;
906 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
907 int amount
= *vector
;
908 if (options
& kCFCalendarComponentsWrap
) {
909 ucal_roll(calendar
->_cal
, field
, amount
, &status
);
911 ucal_add(calendar
->_cal
, field
, amount
, &status
);
917 udate
= ucal_getMillis(calendar
->_cal
, &status
);
918 *atp
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
919 return U_SUCCESS(status
) ? true : false;
924 Boolean
_CFCalendarGetComponentDifferenceV(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, int **vector
, int count
) {
925 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
926 if (calendar
->_cal
) {
927 UErrorCode status
= U_ZERO_ERROR
;
928 ucal_clear(calendar
->_cal
);
929 UDate curr
= floor((startingAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
930 UDate goal
= floor((resultAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
931 ucal_setMillis(calendar
->_cal
, curr
, &status
);
932 int direction
= (startingAT
<= resultAT
) ? 1 : -1;
933 char ch
= *componentDesc
;
935 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
936 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
937 int multiple
= direction
* (1 << multiple_table
[flsl(__CFCalendarGetCalendarUnitFromChar(ch
)) - 1]);
938 Boolean divide
= false, alwaysDivide
= false;
940 while ((direction
> 0 && curr
< goal
) || (direction
< 0 && goal
< curr
)) {
941 ucal_add(calendar
->_cal
, field
, multiple
, &status
);
942 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
943 if ((direction
> 0 && curr
< newcurr
&& newcurr
<= goal
) || (direction
< 0 && newcurr
< curr
&& goal
<= newcurr
)) {
947 // Either newcurr is going backwards, or not making
948 // progress, or has overshot the goal; reset date
949 // and try smaller multiples.
950 ucal_setMillis(calendar
->_cal
, curr
, &status
);
952 // once we start overshooting the goal, the add at
953 // smaller multiples will succeed at most once for
954 // each multiple, so we reduce it every time through
956 if ((direction
> 0 && goal
< newcurr
) || (direction
< 0 && newcurr
< goal
)) alwaysDivide
= true;
959 multiple
= multiple
/ 2;
960 if (0 == multiple
) break;
961 divide
= alwaysDivide
;
969 return U_SUCCESS(status
) ? true : false;
974 Boolean
CFCalendarComposeAbsoluteTime(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, ...) {
976 va_start(args
, componentDesc
);
977 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_composeAbsoluteTime:::", atp
, componentDesc
, args
);
978 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
979 int idx
, cnt
= strlen((char *)componentDesc
);
980 STACK_BUFFER_DECL(int, vector
, cnt
);
981 for (idx
= 0; idx
< cnt
; idx
++) {
982 int arg
= va_arg(args
, int);
986 return _CFCalendarComposeAbsoluteTimeV(calendar
, atp
, componentDesc
, vector
, cnt
);
989 Boolean
CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, ...) {
991 va_start(args
, componentDesc
);
992 CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean
, calendar
, "_decomposeAbsoluteTime:::", at
, componentDesc
, args
);
993 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
994 int idx
, cnt
= strlen((char *)componentDesc
);
995 STACK_BUFFER_DECL(int *, vector
, cnt
);
996 for (idx
= 0; idx
< cnt
; idx
++) {
997 int *arg
= va_arg(args
, int *);
1001 return _CFCalendarDecomposeAbsoluteTimeV(calendar
, at
, componentDesc
, vector
, cnt
);
1004 Boolean
CFCalendarAddComponents(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, ...) {
1006 va_start(args
, componentDesc
);
1007 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_addComponents::::", atp
, options
, componentDesc
, args
);
1008 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1009 int idx
, cnt
= strlen((char *)componentDesc
);
1010 STACK_BUFFER_DECL(int, vector
, cnt
);
1011 for (idx
= 0; idx
< cnt
; idx
++) {
1012 int arg
= va_arg(args
, int);
1016 return _CFCalendarAddComponentsV(calendar
, atp
, options
, componentDesc
, vector
, cnt
);
1019 Boolean
CFCalendarGetComponentDifference(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, ...) {
1021 va_start(args
, componentDesc
);
1022 CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean
, calendar
, "_diffComponents:::::", startingAT
, resultAT
, options
, componentDesc
, args
);
1023 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1024 int idx
, cnt
= strlen((char *)componentDesc
);
1025 STACK_BUFFER_DECL(int *, vector
, cnt
);
1026 for (idx
= 0; idx
< cnt
; idx
++) {
1027 int *arg
= va_arg(args
, int *);
1031 Boolean ret
= _CFCalendarGetComponentDifferenceV(calendar
, startingAT
, resultAT
, options
, componentDesc
, vector
, cnt
);
1035 Boolean
CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
, CFAbsoluteTime
*startp
, CFTimeInterval
*tip
) {
1036 CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean
, calendar
, "_rangeOfUnit:startTime:interval:forAT:", unit
, startp
, tip
, at
);
1037 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
1038 if (kCFCalendarUnitWeekdayOrdinal
== unit
) return false;
1039 if (kCFCalendarUnitWeekday
== unit
) unit
= kCFCalendarUnitDay
;
1040 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
1041 if (calendar
->_cal
) {
1042 ucal_clear(calendar
->_cal
);
1043 __CFCalendarSetToFirstInstant(calendar
, unit
, at
);
1044 UErrorCode status
= U_ZERO_ERROR
;
1045 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
1046 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
1047 ucal_add(calendar
->_cal
, field
, 1, &status
);
1048 UDate end
= ucal_getMillis(calendar
->_cal
, &status
);
1049 if (end
== start
&& kCFCalendarUnitEra
== unit
) {
1050 // ICU refuses to do the addition, probably because we are
1051 // at the limit of UCAL_ERA. Use alternate strategy.
1052 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
1053 if (100000 < limit
) limit
= 100000;
1054 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
1055 end
= ucal_getMillis(calendar
->_cal
, &status
);
1057 if (U_SUCCESS(status
)) {
1058 if (startp
) *startp
= (double)start
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
1059 if (tip
) *tip
= (double)(end
- start
) / 1000.0;