2 * Copyright (c) 2014 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-2014, 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 CFTypeID
CFCalendarGetTypeID(void) {
87 static dispatch_once_t initOnce
;
88 dispatch_once(&initOnce
, ^{ __kCFCalendarTypeID
= _CFRuntimeRegisterClass(&__CFCalendarClass
); });
89 return __kCFCalendarTypeID
;
92 CF_PRIVATE UCalendar
*__CFCalendarCreateUCalendar(CFStringRef calendarID
, CFStringRef localeID
, CFTimeZoneRef tz
) {
94 CFDictionaryRef components
= CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault
, localeID
);
95 CFMutableDictionaryRef mcomponents
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, 0, components
);
96 CFDictionarySetValue(mcomponents
, kCFLocaleCalendarIdentifier
, calendarID
);
97 localeID
= CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault
, mcomponents
);
98 CFRelease(mcomponents
);
99 CFRelease(components
);
102 char buffer
[BUFFER_SIZE
];
103 const char *cstr
= CFStringGetCStringPtr(localeID
, kCFStringEncodingASCII
);
105 if (CFStringGetCString(localeID
, buffer
, BUFFER_SIZE
, kCFStringEncodingASCII
)) cstr
= buffer
;
108 if (calendarID
) CFRelease(localeID
);
112 UChar ubuffer
[BUFFER_SIZE
];
113 CFStringRef tznam
= CFTimeZoneGetName(tz
);
114 CFIndex cnt
= CFStringGetLength(tznam
);
115 if (BUFFER_SIZE
< cnt
) cnt
= BUFFER_SIZE
;
116 CFStringGetCharacters(tznam
, CFRangeMake(0, cnt
), (UniChar
*)ubuffer
);
118 UErrorCode status
= U_ZERO_ERROR
;
119 UCalendar
*cal
= ucal_open(ubuffer
, cnt
, cstr
, UCAL_DEFAULT
, &status
);
120 if (calendarID
) CFRelease(localeID
);
124 static void __CFCalendarSetupCal(CFCalendarRef calendar
) {
125 calendar
->_cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
128 static void __CFCalendarZapCal(CFCalendarRef calendar
) {
129 ucal_close(calendar
->_cal
);
130 calendar
->_cal
= NULL
;
133 CFCalendarRef
CFCalendarCopyCurrent(void) {
134 CFLocaleRef locale
= CFLocaleCopyCurrent();
135 CFCalendarRef calID
= (CFCalendarRef
)CFLocaleGetValue(locale
, kCFLocaleCalendarIdentifier
);
137 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, (CFStringRef
)calID
);
138 CFCalendarSetLocale(calendar
, locale
);
145 CFCalendarRef
CFCalendarCreateWithIdentifier(CFAllocatorRef allocator
, CFStringRef identifier
) {
146 if (allocator
== NULL
) allocator
= __CFGetDefaultAllocator();
147 __CFGenericValidateType(allocator
, CFAllocatorGetTypeID());
148 __CFGenericValidateType(identifier
, CFStringGetTypeID());
149 // return NULL until Chinese calendar is available
150 if (identifier
!= kCFGregorianCalendar
&& identifier
!= kCFBuddhistCalendar
&& identifier
!= kCFJapaneseCalendar
&& identifier
!= kCFIslamicCalendar
&& identifier
!= kCFIslamicCivilCalendar
&& identifier
!= kCFHebrewCalendar
) {
151 // if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
152 if (CFEqual(kCFGregorianCalendar
, identifier
)) identifier
= kCFGregorianCalendar
;
153 else if (CFEqual(kCFBuddhistCalendar
, identifier
)) identifier
= kCFBuddhistCalendar
;
154 else if (CFEqual(kCFJapaneseCalendar
, identifier
)) identifier
= kCFJapaneseCalendar
;
155 else if (CFEqual(kCFIslamicCalendar
, identifier
)) identifier
= kCFIslamicCalendar
;
156 else if (CFEqual(kCFIslamicCivilCalendar
, identifier
)) identifier
= kCFIslamicCivilCalendar
;
157 else if (CFEqual(kCFHebrewCalendar
, identifier
)) identifier
= kCFHebrewCalendar
;
158 // else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
161 struct __CFCalendar
*calendar
= NULL
;
162 uint32_t size
= sizeof(struct __CFCalendar
) - sizeof(CFRuntimeBase
);
163 calendar
= (struct __CFCalendar
*)_CFRuntimeCreateInstance(allocator
, CFCalendarGetTypeID(), size
, NULL
);
164 if (NULL
== calendar
) {
167 calendar
->_identifier
= (CFStringRef
)CFRetain(identifier
);
168 calendar
->_locale
= NULL
;
169 calendar
->_localeID
= CFLocaleGetIdentifier(CFLocaleGetSystem());
170 calendar
->_tz
= CFTimeZoneCopyDefault();
171 calendar
->_cal
= NULL
;
172 return (CFCalendarRef
)calendar
;
175 CFStringRef
CFCalendarGetIdentifier(CFCalendarRef calendar
) {
176 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFStringRef
, calendar
, calendarIdentifier
);
177 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
178 return calendar
->_identifier
;
181 CFLocaleRef
CFCalendarCopyLocale(CFCalendarRef calendar
) {
182 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFLocaleRef
, calendar
, _copyLocale
);
183 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
184 return (CFLocaleRef
)CFLocaleCreate(kCFAllocatorSystemDefault
, calendar
->_localeID
);
187 void CFCalendarSetLocale(CFCalendarRef calendar
, CFLocaleRef locale
) {
188 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar
, setLocale
:locale
);
189 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
190 __CFGenericValidateType(locale
, CFLocaleGetTypeID());
191 CFStringRef localeID
= CFLocaleGetIdentifier(locale
);
192 if (localeID
!= calendar
->_localeID
) {
193 CFRelease(calendar
->_localeID
);
195 calendar
->_localeID
= localeID
;
196 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
200 CFTimeZoneRef
CFCalendarCopyTimeZone(CFCalendarRef calendar
) {
201 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFTimeZoneRef
, calendar_copyTimeZone
);
202 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
203 return (CFTimeZoneRef
)CFRetain(calendar
->_tz
);
206 void CFCalendarSetTimeZone(CFCalendarRef calendar
, CFTimeZoneRef tz
) {
207 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar
, setTimeZone
:tz
);
208 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
209 if (tz
) __CFGenericValidateType(tz
, CFTimeZoneGetTypeID());
210 if (tz
!= calendar
->_tz
) {
211 CFRelease(calendar
->_tz
);
212 calendar
->_tz
= tz
? (CFTimeZoneRef
)CFRetain(tz
) : CFTimeZoneCopyDefault();
213 if (calendar
->_cal
) __CFCalendarZapCal(calendar
);
217 CFIndex
CFCalendarGetFirstWeekday(CFCalendarRef calendar
) {
218 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex
, calendar
, firstWeekday
);
219 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
220 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
221 if (calendar
->_cal
) {
222 return ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
227 void CFCalendarSetFirstWeekday(CFCalendarRef calendar
, CFIndex wkdy
) {
228 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar
, setFirstWeekday
:wkdy
);
229 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
230 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
231 if (calendar
->_cal
) {
232 ucal_setAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
, wkdy
);
236 CFIndex
CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar
) {
237 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex
, calendar
, minimumDaysInFirstWeek
);
238 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
239 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
240 return calendar
->_cal
? ucal_getAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
) : -1;
243 void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar
, CFIndex mwd
) {
244 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar
, setMinimumDaysInFirstWeek
:mwd
);
245 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
246 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
247 if (calendar
->_cal
) ucal_setAttribute(calendar
->_cal
, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
, mwd
);
250 CFDateRef
CFCalendarCopyGregorianStartDate(CFCalendarRef calendar
) {
251 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFDateRef
, calendar
, _gregorianStartDate
);
252 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
253 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
254 UErrorCode status
= U_ZERO_ERROR
;
255 UDate udate
= calendar
->_cal
? ucal_getGregorianChange(calendar
->_cal
, &status
) : 0;
256 if (calendar
->_cal
&& U_SUCCESS(status
)) {
257 CFAbsoluteTime at
= (double)udate
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
258 return CFDateCreate(CFGetAllocator(calendar
), at
);
263 void CFCalendarSetGregorianStartDate(CFCalendarRef calendar
, CFDateRef date
) {
264 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), void, calendar
, _setGregorianStartDate
:date
);
265 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
266 if (date
) __CFGenericValidateType(date
, CFDateGetTypeID());
267 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
268 if (!calendar
->_cal
) return;
270 UErrorCode status
= U_ZERO_ERROR
;
271 UCalendar
*cal
= __CFCalendarCreateUCalendar(calendar
->_identifier
, calendar
->_localeID
, calendar
->_tz
);
272 UDate udate
= cal
? ucal_getGregorianChange(cal
, &status
) : 0;
273 if (cal
&& U_SUCCESS(status
)) {
274 status
= U_ZERO_ERROR
;
275 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
277 if (cal
) ucal_close(cal
);
279 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
280 UDate udate
= (at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0;
281 UErrorCode status
= U_ZERO_ERROR
;
282 if (calendar
->_cal
) ucal_setGregorianChange(calendar
->_cal
, udate
, &status
);
287 static UCalendarDateFields
__CFCalendarGetICUFieldCode(CFCalendarUnit unit
) {
289 case kCFCalendarUnitEra
: return UCAL_ERA
;
290 case kCFCalendarUnitYear
: return UCAL_YEAR
;
291 case kCFCalendarUnitMonth
: return UCAL_MONTH
;
292 case kCFCalendarUnitDay
: return UCAL_DAY_OF_MONTH
;
293 case kCFCalendarUnitHour
: return UCAL_HOUR_OF_DAY
;
294 case kCFCalendarUnitMinute
: return UCAL_MINUTE
;
295 case kCFCalendarUnitSecond
: return UCAL_SECOND
;
296 case kCFCalendarUnitWeek
: return UCAL_WEEK_OF_YEAR
;
297 case kCFCalendarUnitWeekOfYear
: return UCAL_WEEK_OF_YEAR
;
298 case kCFCalendarUnitWeekOfMonth
: return UCAL_WEEK_OF_MONTH
;
299 case kCFCalendarUnitYearForWeekOfYear
: return UCAL_YEAR_WOY
;
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 'Y': return UCAL_YEAR_WOY
;
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 CFCalendarUnit
__CFCalendarGetCalendarUnitFromChar(char ch
) {
331 case 'G': return kCFCalendarUnitEra
;
332 case 'y': return kCFCalendarUnitYear
;
333 case 'M': return kCFCalendarUnitMonth
;
334 case 'd': return kCFCalendarUnitDay
;
335 case 'H': return kCFCalendarUnitHour
;
336 case 'm': return kCFCalendarUnitMinute
;
337 case 's': return kCFCalendarUnitSecond
;
338 case 'w': return kCFCalendarUnitWeekOfYear
;
339 case 'W': return kCFCalendarUnitWeekOfMonth
;
340 case 'Y': return kCFCalendarUnitYearForWeekOfYear
;
341 case 'E': return kCFCalendarUnitWeekday
;
342 case 'F': return kCFCalendarUnitWeekdayOrdinal
;
344 return (UCalendarDateFields
)-1;
347 CFRange
CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
348 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange
, calendar
, _minimumRangeOfUnit
:unit
);
349 CFRange range
= {kCFNotFound
, kCFNotFound
};
350 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
351 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
352 if (calendar
->_cal
) {
353 ucal_clear(calendar
->_cal
);
354 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
355 UErrorCode status
= U_ZERO_ERROR
;
356 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_GREATEST_MINIMUM
, &status
);
357 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_LEAST_MAXIMUM
, &status
) - range
.location
+ 1;
358 if (UCAL_MONTH
== field
) range
.location
++;
359 if (100000 < range
.length
) range
.length
= 100000;
364 CFRange
CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
) {
365 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange
, calendar
, _maximumRangeOfUnit
:unit
);
366 CFRange range
= {kCFNotFound
, kCFNotFound
};
367 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
368 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
369 if (calendar
->_cal
) {
370 ucal_clear(calendar
->_cal
);
371 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
372 UErrorCode status
= U_ZERO_ERROR
;
373 range
.location
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MINIMUM
, &status
);
374 range
.length
= ucal_getLimit(calendar
->_cal
, field
, UCAL_MAXIMUM
, &status
) - range
.location
+ 1;
375 if (UCAL_MONTH
== field
) range
.location
++;
376 if (100000 < range
.length
) range
.length
= 100000;
381 static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
) {
382 // Set UCalendar to first instant of unit prior to 'at'
383 UErrorCode status
= U_ZERO_ERROR
;
384 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
385 ucal_setMillis(calendar
->_cal
, udate
, &status
);
386 int target_era
= INT_MIN
;
387 switch (unit
) { // largest to smallest, we set the fields to their minimum value
388 case kCFCalendarUnitYearForWeekOfYear
:;
389 ucal_set(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
390 case kCFCalendarUnitWeek
:
391 case kCFCalendarUnitWeekOfMonth
:;
392 case kCFCalendarUnitWeekOfYear
:;
394 // reduce to first day of week, then reduce the rest of the day
395 int32_t goal
= ucal_getAttribute(calendar
->_cal
, UCAL_FIRST_DAY_OF_WEEK
);
396 int32_t dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
397 while (dow
!= goal
) {
398 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, -1, &status
);
399 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
403 case kCFCalendarUnitEra
:
405 target_era
= ucal_get(calendar
->_cal
, UCAL_ERA
, &status
);
406 ucal_set(calendar
->_cal
, UCAL_YEAR
, ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_ACTUAL_MINIMUM
, &status
));
408 case kCFCalendarUnitYear
:
409 ucal_set(calendar
->_cal
, UCAL_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
410 case kCFCalendarUnitMonth
:
411 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, ucal_getLimit(calendar
->_cal
, UCAL_DAY_OF_MONTH
, UCAL_ACTUAL_MINIMUM
, &status
));
412 case kCFCalendarUnitWeekday
:
413 case kCFCalendarUnitDay
:
415 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, ucal_getLimit(calendar
->_cal
, UCAL_HOUR_OF_DAY
, UCAL_ACTUAL_MINIMUM
, &status
));
416 case kCFCalendarUnitHour
:
417 ucal_set(calendar
->_cal
, UCAL_MINUTE
, ucal_getLimit(calendar
->_cal
, UCAL_MINUTE
, UCAL_ACTUAL_MINIMUM
, &status
));
418 case kCFCalendarUnitMinute
:
419 ucal_set(calendar
->_cal
, UCAL_SECOND
, ucal_getLimit(calendar
->_cal
, UCAL_SECOND
, UCAL_ACTUAL_MINIMUM
, &status
));
420 case kCFCalendarUnitSecond
:
421 ucal_set(calendar
->_cal
, UCAL_MILLISECOND
, 0);
423 if (INT_MIN
!= target_era
&& ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
424 // In the Japanese calendar, and possibly others, eras don't necessarily
425 // start on the first day of a year, so the previous code may have backed
426 // up into the previous era, and we have to correct forward.
427 UDate bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
428 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
429 while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
430 bad_udate
= ucal_getMillis(calendar
->_cal
, &status
);
431 ucal_add(calendar
->_cal
, UCAL_MONTH
, 1, &status
);
433 udate
= ucal_getMillis(calendar
->_cal
, &status
);
434 // target date is between bad_udate and udate
436 UDate test_udate
= (udate
+ bad_udate
) / 2;
437 ucal_setMillis(calendar
->_cal
, test_udate
, &status
);
438 if (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
) {
439 bad_udate
= test_udate
;
443 if (fabs(udate
- bad_udate
) < 1000) break;
446 bad_udate
= floor((bad_udate
+ 1000) / 1000) * 1000;
447 ucal_setMillis(calendar
->_cal
, bad_udate
, &status
);
448 } while (ucal_get(calendar
->_cal
, UCAL_ERA
, &status
) < target_era
);
452 static Boolean
__validUnits(CFCalendarUnit smaller
, CFCalendarUnit bigger
) {
454 case kCFCalendarUnitEra
:
455 if (kCFCalendarUnitEra
== smaller
) return false;
456 if (kCFCalendarUnitWeekday
== smaller
) return false;
457 if (kCFCalendarUnitMinute
== smaller
) return false; // this causes CFIndex overflow in range.length
458 if (kCFCalendarUnitSecond
== smaller
) return false; // this causes CFIndex overflow in range.length
460 case kCFCalendarUnitYearForWeekOfYear
:
461 case kCFCalendarUnitYear
:
462 if (kCFCalendarUnitEra
== smaller
) return false;
463 if (kCFCalendarUnitYear
== smaller
) return false;
464 if (kCFCalendarUnitYearForWeekOfYear
== smaller
) return false;
465 if (kCFCalendarUnitWeekday
== smaller
) return false;
467 case kCFCalendarUnitMonth
:
468 if (kCFCalendarUnitEra
== smaller
) return false;
469 if (kCFCalendarUnitYear
== smaller
) return false;
470 if (kCFCalendarUnitMonth
== smaller
) return false;
471 if (kCFCalendarUnitWeekday
== smaller
) return false;
473 case kCFCalendarUnitDay
:
474 if (kCFCalendarUnitHour
== smaller
) return true;
475 if (kCFCalendarUnitMinute
== smaller
) return true;
476 if (kCFCalendarUnitSecond
== smaller
) return true;
478 case kCFCalendarUnitHour
:
479 if (kCFCalendarUnitMinute
== smaller
) return true;
480 if (kCFCalendarUnitSecond
== smaller
) return true;
482 case kCFCalendarUnitMinute
:
483 if (kCFCalendarUnitSecond
== smaller
) return true;
485 case kCFCalendarUnitWeek
:
486 case kCFCalendarUnitWeekOfMonth
:
487 case kCFCalendarUnitWeekOfYear
:
488 if (kCFCalendarUnitWeekday
== smaller
) return true;
489 if (kCFCalendarUnitDay
== smaller
) return true;
490 if (kCFCalendarUnitHour
== smaller
) return true;
491 if (kCFCalendarUnitMinute
== smaller
) return true;
492 if (kCFCalendarUnitSecond
== smaller
) return true;
494 case kCFCalendarUnitSecond
:
495 case kCFCalendarUnitWeekday
:
496 case kCFCalendarUnitWeekdayOrdinal
:
502 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) __attribute__((noinline
));
503 static CFRange
__CFCalendarGetRangeOfUnit2(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
504 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange
, calendar
, _rangeOfUnit
:smallerUnit inUnit
:biggerUnit forAT
:at
);
505 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
506 CFRange range
= {kCFNotFound
, kCFNotFound
};
507 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
508 if (calendar
->_cal
) {
509 switch (smallerUnit
) {
510 case kCFCalendarUnitSecond
:
511 switch (biggerUnit
) {
512 case kCFCalendarUnitMinute
:
513 case kCFCalendarUnitHour
:
514 case kCFCalendarUnitDay
:
515 case kCFCalendarUnitWeekday
:
516 case kCFCalendarUnitWeek
:
517 case kCFCalendarUnitMonth
:
518 case kCFCalendarUnitYear
:
519 case kCFCalendarUnitEra
:
526 case kCFCalendarUnitMinute
:
527 switch (biggerUnit
) {
528 case kCFCalendarUnitHour
:
529 case kCFCalendarUnitDay
:
530 case kCFCalendarUnitWeekday
:
531 case kCFCalendarUnitWeek
:
532 case kCFCalendarUnitMonth
:
533 case kCFCalendarUnitYear
:
534 case kCFCalendarUnitEra
:
541 case kCFCalendarUnitHour
:
542 switch (biggerUnit
) {
543 case kCFCalendarUnitDay
:
544 case kCFCalendarUnitWeekday
:
545 case kCFCalendarUnitWeek
:
546 case kCFCalendarUnitMonth
:
547 case kCFCalendarUnitYear
:
548 case kCFCalendarUnitEra
:
555 case kCFCalendarUnitDay
:
556 switch (biggerUnit
) {
557 case kCFCalendarUnitWeek
:
558 case kCFCalendarUnitMonth
:
559 case kCFCalendarUnitYear
:
560 case kCFCalendarUnitEra
:
565 case kCFCalendarUnitWeekday
:
566 switch (biggerUnit
) {
567 case kCFCalendarUnitWeek
:
568 case kCFCalendarUnitMonth
:
569 case kCFCalendarUnitYear
:
570 case kCFCalendarUnitEra
:
575 case kCFCalendarUnitWeekdayOrdinal
:
576 switch (biggerUnit
) {
577 case kCFCalendarUnitMonth
:
578 case kCFCalendarUnitYear
:
579 case kCFCalendarUnitEra
:
584 case kCFCalendarUnitWeek
:
585 switch (biggerUnit
) {
586 case kCFCalendarUnitMonth
:
587 case kCFCalendarUnitYear
:
588 case kCFCalendarUnitEra
:
593 case kCFCalendarUnitMonth
:
594 switch (biggerUnit
) {
595 case kCFCalendarUnitYear
:
596 case kCFCalendarUnitEra
:
601 case kCFCalendarUnitYear
:
602 switch (biggerUnit
) {
603 case kCFCalendarUnitEra
:
608 case kCFCalendarUnitEra
:
615 ucal_clear(calendar
->_cal
);
616 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
617 UCalendarDateFields bigField
= __CFCalendarGetICUFieldCode(biggerUnit
);
618 UCalendarDateFields yearField
= __CFCalendarGetICUFieldCode(kCFCalendarUnitYear
);
619 UCalendarDateFields fieldToAdd
= smallField
;
620 if (kCFCalendarUnitWeekday
== smallerUnit
) {
621 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitDay
);
624 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
625 UErrorCode status
= U_ZERO_ERROR
;
626 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
627 ucal_setMillis(calendar
->_cal
, udate
, &status
);
628 dow
= ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
);
629 fieldToAdd
= __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek
);
631 // Set calendar to first instant of big unit
632 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
633 if (kCFCalendarUnitWeekdayOrdinal
== smallerUnit
) {
634 UErrorCode status
= U_ZERO_ERROR
;
635 // roll day forward to first 'dow'
636 while (ucal_get(calendar
->_cal
, (kCFCalendarUnitMonth
== biggerUnit
) ? UCAL_WEEK_OF_MONTH
: UCAL_WEEK_OF_YEAR
, &status
) != 1) {
637 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
639 while (ucal_get(calendar
->_cal
, UCAL_DAY_OF_WEEK
, &status
) != dow
) {
640 ucal_add(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1, &status
);
643 int32_t minSmallValue
= INT32_MAX
;
644 int32_t maxSmallValue
= INT32_MIN
;
645 UErrorCode status
= U_ZERO_ERROR
;
646 int32_t bigValue
= ucal_get(calendar
->_cal
, bigField
, &status
);
648 int32_t smallValue
= ucal_get(calendar
->_cal
, smallField
, &status
);
649 if (smallValue
< minSmallValue
) minSmallValue
= smallValue
;
650 if (smallValue
> maxSmallValue
) maxSmallValue
= smallValue
;
651 ucal_add(calendar
->_cal
, fieldToAdd
, 1, &status
);
652 if (bigValue
!= ucal_get(calendar
->_cal
, bigField
, &status
)) break;
653 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) break;
654 // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time
656 status
= U_ZERO_ERROR
;
657 range
.location
= minSmallValue
;
658 if (smallerUnit
== kCFCalendarUnitMonth
) range
.location
= 1;
659 range
.length
= maxSmallValue
- minSmallValue
+ 1;
660 if (biggerUnit
== kCFCalendarUnitEra
&& ucal_get(calendar
->_cal
, yearField
, &status
) > 10000) range
.length
= 100000;
665 CFRange
CFCalendarGetRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
666 return __CFCalendarGetRangeOfUnit2(calendar
, smallerUnit
, biggerUnit
, at
);
669 CFIndex
CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar
, CFCalendarUnit smallerUnit
, CFCalendarUnit biggerUnit
, CFAbsoluteTime at
) {
670 CFIndex result
= kCFNotFound
;
671 if (!__validUnits(smallerUnit
, biggerUnit
)) return result
;
672 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex
, calendar
, _ordinalityOfUnit
:smallerUnit inUnit
:biggerUnit forAT
:at
);
673 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
674 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
675 if (calendar
->_cal
) {
676 UErrorCode status
= U_ZERO_ERROR
;
677 ucal_clear(calendar
->_cal
);
678 if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitYear
== biggerUnit
) {
679 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
680 ucal_setMillis(calendar
->_cal
, udate
, &status
);
681 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_YEAR
, &status
);
683 } else if (kCFCalendarUnitWeek
== smallerUnit
&& kCFCalendarUnitMonth
== biggerUnit
) {
684 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
685 ucal_setMillis(calendar
->_cal
, udate
, &status
);
686 int32_t val
= ucal_get(calendar
->_cal
, UCAL_WEEK_OF_MONTH
, &status
);
689 UCalendarDateFields smallField
= __CFCalendarGetICUFieldCode(smallerUnit
);
690 // Set calendar to first instant of big unit
691 __CFCalendarSetToFirstInstant(calendar
, biggerUnit
, at
);
692 UDate curr
= ucal_getMillis(calendar
->_cal
, &status
);
693 UDate goal
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
695 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
696 int multiple
= (1 << multiple_table
[flsl(smallerUnit
) - 1]);
697 Boolean divide
= false, alwaysDivide
= false;
698 while (curr
< goal
) {
699 ucal_add(calendar
->_cal
, smallField
, multiple
, &status
);
700 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
701 if (curr
< newcurr
&& newcurr
<= goal
) {
705 // Either newcurr is going backwards, or not making
706 // progress, or has overshot the goal; reset date
707 // and try smaller multiples.
708 ucal_setMillis(calendar
->_cal
, curr
, &status
);
710 // once we start overshooting the goal, the add at
711 // smaller multiples will succeed at most once for
712 // each multiple, so we reduce it every time through
714 if (goal
< newcurr
) alwaysDivide
= true;
717 multiple
= multiple
/ 2;
718 if (0 == multiple
) break;
719 divide
= alwaysDivide
;
726 Boolean
_CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, int *vector
, int count
) {
727 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
728 if (calendar
->_cal
) {
729 UErrorCode status
= U_ZERO_ERROR
;
730 ucal_clear(calendar
->_cal
);
731 ucal_set(calendar
->_cal
, UCAL_YEAR
, 1);
732 ucal_set(calendar
->_cal
, UCAL_MONTH
, 0);
733 ucal_set(calendar
->_cal
, UCAL_DAY_OF_MONTH
, 1);
734 ucal_set(calendar
->_cal
, UCAL_HOUR_OF_DAY
, 0);
735 ucal_set(calendar
->_cal
, UCAL_MINUTE
, 0);
736 ucal_set(calendar
->_cal
, UCAL_SECOND
, 0);
737 const char *desc
= componentDesc
;
738 Boolean doWOY
= false;
741 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
742 if (UCAL_WEEK_OF_YEAR
== field
) {
748 desc
= componentDesc
;
751 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
753 if (UCAL_YEAR
== field
&& doWOY
) field
= UCAL_YEAR_WOY
;
754 if (UCAL_MONTH
== field
) value
--;
755 ucal_set(calendar
->_cal
, field
, value
);
760 UDate udate
= ucal_getMillis(calendar
->_cal
, &status
);
761 CFAbsoluteTime at
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
763 return U_SUCCESS(status
) ? true : false;
768 Boolean
_CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, int **vector
, int count
) {
769 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
770 if (calendar
->_cal
) {
771 UErrorCode status
= U_ZERO_ERROR
;
772 ucal_clear(calendar
->_cal
);
773 UDate udate
= floor((at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
774 ucal_setMillis(calendar
->_cal
, udate
, &status
);
775 char ch
= *componentDesc
;
777 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
778 int value
= ucal_get(calendar
->_cal
, field
, &status
);
779 if (UCAL_MONTH
== field
) value
++;
785 return U_SUCCESS(status
) ? true : false;
790 Boolean
_CFCalendarAddComponentsV(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, int *vector
, int count
) {
791 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
792 if (calendar
->_cal
) {
793 UErrorCode status
= U_ZERO_ERROR
;
794 ucal_clear(calendar
->_cal
);
795 UDate udate
= floor((*atp
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
796 ucal_setMillis(calendar
->_cal
, udate
, &status
);
797 char ch
= *componentDesc
;
799 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
800 int amount
= *vector
;
801 if (options
& kCFCalendarComponentsWrap
) {
802 ucal_roll(calendar
->_cal
, field
, amount
, &status
);
804 ucal_add(calendar
->_cal
, field
, amount
, &status
);
810 udate
= ucal_getMillis(calendar
->_cal
, &status
);
811 *atp
= (udate
/ 1000.0) - kCFAbsoluteTimeIntervalSince1970
;
812 return U_SUCCESS(status
) ? true : false;
817 Boolean
_CFCalendarGetComponentDifferenceV(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, int **vector
, int count
) {
818 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
819 if (calendar
->_cal
) {
820 UErrorCode status
= U_ZERO_ERROR
;
821 ucal_clear(calendar
->_cal
);
822 UDate curr
= floor((startingAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
823 UDate goal
= floor((resultAT
+ kCFAbsoluteTimeIntervalSince1970
) * 1000.0);
824 ucal_setMillis(calendar
->_cal
, curr
, &status
);
825 int direction
= (startingAT
<= resultAT
) ? 1 : -1;
826 char ch
= *componentDesc
;
828 UCalendarDateFields field
= __CFCalendarGetICUFieldCodeFromChar(ch
);
829 const int multiple_table
[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
830 int multiple
= direction
* (1 << multiple_table
[flsl(__CFCalendarGetCalendarUnitFromChar(ch
)) - 1]);
831 Boolean divide
= false, alwaysDivide
= false;
833 while ((direction
> 0 && curr
< goal
) || (direction
< 0 && goal
< curr
)) {
834 ucal_add(calendar
->_cal
, field
, multiple
, &status
);
835 UDate newcurr
= ucal_getMillis(calendar
->_cal
, &status
);
836 if ((direction
> 0 && curr
< newcurr
&& newcurr
<= goal
) || (direction
< 0 && newcurr
< curr
&& goal
<= newcurr
)) {
840 // Either newcurr is going backwards, or not making
841 // progress, or has overshot the goal; reset date
842 // and try smaller multiples.
843 ucal_setMillis(calendar
->_cal
, curr
, &status
);
845 // once we start overshooting the goal, the add at
846 // smaller multiples will succeed at most once for
847 // each multiple, so we reduce it every time through
849 if ((direction
> 0 && goal
< newcurr
) || (direction
< 0 && newcurr
< goal
)) alwaysDivide
= true;
852 multiple
= multiple
/ 2;
853 if (0 == multiple
) break;
854 divide
= alwaysDivide
;
862 return U_SUCCESS(status
) ? true : false;
867 Boolean
CFCalendarComposeAbsoluteTime(CFCalendarRef calendar
, /* out */ CFAbsoluteTime
*atp
, const char *componentDesc
, ...) {
869 va_start(args
, componentDesc
);
870 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean
, calendar
, _composeAbsoluteTime
:atp
:componentDesc
:args
);
871 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
872 int idx
, cnt
= strlen((char *)componentDesc
);
873 STACK_BUFFER_DECL(int, vector
, cnt
);
874 for (idx
= 0; idx
< cnt
; idx
++) {
875 int arg
= va_arg(args
, int);
879 return _CFCalendarComposeAbsoluteTimeV(calendar
, atp
, componentDesc
, vector
, cnt
);
882 Boolean
CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar
, CFAbsoluteTime at
, const char *componentDesc
, ...) {
884 va_start(args
, componentDesc
);
885 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean
, calendar
, _decomposeAbsoluteTime
:at
:componentDesc
:args
);
886 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
887 int idx
, cnt
= strlen((char *)componentDesc
);
888 STACK_BUFFER_DECL(int *, vector
, cnt
);
889 for (idx
= 0; idx
< cnt
; idx
++) {
890 int *arg
= va_arg(args
, int *);
894 return _CFCalendarDecomposeAbsoluteTimeV(calendar
, at
, componentDesc
, vector
, cnt
);
897 Boolean
CFCalendarAddComponents(CFCalendarRef calendar
, /* inout */ CFAbsoluteTime
*atp
, CFOptionFlags options
, const char *componentDesc
, ...) {
899 va_start(args
, componentDesc
);
900 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean
, calendar
, _addComponents
:atp
:options
:componentDesc
:args
);
901 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
902 int idx
, cnt
= strlen((char *)componentDesc
);
903 STACK_BUFFER_DECL(int, vector
, cnt
);
904 for (idx
= 0; idx
< cnt
; idx
++) {
905 int arg
= va_arg(args
, int);
909 return _CFCalendarAddComponentsV(calendar
, atp
, options
, componentDesc
, vector
, cnt
);
912 Boolean
CFCalendarGetComponentDifference(CFCalendarRef calendar
, CFAbsoluteTime startingAT
, CFAbsoluteTime resultAT
, CFOptionFlags options
, const char *componentDesc
, ...) {
914 va_start(args
, componentDesc
);
915 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean
, calendar
, _diffComponents
:startingAT
:resultAT
:options
:componentDesc
:args
);
916 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
917 int idx
, cnt
= strlen((char *)componentDesc
);
918 STACK_BUFFER_DECL(int *, vector
, cnt
);
919 for (idx
= 0; idx
< cnt
; idx
++) {
920 int *arg
= va_arg(args
, int *);
924 Boolean ret
= _CFCalendarGetComponentDifferenceV(calendar
, startingAT
, resultAT
, options
, componentDesc
, vector
, cnt
);
928 Boolean
CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar
, CFCalendarUnit unit
, CFAbsoluteTime at
, CFAbsoluteTime
*startp
, CFTimeInterval
*tip
) {
929 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean
, calendar
, _rangeOfUnit
:unit startTime
:startp interval
:tip forAT
:at
);
930 __CFGenericValidateType(calendar
, CFCalendarGetTypeID());
931 if (kCFCalendarUnitWeekdayOrdinal
== unit
) return false;
932 if (kCFCalendarUnitWeekday
== unit
) unit
= kCFCalendarUnitDay
;
933 if (!calendar
->_cal
) __CFCalendarSetupCal(calendar
);
934 if (calendar
->_cal
) {
935 ucal_clear(calendar
->_cal
);
936 __CFCalendarSetToFirstInstant(calendar
, unit
, at
);
937 UErrorCode status
= U_ZERO_ERROR
;
938 UDate start
= ucal_getMillis(calendar
->_cal
, &status
);
939 UCalendarDateFields field
= __CFCalendarGetICUFieldCode(unit
);
940 ucal_add(calendar
->_cal
, field
, 1, &status
);
941 UDate end
= ucal_getMillis(calendar
->_cal
, &status
);
942 if (end
== start
&& kCFCalendarUnitEra
== unit
) {
943 // ICU refuses to do the addition, probably because we are
944 // at the limit of UCAL_ERA. Use alternate strategy.
945 CFIndex limit
= ucal_getLimit(calendar
->_cal
, UCAL_YEAR
, UCAL_MAXIMUM
, &status
);
946 if (100000 < limit
) limit
= 100000;
947 ucal_add(calendar
->_cal
, UCAL_YEAR
, limit
, &status
);
948 end
= ucal_getMillis(calendar
->_cal
, &status
);
950 if (U_SUCCESS(status
)) {
951 if (startp
) *startp
= (double)start
/ 1000.0 - kCFAbsoluteTimeIntervalSince1970
;
952 if (tip
) *tip
= (double)(end
- start
) / 1000.0;