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