]> git.saurik.com Git - apple/cf.git/blob - NumberDate.subproj/CFDate.c
CF-299.tar.gz
[apple/cf.git] / NumberDate.subproj / CFDate.c
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /* CFDate.c
26 Copyright 1998-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
28 */
29
30 #include <CoreFoundation/CFDate.h>
31 #include <CoreFoundation/CFTimeZone.h>
32 #include <CoreFoundation/CFDictionary.h>
33 #include <CoreFoundation/CFArray.h>
34 #include <CoreFoundation/CFString.h>
35 #include <CoreFoundation/CFNumber.h>
36 #include "CFInternal.h"
37 #include <math.h>
38 #if defined(__MACH__) || defined(__LINUX__)
39 #include <sys/time.h>
40 #endif
41 #if defined(__WIN32__)
42 #include <windows.h>
43 #endif
44
45 const CFTimeInterval kCFAbsoluteTimeIntervalSince1970 = 978307200.0L;
46 const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L;
47
48 /* cjk: The Julian Date for the reference date is 2451910.5,
49 I think, in case that's ever useful. */
50
51 struct __CFDate {
52 CFRuntimeBase _base;
53 CFAbsoluteTime _time; /* immutable */
54 };
55
56 #if defined(__MACH__)
57 static double __CFTSRRate = 0.0;
58 static CFAbsoluteTime __CFBootAbsoluteTime = 0.0;
59 static CFTimeInterval __CFLastSyncOffset = -1.0E+20;
60
61 __private_extern__ int64_t __CFTimeIntervalToTSR(CFTimeInterval ti) {
62 return (int64_t)(ti * __CFTSRRate);
63 }
64
65 __private_extern__ CFTimeInterval __CFTSRToTimeInterval(int64_t tsr) {
66 return (CFTimeInterval)((double)tsr / __CFTSRRate);
67 }
68
69 __private_extern__ int64_t __CFAbsoluteTimeToTSR(CFAbsoluteTime at) {
70 CFTimeInterval delta = __CFReadTSR() / __CFTSRRate;
71 if (__CFLastSyncOffset + 0.01 < delta) {
72 struct timeval tv;
73 /* 0.01 seconds is arbitrarily chosen, but keeps the error under
74 0.00001 seconds generally; we need a number which is large enough
75 to cut down on the number of gettimeofday() calls, but small
76 enough that radical changes to the calendar clock are noticed
77 reasonably quickly. */
78 gettimeofday(&tv, NULL);
79 /* hope we don't context-switch here */
80 delta = __CFReadTSR() / __CFTSRRate;
81 __CFLastSyncOffset = delta;
82 CFAbsoluteTime newBoot = ((double)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970) + 1.0E-6 * (double)tv.tv_usec - delta;
83 if (0.4 < fabs(newBoot - __CFBootAbsoluteTime)) {
84 /* 0.4 arbitrarily chosen to keep the reported absolute time too
85 * the 'actual' value, but not update __CFBootAbsoluteTime with
86 * small changes to keep it from jittering. */
87 __CFBootAbsoluteTime = newBoot;
88 }
89 }
90 return (at - __CFBootAbsoluteTime) * __CFTSRRate;
91 }
92
93 __private_extern__ CFAbsoluteTime __CFTSRToAbsoluteTime(int64_t tsr) {
94 CFTimeInterval delta = __CFReadTSR() / __CFTSRRate;
95 if (__CFLastSyncOffset + 0.01 < delta) {
96 struct timeval tv;
97 /* 0.01 seconds is arbitrarily chosen, but keeps the error under
98 0.00001 seconds generally; we need a number which is large enough
99 to cut down on the number of gettimeofday() calls, but small
100 enough that radical changes to the calendar clock are noticed
101 reasonably quickly. */
102 gettimeofday(&tv, NULL);
103 /* hope we don't context-switch here */
104 delta = __CFReadTSR() / __CFTSRRate;
105 __CFLastSyncOffset = delta;
106 CFAbsoluteTime newBoot = ((double)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970) + 1.0E-6 * (double)tv.tv_usec - delta;
107 if (0.4 < fabs(newBoot - __CFBootAbsoluteTime)) {
108 /* 0.4 arbitrarily chosen to keep the reported absolute time too
109 * the 'actual' value, but not update __CFBootAbsoluteTime with
110 * small changes to keep it from jittering. */
111 __CFBootAbsoluteTime = newBoot;
112 }
113 }
114 return __CFBootAbsoluteTime + (tsr / __CFTSRRate);
115 }
116 #endif
117
118 CFAbsoluteTime CFAbsoluteTimeGetCurrent(void) {
119 CFAbsoluteTime ret;
120 #if defined(__WIN32__)
121 FILETIME ft;
122 GetSystemTimeAsFileTime(&ft);
123 ret = (CFTimeInterval)ft.dwHighDateTime * 429.49672960;
124 ret += (CFTimeInterval)ft.dwLowDateTime / 10000000.0;
125 ret -= (11644473600.0 + kCFAbsoluteTimeIntervalSince1970);
126 /* seconds between 1601 and 1970, 1970 and 2001 */
127 #endif
128 #if defined(__MACH__)
129 ret = __CFTSRToAbsoluteTime(__CFReadTSR());
130 #endif
131 #if defined(__svr4__) || defined(__hpux__) || defined(__LINUX__)
132 struct timeval tv;
133 gettimeofday(&tv, NULL);
134 ret = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970;
135 ret += (1.0E-6 * (CFTimeInterval)tv.tv_usec);
136 #endif
137 return ret;
138 }
139
140 static Boolean __CFDateEqual(CFTypeRef cf1, CFTypeRef cf2) {
141 CFDateRef date1 = (CFDateRef)cf1;
142 CFDateRef date2 = (CFDateRef)cf2;
143 if (date1->_time != date2->_time) return false;
144 return true;
145 }
146
147 static CFHashCode __CFDateHash(CFTypeRef cf) {
148 CFDateRef date = (CFDateRef)cf;
149 return (CFHashCode)(float)floor(date->_time);
150 }
151
152 static CFStringRef __CFDateCopyDescription(CFTypeRef cf) {
153 CFDateRef date = (CFDateRef)cf;
154 return CFStringCreateWithFormat(CFGetAllocator(date), NULL, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf, CFGetAllocator(date), date->_time);
155 }
156
157 static CFTypeID __kCFDateTypeID = _kCFRuntimeNotATypeID;
158
159 static const CFRuntimeClass __CFDateClass = {
160 0,
161 "CFDate",
162 NULL, // init
163 NULL, // copy
164 NULL, // dealloc
165 __CFDateEqual,
166 __CFDateHash,
167 NULL, //
168 __CFDateCopyDescription
169 };
170
171 __private_extern__ void __CFDateInitialize(void) {
172 #if defined(__MACH__)
173 struct mach_timebase_info info;
174 mach_timebase_info(&info);
175 __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom;
176 #endif
177 __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass);
178 }
179
180 CFTypeID CFDateGetTypeID(void) {
181 return __kCFDateTypeID;
182 }
183
184 CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at) {
185 CFDateRef memory;
186 uint32_t size;
187 size = sizeof(struct __CFDate) - sizeof(CFRuntimeBase);
188 memory = _CFRuntimeCreateInstance(allocator, __kCFDateTypeID, size, NULL);
189 if (NULL == memory) {
190 return NULL;
191 }
192 ((struct __CFDate *)memory)->_time = at;
193 return memory;
194 }
195
196 CFTimeInterval CFDateGetAbsoluteTime(CFDateRef date) {
197 CF_OBJC_FUNCDISPATCH0(__kCFDateTypeID, CFTimeInterval, date, "timeIntervalSinceReferenceDate");
198 __CFGenericValidateType(date, CFDateGetTypeID());
199 return date->_time;
200 }
201
202 CFTimeInterval CFDateGetTimeIntervalSinceDate(CFDateRef date, CFDateRef otherDate) {
203 CF_OBJC_FUNCDISPATCH1(__kCFDateTypeID, CFTimeInterval, date, "timeIntervalSinceDate:", otherDate);
204 __CFGenericValidateType(date, CFDateGetTypeID());
205 __CFGenericValidateType(otherDate, CFDateGetTypeID());
206 return date->_time - otherDate->_time;
207 }
208
209 CFComparisonResult CFDateCompare(CFDateRef date, CFDateRef otherDate, void *context) {
210 CF_OBJC_FUNCDISPATCH1(__kCFDateTypeID, CFComparisonResult, date, "compare:", otherDate);
211 __CFGenericValidateType(date, CFDateGetTypeID());
212 __CFGenericValidateType(otherDate, CFDateGetTypeID());
213 if (date->_time < otherDate->_time) return kCFCompareLessThan;
214 if (date->_time > otherDate->_time) return kCFCompareGreaterThan;
215 return kCFCompareEqualTo;
216 }
217
218 CF_INLINE int32_t __CFDoubleModToInt(double d, int32_t modulus) {
219 int32_t result = (int32_t)(float)floor(d - floor(d / modulus) * modulus);
220 if (result < 0) result += modulus;
221 return result;
222 }
223
224 CF_INLINE double __CFDoubleMod(double d, int32_t modulus) {
225 double result = d - floor(d / modulus) * modulus;
226 if (result < 0.0) result += (double)modulus;
227 return result;
228 }
229
230 static const uint8_t daysInMonth[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0};
231 static const uint16_t daysBeforeMonth[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0};
232 static const uint16_t daysAfterMonth[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0};
233
234 static inline bool isleap(int32_t year) {
235 int32_t y = (year + 1) % 400; /* correct to nearest multiple-of-400 year, then find the remainder */
236 if (y < 0) y = -y;
237 return (0 == (y & 3) && 100 != y && 200 != y && 300 != y);
238 }
239
240 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
241 static inline uint8_t __CFDaysInMonth(int8_t month, int32_t year, bool leap) {
242 return daysInMonth[month] + (2 == month && leap);
243 }
244
245 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
246 static inline uint16_t __CFDaysBeforeMonth(int8_t month, int32_t year, bool leap) {
247 return daysBeforeMonth[month] + (2 < month && leap);
248 }
249
250 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
251 static inline uint16_t __CFDaysAfterMonth(int8_t month, int32_t year, bool leap) {
252 return daysAfterMonth[month] + (month < 2 && leap);
253 }
254
255 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
256 static void __CFYMDFromAbsolute(int32_t absolute, int32_t *year, int8_t *month, int8_t *day) {
257 int32_t b = absolute / 146097; // take care of as many multiples of 400 years as possible
258 int32_t y = b * 400;
259 uint16_t ydays;
260 absolute -= b * 146097;
261 while (absolute < 0) {
262 y -= 1;
263 absolute += __CFDaysAfterMonth(0, y, isleap(y));
264 }
265 /* Now absolute is non-negative days to add to year */
266 ydays = __CFDaysAfterMonth(0, y, isleap(y));
267 while (ydays <= absolute) {
268 y += 1;
269 absolute -= ydays;
270 ydays = __CFDaysAfterMonth(0, y, isleap(y));
271 }
272 /* Now we have year and days-into-year */
273 if (year) *year = y;
274 if (month || day) {
275 int8_t m = absolute / 33 + 1; /* search from the approximation */
276 bool leap = isleap(y);
277 while (__CFDaysBeforeMonth(m + 1, y, leap) <= absolute) m++;
278 if (month) *month = m;
279 if (day) *day = absolute - __CFDaysBeforeMonth(m, y, leap) + 1;
280 }
281 }
282
283 /* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */
284 static int32_t __CFAbsoluteFromYMD(int32_t year, int8_t month, int8_t day) {
285 int32_t absolute = 0, idx;
286 if (year < 0) {
287 for (idx = year; idx < 0; idx++)
288 absolute -= __CFDaysAfterMonth(0, idx, isleap(idx));
289 } else {
290 for (idx = year - 1; 0 <= idx; idx--)
291 absolute += __CFDaysAfterMonth(0, idx, isleap(idx));
292 }
293 /* Now add the days into the original year */
294 absolute += __CFDaysBeforeMonth(month, year, isleap(year)) + day - 1;
295 return absolute;
296 }
297
298 Boolean CFGregorianDateIsValid(CFGregorianDate gdate, CFOptionFlags unitFlags) {
299 if ((unitFlags & kCFGregorianUnitsYears) && (gdate.year <= 0)) return false;
300 if ((unitFlags & kCFGregorianUnitsMonths) && (gdate.month < 1 || 12 < gdate.month)) return false;
301 if ((unitFlags & kCFGregorianUnitsDays) && (gdate.day < 1 || 31 < gdate.day)) return false;
302 if ((unitFlags & kCFGregorianUnitsHours) && (gdate.hour < 0 || 23 < gdate.hour)) return false;
303 if ((unitFlags & kCFGregorianUnitsMinutes) && (gdate.minute < 0 || 59 < gdate.minute)) return false;
304 if ((unitFlags & kCFGregorianUnitsSeconds) && (gdate.second < 0.0 || 60.0 <= gdate.second)) return false;
305 if ((unitFlags & kCFGregorianUnitsDays) && (__CFDaysInMonth(gdate.month, gdate.year - 2001, isleap(gdate.year - 2001)) < gdate.day)) return false;
306 return true;
307 }
308
309 CFAbsoluteTime CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate, CFTimeZoneRef tz) {
310 CFAbsoluteTime at;
311 CFTimeInterval offset0, offset1;
312 if (NULL != tz) {
313 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
314 }
315 at = 86400.0 * __CFAbsoluteFromYMD(gdate.year - 2001, gdate.month, gdate.day);
316 at += 3600.0 * gdate.hour + 60.0 * gdate.minute + gdate.second;
317 if (NULL != tz) {
318 offset0 = CFTimeZoneGetSecondsFromGMT(tz, at);
319 offset1 = CFTimeZoneGetSecondsFromGMT(tz, at - offset0);
320 at -= offset1;
321 }
322 return at;
323 }
324
325 CFGregorianDate CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeZoneRef tz) {
326 CFGregorianDate gdate;
327 int32_t absolute, year;
328 int8_t month, day;
329 CFAbsoluteTime fixedat;
330 if (NULL != tz) {
331 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
332 }
333 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
334 absolute = (int32_t)(float)floor(fixedat / 86400.0);
335 __CFYMDFromAbsolute(absolute, &year, &month, &day);
336 gdate.year = year + 2001;
337 gdate.month = month;
338 gdate.day = day;
339 gdate.hour = __CFDoubleModToInt(floor(fixedat / 3600.0), 24);
340 gdate.minute = __CFDoubleModToInt(floor(fixedat / 60.0), 60);
341 gdate.second = __CFDoubleMod(fixedat, 60);
342 return gdate;
343 }
344
345 /* Note that the units of years and months are not equal length, but are treated as such. */
346 CFAbsoluteTime CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at, CFTimeZoneRef tz, CFGregorianUnits units) {
347 CFGregorianDate gdate;
348 CFGregorianUnits working;
349 CFAbsoluteTime candidate_at0, candidate_at1;
350 uint8_t monthdays;
351
352 if (NULL != tz) {
353 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
354 }
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
434 of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint.
435 Unfortunately, due to the nonuniformity of the year and month units, this
436 inversion essentially has to approximate until it finds the answer. */
437 CFGregorianUnits 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 + (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 return units;
460 }
461
462 SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz) {
463 int32_t absolute;
464 CFAbsoluteTime fixedat;
465 if (NULL != tz) {
466 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
467 }
468 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
469 absolute = (int32_t)(float)floor(fixedat / 86400.0);
470 return (absolute < 0) ? ((absolute + 1) % 7 + 7) : (absolute % 7 + 1); /* Monday = 1, etc. */
471 }
472
473 SInt32 CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) {
474 CFAbsoluteTime fixedat;
475 int32_t absolute, year;
476 int8_t month, day;
477 if (NULL != tz) {
478 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
479 }
480 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
481 absolute = (int32_t)(float)floor(fixedat / 86400.0);
482 __CFYMDFromAbsolute(absolute, &year, &month, &day);
483 return __CFDaysBeforeMonth(month, year, isleap(year)) + day;
484 }
485
486 /* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */
487 SInt32 CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) {
488 int32_t absolute, year;
489 int8_t month, day;
490 int32_t absolute0101, dow0101;
491 CFAbsoluteTime fixedat;
492 if (NULL != tz) {
493 __CFGenericValidateType(tz, CFTimeZoneGetTypeID());
494 }
495 fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0);
496 absolute = (int32_t)(float)floor(fixedat / 86400.0);
497 __CFYMDFromAbsolute(absolute, &year, &month, &day);
498 absolute0101 = __CFAbsoluteFromYMD(year, 1, 1);
499 dow0101 = (absolute0101 < 0) ? ((absolute0101 + 1) % 7 + 7) : (absolute0101 % 7 + 1);
500 /* First three and last three days of a year can end up in a week of a different year */
501 if (1 == month && day < 4) {
502 if ((day < 4 && 5 == dow0101) || (day < 3 && 6 == dow0101) || (day < 2 && 7 == dow0101)) {
503 return 53;
504 }
505 }
506 if (12 == month && 28 < day) {
507 int32_t absolute20101, dow20101;
508 absolute20101 = __CFAbsoluteFromYMD(year + 1, 1, 1);
509 dow20101 = (absolute20101 < 0) ? ((absolute20101 + 1) % 7 + 7) : (absolute20101 % 7 + 1);
510 if ((28 < day && 4 == dow20101) || (29 < day && 3 == dow20101) || (30 < day && 2 == dow20101)) {
511 return 1;
512 }
513 }
514 /* Days into year, plus a week-shifting correction, divided by 7. First week is #1. */
515 return (__CFDaysBeforeMonth(month, year, isleap(year)) + day + (dow0101 - 11) % 7 + 2) / 7 + 1;
516 }
517
518