CFDateComponents.c
1 /* CFDateComponents.c 2 Copyright (c) 2004-2019, Apple Inc. and the Swift project authors 3 4 Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors 5 Licensed under Apache License v2.0 with Runtime Library Exception 6 See http://swift.org/LICENSE.txt for license information 7 See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 Responsibility: Itai Ferber 9 */ 10 11 #include <assert.h> 12 #include <CoreFoundation/CFCalendar.h> 13 #include <CoreFoundation/CFString.h> 14 #include "CFDateComponents.h" 15 #include "CFInternal.h" 16 #include "CFCalendar_Internal.h" 17 #include "CFRuntime_Internal.h" 18 19 static Boolean __CFDateComponentsEqual(CFTypeRef cf1, CFTypeRef cf2) { 20 assert(NULL != cf1); 21 assert(NULL != cf2); 22 CFDateComponentsRef dc1 = (CFDateComponentsRef)cf1; 23 CFDateComponentsRef dc2 = (CFDateComponentsRef)cf2; 24 if (dc1->_era != dc2->_era) return false; 25 if (dc1->_year != dc2->_year) return false; 26 if (dc1->_quarter != dc2->_quarter) return false; 27 if (dc1->_month != dc2->_month) return false; 28 if (dc1->_day != dc2->_day) return false; 29 if (dc1->_hour != dc2->_hour) return false; 30 if (dc1->_minute != dc2->_minute) return false; 31 if (dc1->_second != dc2->_second) return false; 32 if (dc1->_nanosecond != dc2->_nanosecond) return false; 33 if (dc1->_week != dc2->_week) return false; 34 if (dc1->_weekOfYear != dc2->_weekOfYear) return false; 35 if (dc1->_weekOfMonth != dc2->_weekOfMonth) return false; 36 if (dc1->_yearForWeekOfYear != dc2->_yearForWeekOfYear) return false; 37 if (dc1->_weekday != dc2->_weekday) return false; 38 if (dc1->_weekdayOrdinal != dc2->_weekdayOrdinal) return false; 39 // TODO: NSDateComponents would compare leapMonth, not checking isLeapMonthSet first. 'isLeapMonth' returns NO in the case where 'isLeapMonthSet' returns NO. 40 // This also manifested as a bug where setting leapMonth -> NO meant that the 'isLeapMonthSet' was false after decoding the archive, because the value was only set on the decoded NSDateComponents if 'leapMonth' was YES. 41 // For now, we will use the same logic as before, and look into seeing if we can change that behavior for encoding later. 42 if (!((dc1->_leapMonth == 0 && dc2->_leapMonth == CFDateComponentUndefined) || 43 (dc1->_leapMonth == CFDateComponentUndefined && dc2->_leapMonth == 0) || 44 (dc1->_leapMonth == dc2->_leapMonth))) { 45 return false; 46 } 47 if ((dc1->_calendar && !dc2->_calendar) || (!dc1->_calendar && dc2->_calendar)) return false; 48 if (dc1->_calendar && dc2->_calendar && !CFEqual(dc1->_calendar, dc2->_calendar)) return false; 49 if ((dc1->_timeZone && !dc2->_timeZone) || (!dc1->_timeZone && dc2->_timeZone)) return false; 50 if (dc1->_timeZone && dc2->_timeZone && !CFEqual(dc1->_timeZone, dc2->_timeZone)) return false; 51 return true; 52 } 53 54 static CFHashCode __CFDateComponentsHash(CFTypeRef cf) { 55 assert(NULL != cf); 56 CFDateComponentsRef dc = (CFDateComponentsRef)cf; 57 CFIndex calHash = dc->_calendar ? CFHash(dc->_calendar) : 0; 58 CFIndex tzHash = dc->_timeZone ? CFHash(dc->_timeZone) : 0; 59 CFIndex calTzHash = calHash ^ tzHash; 60 CFIndex y = dc->_year; if (y == CFDateComponentUndefined) y = 0; 61 CFIndex m = dc->_month; if (m == CFDateComponentUndefined) m = 0; 62 CFIndex d = dc->_day; if (d == CFDateComponentUndefined) d = 0; 63 CFIndex h = dc->_hour; if (h == CFDateComponentUndefined) h = 0; 64 CFIndex mm = dc->_minute; if (mm == CFDateComponentUndefined) mm = 0; 65 CFIndex s = dc->_second; if (s == CFDateComponentUndefined) s = 0; 66 CFIndex yy = dc->_yearForWeekOfYear; if (yy == CFDateComponentUndefined) yy = 0; 67 CFIndex hash = calTzHash + (32832013 * (y + yy) + 2678437 * m + 86413 * d + 3607 * h + 61 * mm + s); 68 hash = hash + (41 * dc->_weekOfYear + 11 * dc->_weekOfMonth + 7 * dc->_weekday + 3 * dc->_weekdayOrdinal + dc->_quarter) * (1ULL << 5); 69 return hash; 70 } 71 72 Boolean CFDateComponentsIsLeapMonthSet(CFDateComponentsRef dc) { 73 return dc->_leapMonth != CFDateComponentUndefined; 74 } 75 76 Boolean CFDateComponentsIsLeapMonth(CFDateComponentsRef dc) { 77 return (CFDateComponentUndefined != dc->_leapMonth && dc->_leapMonth) ? true : false; 78 } 79 80 CFStringRef _CFDateComponentsCopyDescriptionInner(CFDateComponentsRef dc) { 81 CFMutableStringRef mstr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); 82 CFStringAppend(mstr, CFSTR("{")); 83 CFCalendarRef cal = dc->_calendar; 84 if (cal) CFStringAppendFormat(mstr, NULL, CFSTR("\n Calendar: %@"), cal); 85 CFTimeZoneRef tz = dc->_timeZone; 86 if (tz) CFStringAppendFormat(mstr, NULL, CFSTR("\n TimeZone: %@"), tz); 87 CFIndex val = dc->_era; 88 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Era: %ld"), (long)val); 89 val = dc->_year; 90 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Calendar Year: %ld"), (long)val); 91 val = dc->_month; 92 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Month: %ld"), (long)val); 93 val = dc->_leapMonth; 94 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Leap Month: %ld"), (long)val); 95 val = dc->_day; 96 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Day: %ld"), (long)val); 97 val = dc->_hour; 98 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Hour: %ld"), (long)val); 99 val = dc->_minute; 100 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Minute: %ld"), (long)val); 101 val = dc->_second; 102 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Second: %ld"), (long)val); 103 val = dc->_nanosecond; 104 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Nanosecond: %ld"), (long)val); 105 val = dc->_quarter; 106 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Quarter: %ld"), (long)val); 107 val = dc->_yearForWeekOfYear; 108 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Year for Week of Year: %ld"), (long)val); 109 val = dc->_weekOfYear; 110 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Week of Year: %ld"), (long)val); 111 val = dc->_weekOfMonth; 112 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Week of Month: %ld"), (long)val); 113 #pragma GCC diagnostic push 114 #pragma GCC diagnostic ignored "-Wdeprecated" 115 val = dc->_week; 116 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Week (obsolete): %ld"), (long)val); 117 #pragma GCC diagnostic pop 118 val = dc->_weekday; 119 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Weekday: %ld"), (long)val); 120 val = dc->_weekdayOrdinal; 121 if (CFDateComponentUndefined != val) CFStringAppendFormat(mstr, NULL, CFSTR("\n Weekday Ordinal: %ld"), (long)val); 122 return mstr; 123 } 124 125 static CFStringRef __CFDateComponentsCopyDescription(CFTypeRef cf) { 126 assert(NULL != cf); 127 CFDateComponentsRef dc = (CFDateComponentsRef)cf; 128 CFStringRef interiorDescription = _CFDateComponentsCopyDescriptionInner(dc); 129 CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFDateComponents %p [%p]>%@"), dc, CFGetAllocator(dc), interiorDescription); 130 CFRelease(interiorDescription); 131 return result; 132 } 133 134 static void __CFDateComponentsDeallocate(CFTypeRef cf) { 135 assert(NULL != cf); 136 CFDateComponentsRef dc = (CFDateComponentsRef)cf; 137 if (dc->_calendar) CFRelease(dc->_calendar); 138 if (dc->_timeZone) CFRelease(dc->_timeZone); 139 } 140 141 const CFRuntimeClass __CFDateComponentsClass = { 142 0, 143 "CFDateComponents", 144 NULL, // init 145 NULL, // copy 146 __CFDateComponentsDeallocate, 147 __CFDateComponentsEqual, 148 __CFDateComponentsHash, 149 NULL, // 150 __CFDateComponentsCopyDescription 151 }; 152 153 CFTypeID CFDateComponentsGetTypeID(void) { 154 return _kCFRuntimeIDCFDateComponents; 155 } 156 /* End Runtime setup */ 157 158 CFDateComponentsRef CFDateComponentsCreate(CFAllocatorRef allocator) { 159 if (!allocator) allocator = CFAllocatorGetDefault(); 160 struct __CFDateComponents *dc = NULL; 161 uint32_t size = sizeof(struct __CFDateComponents) - sizeof(CFRuntimeBase); 162 dc = (struct __CFDateComponents *)_CFRuntimeCreateInstance(allocator, CFDateComponentsGetTypeID(), size, NULL); 163 if (NULL == dc) return NULL; 164 dc->_calendar = NULL; 165 dc->_timeZone = NULL; 166 dc->_era = CFDateComponentUndefined; 167 dc->_year = CFDateComponentUndefined; 168 dc->_month = CFDateComponentUndefined; 169 dc->_leapMonth = CFDateComponentUndefined; 170 dc->_day = CFDateComponentUndefined; 171 dc->_hour = CFDateComponentUndefined; 172 dc->_minute = CFDateComponentUndefined; 173 dc->_second = CFDateComponentUndefined; 174 dc->_week = CFDateComponentUndefined; 175 dc->_weekday = CFDateComponentUndefined; 176 dc->_weekdayOrdinal = CFDateComponentUndefined; 177 dc->_quarter = CFDateComponentUndefined; 178 dc->_weekOfMonth = CFDateComponentUndefined; 179 dc->_weekOfYear = CFDateComponentUndefined; 180 dc->_yearForWeekOfYear = CFDateComponentUndefined; 181 dc->_nanosecond = CFDateComponentUndefined; 182 return dc; 183 } 184 185 CFDateComponentsRef CFDateComponentsCreateCopy(CFAllocatorRef allocator, CFDateComponentsRef dc) { 186 CFDateComponentsRef result = CFDateComponentsCreate(allocator); 187 if (!result) HALT_MSG("Out of memory"); 188 189 CFCalendarRef cal = CFDateComponentsCopyCalendar(dc); 190 if (cal) { 191 CFDateComponentsSetCalendar(result, cal); 192 CFRelease(cal); 193 } 194 CFTimeZoneRef tz = CFDateComponentsCopyTimeZone(dc); 195 if (tz) { 196 CFDateComponentsSetTimeZone(result, tz); 197 CFRelease(tz); 198 } 199 200 // Just reach in for the rest 201 result->_era = dc->_era; 202 result->_year = dc->_year; 203 result->_month = dc->_month; 204 result->_leapMonth = dc->_leapMonth; 205 result->_day = dc->_day; 206 result->_hour = dc->_hour; 207 result->_minute = dc->_minute; 208 result->_second = dc->_second; 209 result->_week = dc->_week; 210 result->_weekday = dc->_weekday; 211 result->_weekdayOrdinal = dc->_weekdayOrdinal; 212 result->_quarter = dc->_quarter; 213 result->_weekOfMonth = dc->_weekOfMonth; 214 result->_weekOfYear = dc->_weekOfYear; 215 result->_yearForWeekOfYear = dc->_yearForWeekOfYear; 216 result->_nanosecond = dc->_nanosecond; 217 218 return result; 219 } 220 221 CFIndex CFDateComponentsGetValue(CFDateComponentsRef dateComp, CFCalendarUnit unit) { 222 assert(NULL != dateComp); 223 CFIndex val = CFDateComponentUndefined; 224 switch (unit) { 225 case kCFCalendarUnitEra: 226 val = dateComp->_era; 227 break; 228 case kCFCalendarUnitYear: 229 val = dateComp->_year; 230 break; 231 case kCFCalendarUnitMonth: 232 val = dateComp->_month; 233 break; 234 case kCFCalendarUnitLeapMonth: 235 val = dateComp->_leapMonth; 236 break; 237 case kCFCalendarUnitDay: 238 val = dateComp->_day; 239 break; 240 case kCFCalendarUnitHour: 241 val = dateComp->_hour; 242 break; 243 case kCFCalendarUnitMinute: 244 val = dateComp->_minute; 245 break; 246 case kCFCalendarUnitSecond: 247 val = dateComp->_second; 248 break; 249 #pragma GCC diagnostic push 250 #pragma GCC diagnostic ignored "-Wdeprecated" 251 case kCFCalendarUnitWeek: 252 val = dateComp->_week; 253 break; 254 #pragma GCC diagnostic pop 255 case kCFCalendarUnitWeekday: 256 val = dateComp->_weekday; 257 break; 258 case kCFCalendarUnitWeekdayOrdinal: 259 val = dateComp->_weekdayOrdinal; 260 break; 261 case kCFCalendarUnitQuarter: 262 val = dateComp->_quarter; 263 break; 264 case kCFCalendarUnitWeekOfMonth: 265 val = dateComp->_weekOfMonth; 266 break; 267 case kCFCalendarUnitWeekOfYear: 268 val = dateComp->_weekOfYear; 269 break; 270 case kCFCalendarUnitYearForWeekOfYear: 271 val = dateComp->_yearForWeekOfYear; 272 break; 273 case kCFCalendarUnitNanosecond: 274 val = dateComp->_nanosecond; 275 break; 276 default: 277 // Unknown units are ignored for forwards compatibility 278 break; 279 } 280 return val; 281 } 282 283 void CFDateComponentsSetValue(CFDateComponentsRef dateComp, CFCalendarUnit unit, CFIndex value) { 284 assert(NULL != dateComp); 285 switch (unit) { 286 case kCFCalendarUnitEra: 287 dateComp->_era = value; 288 break; 289 case kCFCalendarUnitYear: 290 dateComp->_year = value; 291 break; 292 case kCFCalendarUnitMonth: 293 dateComp->_month = value; 294 break; 295 case kCFCalendarUnitLeapMonth: 296 dateComp->_leapMonth = value; 297 break; 298 case kCFCalendarUnitDay: 299 dateComp->_day = value; 300 break; 301 case kCFCalendarUnitHour: 302 dateComp->_hour = value; 303 break; 304 case kCFCalendarUnitMinute: 305 dateComp->_minute = value; 306 break; 307 case kCFCalendarUnitSecond: 308 dateComp->_second = value; 309 break; 310 #pragma GCC diagnostic push 311 #pragma GCC diagnostic ignored "-Wdeprecated" 312 case kCFCalendarUnitWeek: 313 dateComp->_week = value; 314 break; 315 #pragma GCC diagnostic pop 316 case kCFCalendarUnitWeekday: 317 dateComp->_weekday = value; 318 break; 319 case kCFCalendarUnitWeekdayOrdinal: 320 dateComp->_weekdayOrdinal = value; 321 break; 322 case kCFCalendarUnitQuarter: 323 dateComp->_quarter = value; 324 break; 325 case kCFCalendarUnitWeekOfMonth: 326 dateComp->_weekOfMonth = value; 327 break; 328 case kCFCalendarUnitWeekOfYear: 329 dateComp->_weekOfYear = value; 330 break; 331 case kCFCalendarUnitYearForWeekOfYear: 332 dateComp->_yearForWeekOfYear = value; 333 break; 334 case kCFCalendarUnitNanosecond: 335 dateComp->_nanosecond = value; 336 break; 337 default: 338 // Unknown units are ignored for forwards compatibility 339 break; 340 } 341 } 342 343 CFCalendarRef CFDateComponentsCopyCalendar(CFDateComponentsRef dateComp) { 344 assert(NULL != dateComp); 345 if (dateComp->_calendar) { 346 return (CFCalendarRef)CFRetain(dateComp->_calendar); 347 } else { 348 return NULL; 349 } 350 } 351 352 void CFDateComponentsSetCalendar(CFDateComponentsRef dateComp, CFCalendarRef calendar) { 353 assert(NULL != dateComp); 354 CFCalendarRef currCal = dateComp->_calendar; 355 if (calendar && currCal) { 356 if (CFEqual(currCal, calendar)) return; 357 } 358 if (currCal) { 359 CFRelease(dateComp->_calendar); 360 dateComp->_calendar = NULL; 361 } 362 if (calendar) { 363 // We copy the calendar, and set its time zone to match the one in the current date components if required 364 CFCalendarRef calCopy = _CFCalendarCreateCopy(kCFAllocatorSystemDefault, calendar); 365 366 if (dateComp->_timeZone) { 367 CFCalendarSetTimeZone(calCopy, dateComp->_timeZone); 368 } 369 dateComp->_calendar = calCopy; 370 } 371 } 372 373 CFTimeZoneRef CFDateComponentsCopyTimeZone(CFDateComponentsRef dateComp) { 374 assert(NULL != dateComp); 375 if (dateComp->_timeZone) { 376 return (CFTimeZoneRef)CFRetain(dateComp->_timeZone); 377 } else { 378 return NULL; 379 } 380 } 381 382 void CFDateComponentsSetTimeZone(CFDateComponentsRef dateComp, CFTimeZoneRef timeZone) { 383 assert(NULL != dateComp); 384 CFTimeZoneRef currTZ = dateComp->_timeZone; 385 if (timeZone && currTZ) { 386 if (CFEqual(currTZ, timeZone)) return; 387 } 388 if (currTZ) { 389 CFRelease(dateComp->_timeZone); 390 dateComp->_timeZone = NULL; 391 } 392 if (timeZone) { 393 dateComp->_timeZone = (CFTimeZoneRef)CFRetain(timeZone); 394 // Also set the tz on our calendar 395 CFCalendarRef cal = dateComp->_calendar; 396 if (cal) { 397 CFCalendarSetTimeZone(cal, timeZone); 398 } 399 } 400 } 401 402 Boolean CFDateComponentsIsValidDate(CFDateComponentsRef dateComp) { 403 CFCalendarRef cal = dateComp->_calendar; 404 if (!cal) { 405 return false; 406 } else { 407 return CFDateComponentsIsValidDateInCalendar(dateComp, cal); 408 } 409 } 410 411 Boolean CFDateComponentsIsValidDateInCalendar(CFDateComponentsRef dateComp, CFCalendarRef inCalendar) { 412 assert(NULL != dateComp); 413 assert(NULL != inCalendar); 414 415 CFIndex ns = dateComp->_nanosecond; 416 if (CFDateComponentUndefined != ns && 1000 * 1000 * 1000UL <= ns) { 417 return false; 418 } 419 420 CFCalendarRef calendar = _CFCalendarCreateCopy(kCFAllocatorSystemDefault, inCalendar); 421 422 // Clear nanoseconds temporarily 423 if (CFDateComponentUndefined != ns && 0 < ns) { 424 dateComp->_nanosecond = 0; 425 } 426 427 CFDateRef date = CFCalendarCreateDateFromComponents(kCFAllocatorSystemDefault, calendar, dateComp); 428 429 // Reset nanoseconds 430 if (CFDateComponentUndefined != ns && 0 < ns) { 431 dateComp->_nanosecond = ns; 432 } 433 434 Boolean result = true; 435 if (date) { 436 CFCalendarUnit all = kCFCalendarUnitEra | kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay | kCFCalendarUnitHour | kCFCalendarUnitMinute | kCFCalendarUnitSecond | kCFCalendarUnitWeekday | kCFCalendarUnitWeekdayOrdinal | kCFCalendarUnitQuarter | kCFCalendarUnitWeekOfMonth | kCFCalendarUnitWeekOfYear | kCFCalendarUnitYearForWeekOfYear; 437 438 CFDateComponentsRef newComps = CFCalendarCreateDateComponentsFromDate(kCFAllocatorSystemDefault, calendar, all, date); 439 440 if (result && CFDateComponentUndefined != dateComp->_era) if (newComps->_era != dateComp->_era) result = false; 441 if (result && CFDateComponentUndefined != dateComp->_year) if (newComps->_year != dateComp->_year) result = false; 442 if (result && CFDateComponentUndefined != dateComp->_month) if (newComps->_month != dateComp->_month) result = false; 443 if (result && CFDateComponentUndefined != dateComp->_leapMonth) if (newComps->_leapMonth != dateComp->_leapMonth) result = false; 444 if (result && CFDateComponentUndefined != dateComp->_day) if (newComps->_day != dateComp->_day) result = false; 445 if (result && CFDateComponentUndefined != dateComp->_hour) if (newComps->_hour != dateComp->_hour) result = false; 446 if (result && CFDateComponentUndefined != dateComp->_minute) if (newComps->_minute != dateComp->_minute) result = false; 447 if (result && CFDateComponentUndefined != dateComp->_second) if (newComps->_second != dateComp->_second) result = false; 448 if (result && CFDateComponentUndefined != dateComp->_weekday) if (newComps->_weekday != dateComp->_weekday) result = false; 449 if (result && CFDateComponentUndefined != dateComp->_weekdayOrdinal) if (newComps->_weekdayOrdinal != dateComp->_weekdayOrdinal) result = false; 450 if (result && CFDateComponentUndefined != dateComp->_quarter) if (newComps->_quarter != dateComp->_quarter) result = false; 451 if (result && CFDateComponentUndefined != dateComp->_weekOfMonth) if (newComps->_weekOfMonth != dateComp->_weekOfMonth) result = false; 452 if (result && CFDateComponentUndefined != dateComp->_weekOfYear) if (newComps->_weekOfYear != dateComp->_weekOfYear) result = false; 453 if (result && CFDateComponentUndefined != dateComp->_yearForWeekOfYear) if (newComps->_yearForWeekOfYear != dateComp->_yearForWeekOfYear) result = false; 454 CFRelease(date); 455 CFRelease(newComps); 456 } 457 458 CFRelease(calendar); 459 460 // NSDateComponents has a legacy behavior here to support an unset calendar that means if date is not set we return true 461 return result; 462 } 463 464 Boolean CFDateComponentsDateMatchesComponents(CFDateComponentsRef dateComp, CFCalendarRef calendar, CFDateRef date) { 465 // TODO 466 return false; 467 }