1/* 2 * Copyright (c) 2014 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-2013, 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 38struct __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 47static 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 53static CFHashCode __CFCalendarHash(CFTypeRef cf) { 54 CFCalendarRef calendar = (CFCalendarRef)cf; 55 return CFHash(calendar->_identifier); 56} 57 58static 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 63static 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 72static CFTypeID __kCFCalendarTypeID = _kCFRuntimeNotATypeID; 73 74static 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 86CF_PRIVATE void __CFCalendarInitialize(void) { 87 __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass); 88} 89 90CFTypeID CFCalendarGetTypeID(void) { 91 if (_kCFRuntimeNotATypeID == __kCFCalendarTypeID) __CFCalendarInitialize(); 92 return __kCFCalendarTypeID; 93} 94 95CF_PRIVATE UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) { 96 if (calendarID) { 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); 103 } 104 105 char buffer[BUFFER_SIZE]; 106 const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII); 107 if (NULL == cstr) { 108 if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; 109 } 110 if (NULL == cstr) { 111 if (calendarID) CFRelease(localeID); 112 return NULL; 113 } 114 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); 120 121 UErrorCode status = U_ZERO_ERROR; 122 UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_DEFAULT, &status); 123 if (calendarID) CFRelease(localeID); 124 return cal; 125} 126 127static void __CFCalendarSetupCal(CFCalendarRef calendar) { 128 calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); 129} 130 131static void __CFCalendarZapCal(CFCalendarRef calendar) { 132 ucal_close(calendar->_cal); 133 calendar->_cal = NULL; 134} 135 136CFCalendarRef CFCalendarCopyCurrent(void) { 137 CFLocaleRef locale = CFLocaleCopyCurrent(); 138 CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier); 139 if (calID) { 140 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID); 141 CFCalendarSetLocale(calendar, locale); 142 CFRelease(locale); 143 return calendar; 144 } 145 return NULL; 146} 147 148CFCalendarRef 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; 162 else return NULL; 163 } 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) { 168 return NULL; 169 } 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; 176} 177 178CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) { 179 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFStringRef, calendar, calendarIdentifier); 180 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 181 return calendar->_identifier; 182} 183 184CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) { 185 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFLocaleRef, calendar, _copyLocale); 186 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 187 return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID); 188} 189 190void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) { 191 CF_OBJC_FUNCDISPATCHV(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); 197 CFRetain(localeID); 198 calendar->_localeID = localeID; 199 if (calendar->_cal) __CFCalendarZapCal(calendar); 200 } 201} 202 203CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) { 204 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFTimeZoneRef, calendar_copyTimeZone); 205 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 206 return (CFTimeZoneRef)CFRetain(calendar->_tz); 207} 208 209void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) { 210 CF_OBJC_FUNCDISPATCHV(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); 217 } 218} 219 220CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) { 221 CF_OBJC_FUNCDISPATCHV(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); 226 } 227 return -1; 228} 229 230void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) { 231 CF_OBJC_FUNCDISPATCHV(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); 236 } 237} 238 239CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) { 240 CF_OBJC_FUNCDISPATCHV(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; 244} 245 246void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) { 247 CF_OBJC_FUNCDISPATCHV(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); 251} 252 253CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar) { 254 CF_OBJC_FUNCDISPATCHV(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); 262 } 263 return NULL; 264} 265 266void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date) { 267 CF_OBJC_FUNCDISPATCHV(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; 272 if (!date) { 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); 279 } 280 if (cal) ucal_close(cal); 281 } else { 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); 286 } 287} 288 289 290static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) { 291 switch (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; 305 } 306 return (UCalendarDateFields)-1; 307} 308 309static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) { 310 switch (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; 328 } 329 return (UCalendarDateFields)-1; 330} 331 332static CFCalendarUnit __CFCalendarGetCalendarUnitFromChar(char ch) { 333 switch (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; 346 } 347 return (UCalendarDateFields)-1; 348} 349 350CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { 351 CF_OBJC_FUNCDISPATCHV(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; 363 } 364 return range; 365} 366 367CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { 368 CF_OBJC_FUNCDISPATCHV(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; 380 } 381 return range; 382} 383 384static 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:; 396 { 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); 403 } 404 goto day; 405 } 406 case kCFCalendarUnitEra: 407 { 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)); 410 } 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: 417 day:; 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); 425 } 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); 435 } 436 udate = ucal_getMillis(calendar->_cal, &status); 437 // target date is between bad_udate and udate 438 for (;;) { 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; 443 } else { 444 udate = test_udate; 445 } 446 if (fabs(udate - bad_udate) < 1000) break; 447 } 448 do { 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); 452 } 453} 454 455static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) { 456 switch (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 462 return true; 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; 469 return true; 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; 475 return true; 476 case kCFCalendarUnitDay: 477 if (kCFCalendarUnitHour == smaller) return true; 478 if (kCFCalendarUnitMinute == smaller) return true; 479 if (kCFCalendarUnitSecond == smaller) return true; 480 return false; 481 case kCFCalendarUnitHour: 482 if (kCFCalendarUnitMinute == smaller) return true; 483 if (kCFCalendarUnitSecond == smaller) return true; 484 return false; 485 case kCFCalendarUnitMinute: 486 if (kCFCalendarUnitSecond == smaller) return true; 487 return false; 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; 496 return false; 497 case kCFCalendarUnitSecond: 498 case kCFCalendarUnitWeekday: 499 case kCFCalendarUnitWeekdayOrdinal: 500 return false; 501 } 502 return false; 503}; 504 505static 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_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _rangeOfUnit:smallerUnit inUnit:biggerUnit forAT:at); 509 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 510 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 511 if (calendar->_cal) { 512 int32_t dow = -1; 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); 521 } 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++; 529 } else { 530 range.location = (kCFCalendarUnitHour == smallerUnit || kCFCalendarUnitMinute == smallerUnit || kCFCalendarUnitSecond == smallerUnit) ? 0 : 1; 531 } 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); 539 } else { 540 ucal_add(calendar->_cal, bigField, 1, &status); 541 } 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); 548 } 549 range.location = 1; 550 return range; 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); 554 range.location = 1; 555 return range; 556 } 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); 564 } 565 while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { 566 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); 567 } 568 start = ucal_getMillis(calendar->_cal, &status); 569 goal -= 1000; 570 range.location = 1; // constant here works around ICU -- see 3948293 571 } 572 UDate curr = start; 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; 582 curr = newcurr; 583 } else { 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); 588 divide = true; 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 592 // the loop. 593 if (goal < newcurr) alwaysDivide = true; 594 } 595 if (divide) { 596 multiple = multiple / 2; 597 if (0 == multiple) break; 598 divide = alwaysDivide; 599 } 600 } 601 } 602 return range; 603} 604 605static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline)); 606static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { 607 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, calendar, _rangeOfUnit:smallerUnit inUnit:biggerUnit forAT: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: 623 // goto calculate; 624 range.location = 0; 625 range.length = 60; 626 break; 627 } 628 break; 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: 638 // goto calculate; 639 range.location = 0; 640 range.length = 60; 641 break; 642 } 643 break; 644 case kCFCalendarUnitHour: 645 switch (biggerUnit) { 646 case kCFCalendarUnitDay: 647 case kCFCalendarUnitWeekday: 648 case kCFCalendarUnitWeek: 649 case kCFCalendarUnitMonth: 650 case kCFCalendarUnitYear: 651 case kCFCalendarUnitEra: 652 // goto calculate; 653 range.location = 0; 654 range.length = 24; 655 break; 656 } 657 break; 658 case kCFCalendarUnitDay: 659 switch (biggerUnit) { 660 case kCFCalendarUnitWeek: 661 case kCFCalendarUnitMonth: 662 case kCFCalendarUnitYear: 663 case kCFCalendarUnitEra: 664 goto calculate; 665 break; 666 } 667 break; 668 case kCFCalendarUnitWeekday: 669 switch (biggerUnit) { 670 case kCFCalendarUnitWeek: 671 case kCFCalendarUnitMonth: 672 case kCFCalendarUnitYear: 673 case kCFCalendarUnitEra: 674 goto calculate; 675 break; 676 } 677 break; 678 case kCFCalendarUnitWeekdayOrdinal: 679 switch (biggerUnit) { 680 case kCFCalendarUnitMonth: 681 case kCFCalendarUnitYear: 682 case kCFCalendarUnitEra: 683 goto calculate; 684 break; 685 } 686 break; 687 case kCFCalendarUnitWeek: 688 switch (biggerUnit) { 689 case kCFCalendarUnitMonth: 690 case kCFCalendarUnitYear: 691 case kCFCalendarUnitEra: 692 goto calculate; 693 break; 694 } 695 break; 696 case kCFCalendarUnitMonth: 697 switch (biggerUnit) { 698 case kCFCalendarUnitYear: 699 case kCFCalendarUnitEra: 700 goto calculate; 701 break; 702 } 703 break; 704 case kCFCalendarUnitYear: 705 switch (biggerUnit) { 706 case kCFCalendarUnitEra: 707 goto calculate; 708 break; 709 } 710 break; 711 case kCFCalendarUnitEra: 712 break; 713 } 714 } 715 return range; 716 717 calculate:; 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); 725 } 726 int32_t dow = -1; 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); 733 } 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); 741 } 742 while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { 743 ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); 744 } 745 } 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); 750 for (;;) { 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 758 } 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; 764 765 return range; 766} 767 768CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { 769 return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at); 770} 771 772CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { 773 CFIndex result = kCFNotFound; 774 if (!__validUnits(smallerUnit, biggerUnit)) return result; 775 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, _ordinalityOfUnit:smallerUnit inUnit:biggerUnit forAT:at); 776 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 777 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 778 if (calendar->_cal) { 779 UErrorCode status = U_ZERO_ERROR; 780 ucal_clear(calendar->_cal); 781 if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { 782 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 783 ucal_setMillis(calendar->_cal, udate, &status); 784 int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); 785 return val; 786 } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { 787 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 788 ucal_setMillis(calendar->_cal, udate, &status); 789 int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_MONTH, &status); 790 return val; 791 } 792 UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); 793 // Set calendar to first instant of big unit 794 __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); 795 UDate curr = ucal_getMillis(calendar->_cal, &status); 796 UDate goal = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 797 result = 1; 798 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; 799 int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); 800 Boolean divide = false, alwaysDivide = false; 801 while (curr < goal) { 802 ucal_add(calendar->_cal, smallField, multiple, &status); 803 UDate newcurr = ucal_getMillis(calendar->_cal, &status); 804 if (curr < newcurr && newcurr <= goal) { 805 result += multiple; 806 curr = newcurr; 807 } else { 808 // Either newcurr is going backwards, or not making 809 // progress, or has overshot the goal; reset date 810 // and try smaller multiples. 811 ucal_setMillis(calendar->_cal, curr, &status); 812 divide = true; 813 // once we start overshooting the goal, the add at 814 // smaller multiples will succeed at most once for 815 // each multiple, so we reduce it every time through 816 // the loop. 817 if (goal < newcurr) alwaysDivide = true; 818 } 819 if (divide) { 820 multiple = multiple / 2; 821 if (0 == multiple) break; 822 divide = alwaysDivide; 823 } 824 } 825 } 826 return result; 827} 828 829Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int *vector, int count) { 830 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 831 if (calendar->_cal) { 832 UErrorCode status = U_ZERO_ERROR; 833 ucal_clear(calendar->_cal); 834 ucal_set(calendar->_cal, UCAL_YEAR, 1); 835 ucal_set(calendar->_cal, UCAL_MONTH, 0); 836 ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, 1); 837 ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, 0); 838 ucal_set(calendar->_cal, UCAL_MINUTE, 0); 839 ucal_set(calendar->_cal, UCAL_SECOND, 0); 840 const char *desc = componentDesc; 841 Boolean doWOY = false; 842 char ch = *desc; 843 while (ch) { 844 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 845 if (UCAL_WEEK_OF_YEAR == field) { 846 doWOY = true; 847 } 848 desc++; 849 ch = *desc; 850 } 851 desc = componentDesc; 852 ch = *desc; 853 while (ch) { 854 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 855 int value = *vector; 856 if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY; 857 if (UCAL_MONTH == field) value--; 858 ucal_set(calendar->_cal, field, value); 859 vector++; 860 desc++; 861 ch = *desc; 862 } 863 UDate udate = ucal_getMillis(calendar->_cal, &status); 864 CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; 865 if (atp) *atp = at; 866 return U_SUCCESS(status) ? true : false; 867 } 868 return false; 869} 870 871Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int **vector, int count) { 872 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 873 if (calendar->_cal) { 874 UErrorCode status = U_ZERO_ERROR; 875 ucal_clear(calendar->_cal); 876 UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 877 ucal_setMillis(calendar->_cal, udate, &status); 878 char ch = *componentDesc; 879 while (ch) { 880 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 881 int value = ucal_get(calendar->_cal, field, &status); 882 if (UCAL_MONTH == field) value++; 883 *(*vector) = value; 884 vector++; 885 componentDesc++; 886 ch = *componentDesc; 887 } 888 return U_SUCCESS(status) ? true : false; 889 } 890 return false; 891} 892 893Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int *vector, int count) { 894 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 895 if (calendar->_cal) { 896 UErrorCode status = U_ZERO_ERROR; 897 ucal_clear(calendar->_cal); 898 UDate udate = floor((*atp + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 899 ucal_setMillis(calendar->_cal, udate, &status); 900 char ch = *componentDesc; 901 while (ch) { 902 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 903 int amount = *vector; 904 if (options & kCFCalendarComponentsWrap) { 905 ucal_roll(calendar->_cal, field, amount, &status); 906 } else { 907 ucal_add(calendar->_cal, field, amount, &status); 908 } 909 vector++; 910 componentDesc++; 911 ch = *componentDesc; 912 } 913 udate = ucal_getMillis(calendar->_cal, &status); 914 *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; 915 return U_SUCCESS(status) ? true : false; 916 } 917 return false; 918} 919 920Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int **vector, int count) { 921 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 922 if (calendar->_cal) { 923 UErrorCode status = U_ZERO_ERROR; 924 ucal_clear(calendar->_cal); 925 UDate curr = floor((startingAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 926 UDate goal = floor((resultAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); 927 ucal_setMillis(calendar->_cal, curr, &status); 928 int direction = (startingAT <= resultAT) ? 1 : -1; 929 char ch = *componentDesc; 930 while (ch) { 931 UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); 932 const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; 933 int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]); 934 Boolean divide = false, alwaysDivide = false; 935 int result = 0; 936 while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) { 937 ucal_add(calendar->_cal, field, multiple, &status); 938 UDate newcurr = ucal_getMillis(calendar->_cal, &status); 939 if ((direction > 0 && curr < newcurr && newcurr <= goal) || (direction < 0 && newcurr < curr && goal <= newcurr)) { 940 result += multiple; 941 curr = newcurr; 942 } else { 943 // Either newcurr is going backwards, or not making 944 // progress, or has overshot the goal; reset date 945 // and try smaller multiples. 946 ucal_setMillis(calendar->_cal, curr, &status); 947 divide = true; 948 // once we start overshooting the goal, the add at 949 // smaller multiples will succeed at most once for 950 // each multiple, so we reduce it every time through 951 // the loop. 952 if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true; 953 } 954 if (divide) { 955 multiple = multiple / 2; 956 if (0 == multiple) break; 957 divide = alwaysDivide; 958 } 959 } 960 *(*vector) = result; 961 vector++; 962 componentDesc++; 963 ch = *componentDesc; 964 } 965 return U_SUCCESS(status) ? true : false; 966 } 967 return false; 968} 969 970Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) { 971 va_list args; 972 va_start(args, componentDesc); 973 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _composeAbsoluteTime:atp :componentDesc :args); 974 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 975 int idx, cnt = strlen((char *)componentDesc); 976 STACK_BUFFER_DECL(int, vector, cnt); 977 for (idx = 0; idx < cnt; idx++) { 978 int arg = va_arg(args, int); 979 vector[idx] = arg; 980 } 981 va_end(args); 982 return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt); 983} 984 985Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) { 986 va_list args; 987 va_start(args, componentDesc); 988 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _decomposeAbsoluteTime:at :componentDesc :args); 989 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 990 int idx, cnt = strlen((char *)componentDesc); 991 STACK_BUFFER_DECL(int *, vector, cnt); 992 for (idx = 0; idx < cnt; idx++) { 993 int *arg = va_arg(args, int *); 994 vector[idx] = arg; 995 } 996 va_end(args); 997 return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt); 998} 999 1000Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) { 1001 va_list args; 1002 va_start(args, componentDesc); 1003 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _addComponents:atp :options :componentDesc :args); 1004 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 1005 int idx, cnt = strlen((char *)componentDesc); 1006 STACK_BUFFER_DECL(int, vector, cnt); 1007 for (idx = 0; idx < cnt; idx++) { 1008 int arg = va_arg(args, int); 1009 vector[idx] = arg; 1010 } 1011 va_end(args); 1012 return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt); 1013} 1014 1015Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) { 1016 va_list args; 1017 va_start(args, componentDesc); 1018 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _diffComponents:startingAT :resultAT :options :componentDesc :args); 1019 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 1020 int idx, cnt = strlen((char *)componentDesc); 1021 STACK_BUFFER_DECL(int *, vector, cnt); 1022 for (idx = 0; idx < cnt; idx++) { 1023 int *arg = va_arg(args, int *); 1024 vector[idx] = arg; 1025 } 1026 va_end(args); 1027 Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt); 1028 return ret; 1029} 1030 1031Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) { 1032 CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, calendar, _rangeOfUnit:unit startTime:startp interval:tip forAT:at); 1033 __CFGenericValidateType(calendar, CFCalendarGetTypeID()); 1034 if (kCFCalendarUnitWeekdayOrdinal == unit) return false; 1035 if (kCFCalendarUnitWeekday == unit) unit = kCFCalendarUnitDay; 1036 if (!calendar->_cal) __CFCalendarSetupCal(calendar); 1037 if (calendar->_cal) { 1038 ucal_clear(calendar->_cal); 1039 __CFCalendarSetToFirstInstant(calendar, unit, at); 1040 UErrorCode status = U_ZERO_ERROR; 1041 UDate start = ucal_getMillis(calendar->_cal, &status); 1042 UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); 1043 ucal_add(calendar->_cal, field, 1, &status); 1044 UDate end = ucal_getMillis(calendar->_cal, &status); 1045 if (end == start && kCFCalendarUnitEra == unit) { 1046 // ICU refuses to do the addition, probably because we are 1047 // at the limit of UCAL_ERA. Use alternate strategy. 1048 CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); 1049 if (100000 < limit) limit = 100000; 1050 ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); 1051 end = ucal_getMillis(calendar->_cal, &status); 1052 } 1053 if (U_SUCCESS(status)) { 1054 if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970; 1055 if (tip) *tip = (double)(end - start) / 1000.0; 1056 return true; 1057 } 1058 } 1059 1060 return false; 1061} 1062 1063#undef BUFFER_SIZE 1064 1065