/ CFDate.c
CFDate.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 /* CFDate.c 25 Copyright (c) 1998-2014, Apple Inc. All rights reserved. 26 Responsibility: Christopher Kane 27 */ 28 29 #include <CoreFoundation/CFDate.h> 30 #include <CoreFoundation/CFTimeZone.h> 31 #include <CoreFoundation/CFDictionary.h> 32 #include <CoreFoundation/CFArray.h> 33 #include <CoreFoundation/CFString.h> 34 #include <CoreFoundation/CFNumber.h> 35 #include "CFInternal.h" 36 #include <math.h> 37 #include <dispatch/dispatch.h> 38 39 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 40 #include <sys/time.h> 41 #endif 42 43 #define DEFINE_CFDATE_FUNCTIONS 1 44 45 /* cjk: The Julian Date for the reference date is 2451910.5, 46 I think, in case that's ever useful. */ 47 48 #if DEFINE_CFDATE_FUNCTIONS 49 50 const CFTimeInterval kCFAbsoluteTimeIntervalSince1970 = 978307200.0L; 51 const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L; 52 53 CF_PRIVATE double __CFTSRRate = 0.0; 54 static double __CF1_TSRRate = 0.0; 55 56 CF_PRIVATE uint64_t __CFTimeIntervalToTSR(CFTimeInterval ti) { 57 if ((ti * __CFTSRRate) > INT64_MAX / 2) return (INT64_MAX / 2); 58 return (uint64_t)(ti * __CFTSRRate); 59 } 60 61 CF_PRIVATE CFTimeInterval __CFTSRToTimeInterval(uint64_t tsr) { 62 return (CFTimeInterval)((double)tsr * __CF1_TSRRate); 63 } 64 65 CF_PRIVATE CFTimeInterval __CFTimeIntervalUntilTSR(uint64_t tsr) { 66 CFDateGetTypeID(); 67 uint64_t now = mach_absolute_time(); 68 if (tsr >= now) { 69 return __CFTSRToTimeInterval(tsr - now); 70 } else { 71 return -__CFTSRToTimeInterval(now - tsr); 72 } 73 } 74 75 // Technically this is 'TSR units' not a strict 'TSR' absolute time 76 CF_PRIVATE uint64_t __CFTSRToNanoseconds(uint64_t tsr) { 77 double tsrInNanoseconds = floor(tsr * __CF1_TSRRate * NSEC_PER_SEC); 78 uint64_t ns = (uint64_t)tsrInNanoseconds; 79 return ns; 80 } 81 82 CF_PRIVATE dispatch_time_t __CFTSRToDispatchTime(uint64_t tsr) { 83 uint64_t tsrInNanoseconds = __CFTSRToNanoseconds(tsr); 84 85 // It's important to clamp this value to INT64_MAX or it will become interpreted by dispatch_time as a relative value instead of absolute time 86 if (tsrInNanoseconds > INT64_MAX - 1) tsrInNanoseconds = INT64_MAX - 1; 87 88 // 2nd argument of dispatch_time is a value in nanoseconds, but tsr does not equal nanoseconds on all platforms. 89 return dispatch_time(1, (int64_t)tsrInNanoseconds); 90 } 91 92 CFAbsoluteTime CFAbsoluteTimeGetCurrent(void) { 93 CFAbsoluteTime ret; 94 struct timeval tv; 95 gettimeofday(&tv, NULL); 96 ret = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970; 97 ret += (1.0E-6 * (CFTimeInterval)tv.tv_usec); 98 return ret; 99 } 100 101 struct __CFDate { 102 CFRuntimeBase _base; 103 CFAbsoluteTime _time; /* immutable */ 104 }; 105 106 static Boolean __CFDateEqual(CFTypeRef cf1, CFTypeRef cf2) { 107 CFDateRef date1 = (CFDateRef)cf1; 108 CFDateRef date2 = (CFDateRef)cf2; 109 if (date1->_time != date2->_time) return false; 110 return true; 111 } 112 113 static CFHashCode __CFDateHash(CFTypeRef cf) { 114 CFDateRef date = (CFDateRef)cf; 115 return (CFHashCode)(float)floor(date->_time); 116 } 117 118 static CFStringRef __CFDateCopyDescription(CFTypeRef cf) { 119 CFDateRef date = (CFDateRef)cf; 120 return CFStringCreateWithFormat(CFGetAllocator(date), NULL, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf, CFGetAllocator(date), date->_time); 121 } 122 123 static CFTypeID __kCFDateTypeID = _kCFRuntimeNotATypeID; 124 125 static const CFRuntimeClass __CFDateClass = { 126 0, 127 "CFDate", 128 NULL, // init 129 NULL, // copy 130 NULL, // dealloc 131 __CFDateEqual, 132 __CFDateHash, 133 NULL, // 134 __CFDateCopyDescription 135 }; 136 137 CFTypeID CFDateGetTypeID(void) { 138 static dispatch_once_t initOnce; 139 dispatch_once(&initOnce, ^{ 140 __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass); 141 142 // HACK: this effectively reserves the type ID for the date class. 143 // this ensures that date objects are treated like ObjC objects and not CF ones. 144 // Apple's newer CF returns a static value for the CFDate type ID and never actually registers a CFDate class. 145 // instead, they just use the ObjC class for everything. 146 if (__kCFDateTypeID != _kCFRuntimeNotATypeID) { 147 _CFRuntimeUnregisterClassWithTypeID(__kCFDateTypeID); 148 } 149 150 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 151 struct mach_timebase_info info; 152 mach_timebase_info(&info); 153 __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom; 154 __CF1_TSRRate = 1.0 / __CFTSRRate; 155 #elif DEPLOYMENT_TARGET_WINDOWS 156 LARGE_INTEGER freq; 157 if (!QueryPerformanceFrequency(&freq)) { 158 HALT; 159 } 160 __CFTSRRate = (double)freq.QuadPart; 161 __CF1_TSRRate = 1.0 / __CFTSRRate; 162 #elif DEPLOYMENT_TARGET_LINUX 163 struct timespec res; 164 if (clock_getres(CLOCK_MONOTONIC, &res) != 0) { 165 HALT; 166 } 167 __CFTSRRate = res.tv_sec + (1000000000 * res.tv_nsec); 168 __CF1_TSRRate = 1.0 / __CFTSRRate; 169 #else 170 #error Unable to initialize date 171 #endif 172 }); 173 return __kCFDateTypeID; 174 } 175 176 #endif 177 178 CF_INLINE int32_t __CFDoubleModToInt(double d, int32_t modulus) { 179 int32_t result = (int32_t)(float)floor(d - floor(d / modulus) * modulus); 180 if (result < 0) result += modulus; 181 return result; 182 } 183 184 CF_INLINE double __CFDoubleMod(double d, int32_t modulus) { 185 double result = d - floor(d / modulus) * modulus; 186 if (result < 0.0) result += (double)modulus; 187 return result; 188 } 189 190 static const uint8_t daysInMonth[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0}; 191 static const uint16_t daysBeforeMonth[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0}; 192 static const uint16_t daysAfterMonth[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0}; 193 194 CF_INLINE bool isleap(int64_t year) { 195 int64_t y = (year + 1) % 400; /* correct to nearest multiple-of-400 year, then find the remainder */ 196 if (y < 0) y = -y; 197 return (0 == (y & 3) && 100 != y && 200 != y && 300 != y); 198 } 199 200 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ 201 CF_INLINE uint8_t __CFDaysInMonth(int8_t month, int64_t year, bool leap) { 202 return daysInMonth[month] + (2 == month && leap); 203 } 204 205 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ 206 CF_INLINE uint16_t __CFDaysBeforeMonth(int8_t month, int64_t year, bool leap) { 207 return daysBeforeMonth[month] + (2 < month && leap); 208 } 209 210 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ 211 CF_INLINE uint16_t __CFDaysAfterMonth(int8_t month, int64_t year, bool leap) { 212 return daysAfterMonth[month] + (month < 2 && leap); 213 } 214 215 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ 216 static void __CFYMDFromAbsolute(int64_t absolute, int64_t *year, int8_t *month, int8_t *day) { 217 int64_t b = absolute / 146097; // take care of as many multiples of 400 years as possible 218 int64_t y = b * 400; 219 uint16_t ydays; 220 absolute -= b * 146097; 221 while (absolute < 0) { 222 y -= 1; 223 absolute += __CFDaysAfterMonth(0, y, isleap(y)); 224 } 225 /* Now absolute is non-negative days to add to year */ 226 ydays = __CFDaysAfterMonth(0, y, isleap(y)); 227 while (ydays <= absolute) { 228 y += 1; 229 absolute -= ydays; 230 ydays = __CFDaysAfterMonth(0, y, isleap(y)); 231 } 232 /* Now we have year and days-into-year */ 233 if (year) *year = y; 234 if (month || day) { 235 int8_t m = absolute / 33 + 1; /* search from the approximation */ 236 bool leap = isleap(y); 237 while (__CFDaysBeforeMonth(m + 1, y, leap) <= absolute) m++; 238 if (month) *month = m; 239 if (day) *day = absolute - __CFDaysBeforeMonth(m, y, leap) + 1; 240 } 241 } 242 243 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ 244 static double __CFAbsoluteFromYMD(int64_t year, int8_t month, int8_t day) { 245 double absolute = 0.0; 246 int64_t idx; 247 int64_t b = year / 400; // take care of as many multiples of 400 years as possible 248 absolute += b * 146097.0; 249 year -= b * 400; 250 if (year < 0) { 251 for (idx = year; idx < 0; idx++) 252 absolute -= __CFDaysAfterMonth(0, idx, isleap(idx)); 253 } else { 254 for (idx = 0; idx < year; idx++) 255 absolute += __CFDaysAfterMonth(0, idx, isleap(idx)); 256 } 257 /* Now add the days into the original year */ 258 absolute += __CFDaysBeforeMonth(month, year, isleap(year)) + day - 1; 259 return absolute; 260 } 261 262 Boolean CFGregorianDateIsValid(CFGregorianDate gdate, CFOptionFlags unitFlags) { 263 if ((unitFlags & kCFGregorianUnitsYears) && (gdate.year <= 0)) return false; 264 if ((unitFlags & kCFGregorianUnitsMonths) && (gdate.month < 1 || 12 < gdate.month)) return false; 265 if ((unitFlags & kCFGregorianUnitsDays) && (gdate.day < 1 || 31 < gdate.day)) return false; 266 if ((unitFlags & kCFGregorianUnitsHours) && (gdate.hour < 0 || 23 < gdate.hour)) return false; 267 if ((unitFlags & kCFGregorianUnitsMinutes) && (gdate.minute < 0 || 59 < gdate.minute)) return false; 268 if ((unitFlags & kCFGregorianUnitsSeconds) && (gdate.second < 0.0 || 60.0 <= gdate.second)) return false; 269 if ((unitFlags & kCFGregorianUnitsDays) && (unitFlags & kCFGregorianUnitsMonths) && (unitFlags & kCFGregorianUnitsYears) && (__CFDaysInMonth(gdate.month, gdate.year - 2001, isleap(gdate.year - 2001)) < gdate.day)) return false; 270 return true; 271 } 272 273 CFAbsoluteTime CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate, CFTimeZoneRef tz) { 274 CFAbsoluteTime at; 275 at = 86400.0 * __CFAbsoluteFromYMD(gdate.year - 2001, gdate.month, gdate.day); 276 at += 3600.0 * gdate.hour + 60.0 * gdate.minute + gdate.second; 277 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 278 if (NULL != tz) { 279 __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); 280 } 281 CFTimeInterval offset0, offset1; 282 if (NULL != tz) { 283 offset0 = CFTimeZoneGetSecondsFromGMT(tz, at); 284 offset1 = CFTimeZoneGetSecondsFromGMT(tz, at - offset0); 285 at -= offset1; 286 } 287 #endif 288 return at; 289 } 290 291 CFGregorianDate CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeZoneRef tz) { 292 CFGregorianDate gdate; 293 int64_t absolute, year; 294 int8_t month, day; 295 CFAbsoluteTime fixedat; 296 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 297 if (NULL != tz) { 298 __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); 299 } 300 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); 301 #else 302 fixedat = at; 303 #endif 304 absolute = (int64_t)floor(fixedat / 86400.0); 305 __CFYMDFromAbsolute(absolute, &year, &month, &day); 306 if (INT32_MAX - 2001 < year) year = INT32_MAX - 2001; 307 gdate.year = year + 2001; 308 gdate.month = month; 309 gdate.day = day; 310 gdate.hour = __CFDoubleModToInt(floor(fixedat / 3600.0), 24); 311 gdate.minute = __CFDoubleModToInt(floor(fixedat / 60.0), 60); 312 gdate.second = __CFDoubleMod(fixedat, 60); 313 if (0.0 == gdate.second) gdate.second = 0.0; // stomp out possible -0.0 314 return gdate; 315 } 316 317 /* Note that the units of years and months are not equal length, but are treated as such. */ 318 CFAbsoluteTime CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at, CFTimeZoneRef tz, CFGregorianUnits units) { 319 CFGregorianDate gdate; 320 CFGregorianUnits working; 321 CFAbsoluteTime candidate_at0, candidate_at1; 322 uint8_t monthdays; 323 324 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 325 if (NULL != tz) { 326 __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); 327 } 328 #endif 329 330 /* Most people seem to expect years, then months, then days, etc. 331 to be added in that order. Thus, 27 April + (4 days, 1 month) 332 = 31 May, and not 1 June. This is also relatively predictable. 333 334 On another issue, months not being equal length, people also 335 seem to expect late day-of-month clamping (don't clamp as you 336 go through months), but clamp before adding in the days. Late 337 clamping is also more predictable given random starting points 338 and random numbers of months added (ie Jan 31 + 2 months could 339 be March 28 or March 29 in different years with aggressive 340 clamping). Proportionality (28 Feb + 1 month = 31 March) is 341 also not expected. 342 343 Also, people don't expect time zone transitions to have any 344 effect when adding years and/or months and/or days, only. 345 Hours, minutes, and seconds, though, are added in as humans 346 would experience the passing of that time. What this means 347 is that if the date, after adding years, months, and days 348 lands on some date, and then adding hours, minutes, and 349 seconds crosses a time zone transition, the time zone 350 transition is accounted for. If adding years, months, and 351 days gets the date into a different time zone offset period, 352 that transition is not taken into account. 353 */ 354 gdate = CFAbsoluteTimeGetGregorianDate(at, tz); 355 /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */ 356 working.years = gdate.year; 357 working.months = gdate.month; 358 working.days = gdate.day; 359 working.years += units.years; 360 working.months += units.months; 361 while (12 < working.months) { 362 working.months -= 12; 363 working.years += 1; 364 } 365 while (working.months < 1) { 366 working.months += 12; 367 working.years -= 1; 368 } 369 monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001)); 370 if (monthdays < working.days) { /* Clamp day to new month */ 371 working.days = monthdays; 372 } 373 working.days += units.days; 374 while (monthdays < working.days) { 375 working.months += 1; 376 if (12 < working.months) { 377 working.months -= 12; 378 working.years += 1; 379 } 380 working.days -= monthdays; 381 monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001)); 382 } 383 while (working.days < 1) { 384 working.months -= 1; 385 if (working.months < 1) { 386 working.months += 12; 387 working.years -= 1; 388 } 389 monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001)); 390 working.days += monthdays; 391 } 392 gdate.year = working.years; 393 gdate.month = working.months; 394 gdate.day = working.days; 395 /* Roll in hours, minutes, and seconds */ 396 candidate_at0 = CFGregorianDateGetAbsoluteTime(gdate, tz); 397 candidate_at1 = candidate_at0 + 3600.0 * units.hours + 60.0 * units.minutes + units.seconds; 398 /* If summing in the hours, minutes, and seconds delta pushes us 399 * into a new time zone offset, that will automatically be taken 400 * care of by the fact that we just add the raw time above. To 401 * undo that effect, we'd have to get the time zone offsets for 402 * candidate_at0 and candidate_at1 here, and subtract the 403 * difference (offset1 - offset0) from candidate_at1. */ 404 return candidate_at1; 405 } 406 407 /* at1 - at2. The only constraint here is that this needs to be the inverse 408 of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint. 409 Unfortunately, due to the nonuniformity of the year and month units, this 410 inversion essentially has to approximate until it finds the answer. */ 411 CFGregorianUnits CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1, CFAbsoluteTime at2, CFTimeZoneRef tz, CFOptionFlags unitFlags) { 412 const int32_t seconds[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60}; 413 CFGregorianUnits units = {0, 0, 0, 0, 0, 0.0}; 414 CFAbsoluteTime atold, atnew = at2; 415 int32_t idx, incr; 416 incr = (at2 < at1) ? 1 : -1; 417 /* Successive approximation: years, then months, then days, then hours, then minutes. */ 418 for (idx = 0; idx < 5; idx++) { 419 if (unitFlags & (1 << idx)) { 420 ((int32_t *)&units)[idx] = -3 * incr + (int32_t)((at1 - atnew) / seconds[idx]); 421 do { 422 atold = atnew; 423 ((int32_t *)&units)[idx] += incr; 424 atnew = CFAbsoluteTimeAddGregorianUnits(at2, tz, units); 425 } while ((1 == incr && atnew <= at1) || (-1 == incr && at1 <= atnew)); 426 ((int32_t *)&units)[idx] -= incr; 427 atnew = atold; 428 } 429 } 430 if (unitFlags & kCFGregorianUnitsSeconds) { 431 units.seconds = at1 - atnew; 432 } 433 if (0.0 == units.seconds) units.seconds = 0.0; // stomp out possible -0.0 434 return units; 435 } 436 437 SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz) { 438 int64_t absolute; 439 CFAbsoluteTime fixedat; 440 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 441 if (NULL != tz) { 442 __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); 443 } 444 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); 445 #else 446 fixedat = at; 447 #endif 448 absolute = (int64_t)floor(fixedat / 86400.0); 449 return (absolute < 0) ? ((absolute + 1) % 7 + 7) : (absolute % 7 + 1); /* Monday = 1, etc. */ 450 } 451 452 SInt32 CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) { 453 CFAbsoluteTime fixedat; 454 int64_t absolute, year; 455 int8_t month, day; 456 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 457 if (NULL != tz) { 458 __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); 459 } 460 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); 461 #else 462 fixedat = at; 463 #endif 464 absolute = (int64_t)floor(fixedat / 86400.0); 465 __CFYMDFromAbsolute(absolute, &year, &month, &day); 466 return __CFDaysBeforeMonth(month, year, isleap(year)) + day; 467 } 468 469 /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */ 470 SInt32 CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) { 471 int64_t absolute, year; 472 int8_t month, day; 473 CFAbsoluteTime fixedat; 474 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 475 if (NULL != tz) { 476 __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); 477 } 478 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); 479 #else 480 fixedat = at; 481 #endif 482 absolute = (int64_t)floor(fixedat / 86400.0); 483 __CFYMDFromAbsolute(absolute, &year, &month, &day); 484 double absolute0101 = __CFAbsoluteFromYMD(year, 1, 1); 485 int64_t dow0101 = __CFDoubleModToInt(absolute0101, 7) + 1; 486 /* First three and last three days of a year can end up in a week of a different year */ 487 if (1 == month && day < 4) { 488 if ((day < 4 && 5 == dow0101) || (day < 3 && 6 == dow0101) || (day < 2 && 7 == dow0101)) { 489 return 53; 490 } 491 } 492 if (12 == month && 28 < day) { 493 double absolute20101 = __CFAbsoluteFromYMD(year + 1, 1, 1); 494 int64_t dow20101 = __CFDoubleModToInt(absolute20101, 7) + 1; 495 if ((28 < day && 4 == dow20101) || (29 < day && 3 == dow20101) || (30 < day && 2 == dow20101)) { 496 return 1; 497 } 498 } 499 /* Days into year, plus a week-shifting correction, divided by 7. First week is 1. */ 500 return (__CFDaysBeforeMonth(month, year, isleap(year)) + day + (dow0101 - 11) % 7 + 2) / 7 + 1; 501 } 502 503