/ CFCalendar.c
CFCalendar.c
1 /* 2 * Copyright (c) 2015 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 /* CFCalendar.c 25 Copyright (c) 2004-2014, Apple Inc. All rights reserved. 26 Responsibility: Christopher Kane 27 */ 28 29 30 #include <CoreFoundation/CFCalendar.h> 31 #include <CoreFoundation/CFRuntime.h> 32 #include "CFInternal.h" 33 #include "CFPriv.h" 34 #include <unicode/ucal.h> 35 36 #define BUFFER_SIZE 512 37 38 struct __CFCalendar { 39 CFRuntimeBase _base; 40 CFStringRef _identifier; // canonical identifier, never NULL 41 CFLocaleRef _locale; 42 CFStringRef _localeID; 43 CFTimeZoneRef _tz; 44 UCalendar *_cal; 45 }; 46 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); 51 } 52 53 static CFHashCode __CFCalendarHash(CFTypeRef cf) { 54 CFCalendarRef calendar = (CFCalendarRef)cf; 55 return CFHash(calendar->_identifier); 56 } 57 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); 61 } 62 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); 70 } 71 72 static CFTypeID __kCFCalendarTypeID = _kCFRuntimeNotATypeID; 73 74 static const CFRuntimeClass __CFCalendarClass = { 75 0, 76 "CFCalendar", 77 NULL, // init 78 NULL, // copy 79 __CFCalendarDeallocate, 80 __CFCalendarEqual, 81 __CFCalendarHash, 82 NULL, // 83 __CFCalendarCopyDescription 84 }; 85 86 CFTypeID CFCalendarGetTypeID(void) { 87 static dispatch_once_t initOnce; 88 dispatch_once(&initOnce, ^{ __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass); }); 89 return __kCFCalendarTypeID; 90 } 91 92 CF_PRIVATE UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) { 93 if (calendarID) { 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); 100 } 101 102 char buffer[BUFFER_SIZE]; 103 const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII); 104 if (NULL == cstr) { 105 if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; 106 } 107 if (NULL == cstr) { 108 if (calendarID) CFRelease(localeID); 109 return NULL; 110 } 111 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); 117 118 UErrorCode status = U_ZERO_ERROR; 119 UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_DEFAULT, &status); 120 if (calendarID) CFRelease(localeID); 121 return cal; 122 } 123 124 static void __CFCalendarSetupCal(CFCalendarRef calendar) { 125 calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); 126 } 127 128 static void __CFCalendarZapCal(CFCalendarRef calendar) { 129 ucal_close(calendar->_cal); 130 calendar->_cal = NULL; 131 } 132 133 CFCalendarRef CFCalendarCopyCurrent(void) { 134 CFLocaleRef locale = CFLocaleCopyCurrent(); 135 CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier); 136 if (calID) { 137 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID); 138 CFCalendarSetLocale(calendar, locale); 139 CFRelease(locale); 140 return calendar; 141 } 142 return NULL; 143 } 144 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; 159 else return NULL; 160 } 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) { 165 return NULL; 166 } 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; 173 } 174 175 CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) { 176 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFStringRef, calendar, calendarIdentifier); 177 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 178 return calendar->_identifier; 179 } 180 181 CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) { 182 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFLocaleRef, calendar, _copyLocale); 183 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 184 return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID); 185 } 186 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); 194 CFRetain(localeID); 195 calendar->_localeID = localeID; 196 if (calendar->_cal) __CFCalendarZapCal(calendar); 197 } 198 } 199 200 CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) { 201 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFTimeZoneRef, calendar, copyTimeZone); 202 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 203 return (CFTimeZoneRef)CFRetain(calendar->_tz); 204 } 205 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); 214 } 215 } 216 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); 223 } 224 return -1; 225 } 226 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); 233 } 234 } 235 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; 241 } 242 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); 248 } 249 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); 259 } 260 return NULL; 261 } 262 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; 269 if (!date) { 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); 276 } 277 if (cal) ucal_close(cal); 278 } else { 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); 283 } 284 } 285 286 287 static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) { 288 switch (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; 302 } 303 return (UCalendarDateFields)-1; 304 } 305 306 static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) { 307 switch (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; 325 } 326 return (UCalendarDateFields)-1; 327 } 328 329 static CFCalendarUnit __CFCalendarGetCalendarUnitFromChar(char ch) { 330 switch (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; 343 } 344 return (UCalendarDateFields)-1; 345 } 346 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; 360 } 361 return range; 362 } 363 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; 377 } 378 return range; 379 } 380 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:; 393 { 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); 400 } 401 goto day; 402 } 403 case kCFCalendarUnitEra: 404 { 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)); 407 } 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: 414 day:; 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); 422 } 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); 432 } 433 udate = ucal_getMillis(calendar->_cal, &status); 434 // target date is between bad_udate and udate 435 for (;;) { 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; 440 } else { 441 udate = test_udate; 442 } 443 if (fabs(udate - bad_udate) < 1000) break; 444 } 445 do { 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); 449 } 450 } 451 452 static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) { 453 switch (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 459 return true; 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; 466 return true; 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; 472 return true; 473 case kCFCalendarUnitDay: 474 if (kCFCalendarUnitHour == smaller) return true; 475 if (kCFCalendarUnitMinute == smaller) return true; 476 if (kCFCalendarUnitSecond == smaller) return true; 477 return false; 478 case kCFCalendarUnitHour: 479 if (kCFCalendarUnitMinute == smaller) return true; 480 if (kCFCalendarUnitSecond == smaller) return true; 481 return false; 482 case kCFCalendarUnitMinute: 483 if (kCFCalendarUnitSecond == smaller) return true; 484 return false; 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; 493 return false; 494 case kCFCalendarUnitSecond: 495 case kCFCalendarUnitWeekday: 496 case kCFCalendarUnitWeekdayOrdinal: 497 return false; 498 } 499 return false; 500 }; 501 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: 520 // goto calculate; 521 range.location = 0; 522 range.length = 60; 523 break; 524 } 525 break; 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: 535 // goto calculate; 536 range.location = 0; 537 range.length = 60; 538 break; 539 } 540 break; 541 case kCFCalendarUnitHour: 542 switch (biggerUnit) { 543 case kCFCalendarUnitDay: 544 case kCFCalendarUnitWeekday: 545 case kCFCalendarUnitWeek: 546 case kCFCalendarUnitMonth: 547 case kCFCalendarUnitYear: 548 case kCFCalendarUnitEra: 549 // goto calculate; 550 range.location = 0; 551 range.length = 24; 552 break; 553 } 554 break; 555 case kCFCalendarUnitDay: 556 switch (biggerUnit) { 557 case kCFCalendarUnitWeek: 558 case kCFCalendarUnitMonth: 559 case kCFCalendarUnitYear: 560 case kCFCalendarUnitEra: 561 goto calculate; 562 break; 563 } 564 break; 565 case kCFCalendarUnitWeekday: 566 switch (biggerUnit) { 567 case kCFCalendarUnitWeek: 568 case kCFCalendarUnitMonth: 569 case kCFCalendarUnitYear: 570 case kCFCalendarUnitEra: 571 goto calculate; 572 break; 573 } 574 break; 575 case kCFCalendarUnitWeekdayOrdinal: 576 switch (biggerUnit) { 577 case kCFCalendarUnitMonth: 578 case kCFCalendarUnitYear: 579 case kCFCalendarUnitEra: 580 goto calculate; 581 break; 582 } 583 break; 584 case kCFCalendarUnitWeek: 585 switch (biggerUnit) { 586 case kCFCalendarUnitMonth: 587 case kCFCalendarUnitYear: 588 case kCFCalendarUnitEra: 589 goto calculate; 590 break; 591 } 592 break; 593 case kCFCalendarUnitMonth: 594 switch (biggerUnit) { 595 case kCFCalendarUnitYear: 596 case kCFCalendarUnitEra: 597 goto calculate; 598 break; 599 } 600 break; 601 case kCFCalendarUnitYear: 602 switch (biggerUnit) { 603 case kCFCalendarUnitEra: 604 goto calculate; 605 break; 606 } 607 break; 608 case kCFCalendarUnitEra: 609 break; 610 } 611 } 612 return range; 613 614 calculate:; 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); 622 } 623 int32_t dow = -1; 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); 630 } 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); 638 } 639 while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { 640 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); 641 } 642 } 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); 647 for (;;) { 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 655 } 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; 661 662 return range; 663 } 664 665 CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { 666 return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at); 667 } 668 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); 682 return val; 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); 687 return val; 688 } 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); 694 result = 1; 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) { 702 result += multiple; 703 curr = newcurr; 704 } else { 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); 709 divide = true; 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 713 // the loop. 714 if (goal < newcurr) alwaysDivide = true; 715 } 716 if (divide) { 717 multiple = multiple / 2; 718 if (0 == multiple) break; 719 divide = alwaysDivide; 720 } 721 } 722 } 723 return result; 724 } 725 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; 739 char ch = *desc; 740 while (ch) { 741 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 742 if (UCAL_WEEK_OF_YEAR == field) { 743 doWOY = true; 744 } 745 desc++; 746 ch = *desc; 747 } 748 desc = componentDesc; 749 ch = *desc; 750 while (ch) { 751 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 752 int value = *vector; 753 if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY; 754 if (UCAL_MONTH == field) value--; 755 ucal_set(calendar->_cal, field, value); 756 vector++; 757 desc++; 758 ch = *desc; 759 } 760 UDate udate = ucal_getMillis(calendar->_cal, &status); 761 CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; 762 if (atp) *atp = at; 763 return U_SUCCESS(status) ? true : false; 764 } 765 return false; 766 } 767 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; 776 while (ch) { 777 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 778 int value = ucal_get(calendar->_cal, field, &status); 779 if (UCAL_MONTH == field) value++; 780 *(*vector) = value; 781 vector++; 782 componentDesc++; 783 ch = *componentDesc; 784 } 785 return U_SUCCESS(status) ? true : false; 786 } 787 return false; 788 } 789 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; 798 while (ch) { 799 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 800 int amount = *vector; 801 if (options & kCFCalendarComponentsWrap) { 802 ucal_roll(calendar->_cal, field, amount, &status); 803 } else { 804 ucal_add(calendar->_cal, field, amount, &status); 805 } 806 vector++; 807 componentDesc++; 808 ch = *componentDesc; 809 } 810 udate = ucal_getMillis(calendar->_cal, &status); 811 *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; 812 return U_SUCCESS(status) ? true : false; 813 } 814 return false; 815 } 816 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; 827 while (ch) { 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; 832 int result = 0; 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)) { 837 result += multiple; 838 curr = newcurr; 839 } else { 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); 844 divide = true; 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 848 // the loop. 849 if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true; 850 } 851 if (divide) { 852 multiple = multiple / 2; 853 if (0 == multiple) break; 854 divide = alwaysDivide; 855 } 856 } 857 *(*vector) = result; 858 vector++; 859 componentDesc++; 860 ch = *componentDesc; 861 } 862 return U_SUCCESS(status) ? true : false; 863 } 864 return false; 865 } 866 867 Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) { 868 va_list args; 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); 876 vector[idx] = arg; 877 } 878 va_end(args); 879 return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt); 880 } 881 882 Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) { 883 va_list args; 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 *); 891 vector[idx] = arg; 892 } 893 va_end(args); 894 return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt); 895 } 896 897 Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) { 898 va_list args; 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); 906 vector[idx] = arg; 907 } 908 va_end(args); 909 return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt); 910 } 911 912 Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) { 913 va_list args; 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 *); 921 vector[idx] = arg; 922 } 923 va_end(args); 924 Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt); 925 return ret; 926 } 927 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); 949 } 950 if (U_SUCCESS(status)) { 951 if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970; 952 if (tip) *tip = (double)(end - start) / 1000.0; 953 return true; 954 } 955 } 956 957 return false; 958 } 959 960 #undef BUFFER_SIZE 961