]> git.saurik.com Git - apple/cf.git/blob - CFDate.c
CF-1152.14.tar.gz
[apple/cf.git] / 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 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
143 struct mach_timebase_info info;
144 mach_timebase_info(&info);
145 __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom;
146 __CF1_TSRRate = 1.0 / __CFTSRRate;
147 #elif DEPLOYMENT_TARGET_WINDOWS
148 LARGE_INTEGER freq;
149 if (!QueryPerformanceFrequency(&freq)) {
150 HALT;
151 }
152 __CFTSRRate = (double)freq.QuadPart;
153 __CF1_TSRRate = 1.0 / __CFTSRRate;
154 #elif DEPLOYMENT_TARGET_LINUX
155 struct timespec res;
156 if (clock_getres(CLOCK_MONOTONIC, &res) != 0) {
157 HALT;
158 }
159 __CFTSRRate = res.tv_sec + (1000000000 * res.tv_nsec);
160 __CF1_TSRRate = 1.0 / __CFTSRRate;
161 #else
162 #error Unable to initialize date
163 #endif
164 });
165 return __kCFDateTypeID;
166 }
167
168 CFDateRef 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
180 CFTimeInterval CFDateGetAbsoluteTime(CFDateRef date) {
181 CF_OBJC_FUNCDISPATCHV(CFDateGetTypeID(), CFTimeInterval, (NSDate *)date, timeIntervalSinceReferenceDate);
182 __CFGenericValidateType(date, CFDateGetTypeID());
183 return date->_time;
184 }
185
186 CFTimeInterval 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
193 CFComparisonResult 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
204 CF_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
210 CF_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
216 static const uint8_t daysInMonth[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0};
217 static const uint16_t daysBeforeMonth[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0};
218 static const uint16_t daysAfterMonth[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0};
219
220 CF_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 */
227 CF_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 */
232 CF_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 */
237 CF_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 */
242 static 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 */
270 static 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
288 Boolean 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
299 CFAbsoluteTime 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
317 CFGregorianDate 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. */
344 CFAbsoluteTime 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
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 + (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
463 SInt32 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
478 SInt32 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) */
496 SInt32 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