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