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