]>
Commit | Line | Data |
---|---|---|
bd5b749c A |
1 | /* |
2 | * Copyright (c) 2008 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 | /* CFTimeZone.c | |
24 | Copyright 1998-2002, Apple, Inc. All rights reserved. | |
25 | Responsibility: Christopher Kane | |
26 | */ | |
27 | ||
28 | #include <CoreFoundation/CFTimeZone.h> | |
29 | #include <CoreFoundation/CFPropertyList.h> | |
30 | #include "CFPriv.h" | |
31 | #include "CFInternal.h" | |
32 | #include <math.h> | |
33 | #include <limits.h> | |
34 | #include <sys/stat.h> | |
35 | #include <fcntl.h> | |
36 | #include <stdlib.h> | |
37 | #include <string.h> | |
38 | #include <unicode/ucal.h> | |
39 | #if DEPLOYMENT_TARGET_MACOSX | |
40 | #include <dirent.h> | |
41 | #include <unistd.h> | |
42 | #include <sys/fcntl.h> | |
43 | #include <tzfile.h> | |
44 | #endif | |
45 | ||
46 | #if DEPLOYMENT_TARGET_MACOSX | |
47 | #define TZZONELINK TZDEFAULT | |
48 | #define TZZONEINFO TZDIR "/" | |
49 | #endif | |
50 | ||
51 | CONST_STRING_DECL(kCFTimeZoneSystemTimeZoneDidChangeNotification, "kCFTimeZoneSystemTimeZoneDidChangeNotification") | |
52 | ||
53 | static CFTimeZoneRef __CFTimeZoneSystem = NULL; | |
54 | static CFTimeZoneRef __CFTimeZoneDefault = NULL; | |
55 | static CFDictionaryRef __CFTimeZoneAbbreviationDict = NULL; | |
56 | static CFSpinLock_t __CFTimeZoneAbbreviationLock = CFSpinLockInit; | |
57 | static CFMutableDictionaryRef __CFTimeZoneCompatibilityMappingDict = NULL; | |
58 | static CFSpinLock_t __CFTimeZoneCompatibilityMappingLock = CFSpinLockInit; | |
59 | static CFArrayRef __CFKnownTimeZoneList = NULL; | |
60 | static CFMutableDictionaryRef __CFTimeZoneCache = NULL; | |
61 | static CFSpinLock_t __CFTimeZoneGlobalLock = CFSpinLockInit; | |
62 | ||
63 | CF_INLINE void __CFTimeZoneLockGlobal(void) { | |
64 | __CFSpinLock(&__CFTimeZoneGlobalLock); | |
65 | } | |
66 | ||
67 | CF_INLINE void __CFTimeZoneUnlockGlobal(void) { | |
68 | __CFSpinUnlock(&__CFTimeZoneGlobalLock); | |
69 | } | |
70 | ||
71 | CF_INLINE void __CFTimeZoneLockAbbreviations(void) { | |
72 | __CFSpinLock(&__CFTimeZoneAbbreviationLock); | |
73 | } | |
74 | ||
75 | CF_INLINE void __CFTimeZoneUnlockAbbreviations(void) { | |
76 | __CFSpinUnlock(&__CFTimeZoneAbbreviationLock); | |
77 | } | |
78 | ||
79 | CF_INLINE void __CFTimeZoneLockCompatibilityMapping(void) { | |
80 | __CFSpinLock(&__CFTimeZoneCompatibilityMappingLock); | |
81 | } | |
82 | ||
83 | CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { | |
84 | __CFSpinUnlock(&__CFTimeZoneCompatibilityMappingLock); | |
85 | } | |
86 | ||
87 | #if DEPLOYMENT_TARGET_MACOSX | |
88 | static CFMutableArrayRef __CFCopyRecursiveDirectoryList() { | |
89 | CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
90 | int fd = open(TZDIR "/zone.tab", O_RDONLY); | |
91 | for (; 0 <= fd;) { | |
92 | uint8_t buffer[4096]; | |
93 | ssize_t len = read(fd, buffer, sizeof(buffer)); | |
94 | if (len <= 0) break; | |
95 | if (len < sizeof(buffer)) { | |
96 | // assumes that partial read only occurs at the end of the file | |
97 | buffer[len] = '\n'; | |
98 | len++; | |
99 | } | |
100 | const uint8_t *bytes = buffer; | |
101 | for (;;) { | |
102 | const uint8_t *nextl = memchr(bytes, '\n', len); | |
103 | if (!nextl) break; | |
104 | nextl++; | |
105 | if ('#' == *bytes) { | |
106 | len -= (nextl - bytes); | |
107 | bytes = nextl; | |
108 | continue; | |
109 | } | |
110 | const uint8_t *tab1 = memchr(bytes, '\t', (nextl - bytes)); | |
111 | if (!tab1) { | |
112 | len -= (nextl - bytes); | |
113 | bytes = nextl; | |
114 | continue; | |
115 | } | |
116 | tab1++; | |
117 | len -= (tab1 - bytes); | |
118 | bytes = tab1; | |
119 | const uint8_t *tab2 = memchr(bytes, '\t', (nextl - bytes)); | |
120 | if (!tab2) { | |
121 | len -= (nextl - bytes); | |
122 | bytes = nextl; | |
123 | continue; | |
124 | } | |
125 | tab2++; | |
126 | len -= (tab2 - bytes); | |
127 | bytes = tab2; | |
128 | const uint8_t *tab3 = memchr(bytes, '\t', (nextl - bytes)); | |
129 | int nmlen = tab3 ? (tab3 - bytes) : (nextl - 1 - bytes); | |
130 | CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, bytes, nmlen, kCFStringEncodingUTF8, false); | |
131 | CFArrayAppendValue(result, string); | |
132 | CFRelease(string); | |
133 | len -= (nextl - bytes); | |
134 | bytes = nextl; | |
135 | } | |
136 | lseek(fd, -len, SEEK_CUR); | |
137 | } | |
138 | close(fd); | |
139 | return result; | |
140 | } | |
141 | #else | |
142 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
143 | #endif | |
144 | ||
145 | typedef struct _CFTZPeriod { | |
146 | int32_t startSec; | |
147 | CFStringRef abbrev; | |
148 | uint32_t info; | |
149 | } CFTZPeriod; | |
150 | ||
151 | struct __CFTimeZone { | |
152 | CFRuntimeBase _base; | |
153 | CFStringRef _name; /* immutable */ | |
154 | CFDataRef _data; /* immutable */ | |
155 | CFTZPeriod *_periods; /* immutable */ | |
156 | int32_t _periodCnt; /* immutable */ | |
157 | }; | |
158 | ||
159 | /* startSec is the whole integer seconds from a CFAbsoluteTime, giving dates | |
160 | * between 1933 and 2069; info outside these years is discarded on read-in */ | |
161 | /* Bits 31-18 of the info are unused */ | |
162 | /* Bit 17 of the info is used for the is-DST state */ | |
163 | /* Bit 16 of the info is used for the sign of the offset (1 == negative) */ | |
164 | /* Bits 15-0 of the info are used for abs(offset) in seconds from GMT */ | |
165 | ||
166 | CF_INLINE void __CFTZPeriodInit(CFTZPeriod *period, int32_t startTime, CFStringRef abbrev, int32_t offset, Boolean isDST) { | |
167 | period->startSec = startTime; | |
168 | period->abbrev = abbrev ? (CFStringRef)CFRetain(abbrev) : NULL; | |
169 | __CFBitfieldSetValue(period->info, 15, 0, abs(offset)); | |
170 | __CFBitfieldSetValue(period->info, 16, 16, (offset < 0 ? 1 : 0)); | |
171 | __CFBitfieldSetValue(period->info, 17, 17, (isDST ? 1 : 0)); | |
172 | } | |
173 | ||
174 | CF_INLINE int32_t __CFTZPeriodStartSeconds(const CFTZPeriod *period) { | |
175 | return period->startSec; | |
176 | } | |
177 | ||
178 | CF_INLINE CFStringRef __CFTZPeriodAbbreviation(const CFTZPeriod *period) { | |
179 | return period->abbrev; | |
180 | } | |
181 | ||
182 | CF_INLINE int32_t __CFTZPeriodGMTOffset(const CFTZPeriod *period) { | |
183 | int32_t v = __CFBitfieldGetValue(period->info, 15, 0); | |
184 | if (__CFBitfieldGetValue(period->info, 16, 16)) v = -v; | |
185 | return v; | |
186 | } | |
187 | ||
188 | CF_INLINE Boolean __CFTZPeriodIsDST(const CFTZPeriod *period) { | |
189 | return (Boolean)__CFBitfieldGetValue(period->info, 17, 17); | |
190 | } | |
191 | ||
192 | static CFComparisonResult __CFCompareTZPeriods(const void *val1, const void *val2, void *context) { | |
193 | CFTZPeriod *tzp1 = (CFTZPeriod *)val1; | |
194 | CFTZPeriod *tzp2 = (CFTZPeriod *)val2; | |
195 | // we treat equal as less than, as the code which uses the | |
196 | // result of the bsearch doesn't expect exact matches | |
197 | // (they're pretty rare, so no point in over-coding for them) | |
198 | if (__CFTZPeriodStartSeconds(tzp1) <= __CFTZPeriodStartSeconds(tzp2)) return kCFCompareLessThan; | |
199 | return kCFCompareGreaterThan; | |
200 | } | |
201 | ||
202 | static CFIndex __CFBSearchTZPeriods(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
203 | CFTZPeriod elem; | |
204 | __CFTZPeriodInit(&elem, (int32_t)floor(at), NULL, 0, false); | |
205 | CFIndex idx = CFBSearch(&elem, sizeof(CFTZPeriod), tz->_periods, tz->_periodCnt, __CFCompareTZPeriods, NULL); | |
206 | if (tz->_periodCnt <= idx) { | |
207 | idx = tz->_periodCnt; | |
208 | } else if (0 == idx) { | |
209 | idx = 1; | |
210 | } | |
211 | return idx - 1; | |
212 | } | |
213 | ||
214 | ||
215 | CF_INLINE int32_t __CFDetzcode(const unsigned char *bufp) { | |
216 | int32_t result = (bufp[0] & 0x80) ? ~0L : 0L; | |
217 | result = (result << 8) | (bufp[0] & 0xff); | |
218 | result = (result << 8) | (bufp[1] & 0xff); | |
219 | result = (result << 8) | (bufp[2] & 0xff); | |
220 | result = (result << 8) | (bufp[3] & 0xff); | |
221 | return result; | |
222 | } | |
223 | ||
224 | CF_INLINE void __CFEntzcode(int32_t value, unsigned char *bufp) { | |
225 | bufp[0] = (value >> 24) & 0xff; | |
226 | bufp[1] = (value >> 16) & 0xff; | |
227 | bufp[2] = (value >> 8) & 0xff; | |
228 | bufp[3] = (value >> 0) & 0xff; | |
229 | } | |
230 | ||
231 | #if DEPLOYMENT_TARGET_MACOSX | |
232 | static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, CFTZPeriod **tzpp, CFIndex *cntp) { | |
233 | int32_t len, timecnt, typecnt, charcnt, idx, cnt; | |
234 | const uint8_t *p, *timep, *typep, *ttisp, *charp; | |
235 | CFStringRef *abbrs; | |
236 | Boolean result = true; | |
237 | ||
238 | p = CFDataGetBytePtr(data); | |
239 | len = CFDataGetLength(data); | |
240 | if (len < (int32_t)sizeof(struct tzhead)) { | |
241 | return false; | |
242 | } | |
243 | ||
244 | if (!(p[0] == 'T' && p[1] == 'Z' && p[2] == 'i' && p[3] == 'f')) return false; /* Don't parse without TZif at head of file */ | |
245 | ||
246 | p += 20 + 4 + 4 + 4; /* skip reserved, ttisgmtcnt, ttisstdcnt, leapcnt */ | |
247 | timecnt = __CFDetzcode(p); | |
248 | p += 4; | |
249 | typecnt = __CFDetzcode(p); | |
250 | p += 4; | |
251 | charcnt = __CFDetzcode(p); | |
252 | p += 4; | |
253 | if (typecnt <= 0 || timecnt < 0 || charcnt < 0) { | |
254 | return false; | |
255 | } | |
256 | if (1024 < timecnt || 32 < typecnt || 128 < charcnt) { | |
257 | // reject excessive timezones to avoid arithmetic overflows for | |
258 | // security reasons and to reject potentially corrupt files | |
259 | return false; | |
260 | } | |
261 | if (len - (int32_t)sizeof(struct tzhead) < (4 + 1) * timecnt + (4 + 1 + 1) * typecnt + charcnt) { | |
262 | return false; | |
263 | } | |
264 | timep = p; | |
265 | typep = timep + 4 * timecnt; | |
266 | ttisp = typep + timecnt; | |
267 | charp = ttisp + (4 + 1 + 1) * typecnt; | |
268 | cnt = (0 < timecnt) ? timecnt : 1; | |
269 | *tzpp = CFAllocatorAllocate(allocator, cnt * sizeof(CFTZPeriod), 0); | |
270 | if (__CFOASafe) __CFSetLastAllocationEventName(*tzpp, "CFTimeZone (store)"); | |
271 | memset(*tzpp, 0, cnt * sizeof(CFTZPeriod)); | |
272 | abbrs = CFAllocatorAllocate(allocator, (charcnt + 1) * sizeof(CFStringRef), 0); | |
273 | if (__CFOASafe) __CFSetLastAllocationEventName(abbrs, "CFTimeZone (temp)"); | |
274 | for (idx = 0; idx < charcnt + 1; idx++) { | |
275 | abbrs[idx] = NULL; | |
276 | } | |
277 | for (idx = 0; idx < cnt; idx++) { | |
278 | CFAbsoluteTime at; | |
279 | int32_t itime, offset; | |
280 | uint8_t type, dst, abbridx; | |
281 | ||
282 | at = (CFAbsoluteTime)(__CFDetzcode(timep) + 0.0) - kCFAbsoluteTimeIntervalSince1970; | |
283 | if (0 == timecnt) itime = INT_MIN; | |
284 | else if (at < (CFAbsoluteTime)INT_MIN) itime = INT_MIN; | |
285 | else if ((CFAbsoluteTime)INT_MAX < at) itime = INT_MAX; | |
286 | else itime = (int32_t)at; | |
287 | timep += 4; /* harmless if 0 == timecnt */ | |
288 | type = (0 < timecnt) ? (uint8_t)*typep++ : 0; | |
289 | if (typecnt <= type) { | |
290 | result = false; | |
291 | break; | |
292 | } | |
293 | offset = __CFDetzcode(ttisp + 6 * type); | |
294 | dst = (uint8_t)*(ttisp + 6 * type + 4); | |
295 | if (0 != dst && 1 != dst) { | |
296 | result = false; | |
297 | break; | |
298 | } | |
299 | abbridx = (uint8_t)*(ttisp + 6 * type + 5); | |
300 | if (charcnt < abbridx) { | |
301 | result = false; | |
302 | break; | |
303 | } | |
304 | if (NULL == abbrs[abbridx]) { | |
305 | abbrs[abbridx] = CFStringCreateWithCString(allocator, (char *)&charp[abbridx], kCFStringEncodingASCII); | |
306 | } | |
307 | __CFTZPeriodInit(*tzpp + idx, itime, abbrs[abbridx], offset, (dst ? true : false)); | |
308 | } | |
309 | for (idx = 0; idx < charcnt + 1; idx++) { | |
310 | if (NULL != abbrs[idx]) { | |
311 | CFRelease(abbrs[idx]); | |
312 | } | |
313 | } | |
314 | CFAllocatorDeallocate(allocator, abbrs); | |
315 | if (result) { | |
316 | // dump all but the last INT_MIN and the first INT_MAX | |
317 | for (idx = 0; idx < cnt; idx++) { | |
318 | if (((*tzpp + idx)->startSec == INT_MIN) && (idx + 1 < cnt) && (((*tzpp + idx + 1)->startSec == INT_MIN))) { | |
319 | if (NULL != (*tzpp + idx)->abbrev) CFRelease((*tzpp + idx)->abbrev); | |
320 | cnt--; | |
321 | memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); | |
322 | idx--; | |
323 | } | |
324 | } | |
325 | // Don't combine these loops! Watch the idx decrementing... | |
326 | for (idx = 0; idx < cnt; idx++) { | |
327 | if (((*tzpp + idx)->startSec == INT_MAX) && (0 < idx) && (((*tzpp + idx - 1)->startSec == INT_MAX))) { | |
328 | if (NULL != (*tzpp + idx)->abbrev) CFRelease((*tzpp + idx)->abbrev); | |
329 | cnt--; | |
330 | memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); | |
331 | idx--; | |
332 | } | |
333 | } | |
334 | CFQSortArray(*tzpp, cnt, sizeof(CFTZPeriod), __CFCompareTZPeriods, NULL); | |
335 | // if the first period is in DST and there is more than one period, drop it | |
336 | if (1 < cnt && __CFTZPeriodIsDST(*tzpp + 0)) { | |
337 | if (NULL != (*tzpp + 0)->abbrev) CFRelease((*tzpp + 0)->abbrev); | |
338 | cnt--; | |
339 | memmove((*tzpp + 0), (*tzpp + 0 + 1), sizeof(CFTZPeriod) * (cnt - 0)); | |
340 | } | |
341 | *cntp = cnt; | |
342 | } else { | |
343 | CFAllocatorDeallocate(allocator, *tzpp); | |
344 | *tzpp = NULL; | |
345 | } | |
346 | return result; | |
347 | } | |
348 | #elif 0 || 0 | |
349 | static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, CFTZPeriod **tzpp, CFIndex *cntp) { | |
350 | /* We use Win32 function to find TimeZone | |
351 | * (Aleksey Dukhnyakov) | |
352 | */ | |
353 | *tzpp = (CFTZPeriod *)CFAllocatorAllocate(allocator, sizeof(CFTZPeriod), 0); | |
354 | memset(*tzpp, 0, sizeof(CFTZPeriod)); | |
355 | __CFTZPeriodInit(*tzpp, 0, NULL, 0, false); | |
356 | *cntp = 1; | |
357 | return TRUE; | |
358 | } | |
359 | #else | |
360 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
361 | #endif | |
362 | ||
363 | static Boolean __CFTimeZoneEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
364 | CFTimeZoneRef tz1 = (CFTimeZoneRef)cf1; | |
365 | CFTimeZoneRef tz2 = (CFTimeZoneRef)cf2; | |
366 | if (!CFEqual(CFTimeZoneGetName(tz1), CFTimeZoneGetName(tz2))) return false; | |
367 | if (!CFEqual(CFTimeZoneGetData(tz1), CFTimeZoneGetData(tz2))) return false; | |
368 | return true; | |
369 | } | |
370 | ||
371 | static CFHashCode __CFTimeZoneHash(CFTypeRef cf) { | |
372 | CFTimeZoneRef tz = (CFTimeZoneRef)cf; | |
373 | return CFHash(CFTimeZoneGetName(tz)); | |
374 | } | |
375 | ||
376 | static CFStringRef __CFTimeZoneCopyDescription(CFTypeRef cf) { | |
377 | CFTimeZoneRef tz = (CFTimeZoneRef)cf; | |
378 | CFStringRef result, abbrev; | |
379 | CFAbsoluteTime at; | |
380 | at = CFAbsoluteTimeGetCurrent(); | |
381 | abbrev = CFTimeZoneCopyAbbreviation(tz, at); | |
382 | result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFTimeZone %p [%p]>{name = %@; abbreviation = %@; GMT offset = %g; is DST = %s}"), cf, CFGetAllocator(tz), tz->_name, abbrev, CFTimeZoneGetSecondsFromGMT(tz, at), CFTimeZoneIsDaylightSavingTime(tz, at) ? "true" : "false"); | |
383 | CFRelease(abbrev); | |
384 | return result; | |
385 | } | |
386 | ||
387 | static void __CFTimeZoneDeallocate(CFTypeRef cf) { | |
388 | CFTimeZoneRef tz = (CFTimeZoneRef)cf; | |
389 | CFAllocatorRef allocator = CFGetAllocator(tz); | |
390 | CFIndex idx; | |
391 | if (tz->_name) CFRelease(tz->_name); | |
392 | if (tz->_data) CFRelease(tz->_data); | |
393 | for (idx = 0; idx < tz->_periodCnt; idx++) { | |
394 | if (NULL != tz->_periods[idx].abbrev) CFRelease(tz->_periods[idx].abbrev); | |
395 | } | |
396 | if (NULL != tz->_periods) CFAllocatorDeallocate(allocator, tz->_periods); | |
397 | } | |
398 | ||
399 | static CFTypeID __kCFTimeZoneTypeID = _kCFRuntimeNotATypeID; | |
400 | ||
401 | static const CFRuntimeClass __CFTimeZoneClass = { | |
402 | 0, | |
403 | "CFTimeZone", | |
404 | NULL, // init | |
405 | NULL, // copy | |
406 | __CFTimeZoneDeallocate, | |
407 | __CFTimeZoneEqual, | |
408 | __CFTimeZoneHash, | |
409 | NULL, // | |
410 | __CFTimeZoneCopyDescription | |
411 | }; | |
412 | ||
413 | __private_extern__ void __CFTimeZoneInitialize(void) { | |
414 | __kCFTimeZoneTypeID = _CFRuntimeRegisterClass(&__CFTimeZoneClass); | |
415 | } | |
416 | ||
417 | CFTypeID CFTimeZoneGetTypeID(void) { | |
418 | return __kCFTimeZoneTypeID; | |
419 | } | |
420 | ||
421 | ||
422 | #if DEPLOYMENT_TARGET_MACOSX | |
423 | static CFTimeZoneRef __CFTimeZoneCreateSystem(void) { | |
424 | CFTimeZoneRef result = NULL; | |
425 | ||
426 | ||
427 | char *tzenv; | |
428 | int ret; | |
429 | char linkbuf[CFMaxPathSize]; | |
430 | ||
431 | tzenv = getenv("TZFILE"); | |
432 | if (NULL != tzenv) { | |
433 | CFStringRef name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)tzenv, strlen(tzenv), kCFStringEncodingUTF8, false); | |
434 | result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, false); | |
435 | CFRelease(name); | |
436 | if (result) return result; | |
437 | } | |
438 | tzenv = getenv("TZ"); | |
439 | if (NULL != tzenv) { | |
440 | CFStringRef name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)tzenv, strlen(tzenv), kCFStringEncodingUTF8, false); | |
441 | result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, true); | |
442 | CFRelease(name); | |
443 | if (result) return result; | |
444 | } | |
445 | ret = readlink(TZZONELINK, linkbuf, sizeof(linkbuf)); | |
446 | if (0 < ret) { | |
447 | CFStringRef name; | |
448 | linkbuf[ret] = '\0'; | |
449 | if (strncmp(linkbuf, TZZONEINFO, sizeof(TZZONEINFO) - 1) == 0) { | |
450 | name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf + sizeof(TZZONEINFO) - 1, strlen(linkbuf) - sizeof(TZZONEINFO) + 1, kCFStringEncodingUTF8, false); | |
451 | } else { | |
452 | name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf, strlen(linkbuf), kCFStringEncodingUTF8, false); | |
453 | } | |
454 | result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, false); | |
455 | CFRelease(name); | |
456 | if (result) return result; | |
457 | } | |
458 | return CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorSystemDefault, 0.0); | |
459 | } | |
460 | #elif 0 || 0 | |
461 | static CFTimeZoneRef __CFTimeZoneCreateSystem(void) { | |
462 | CFTimeZoneRef result = NULL; | |
463 | /* The GetTimeZoneInformation function retrieves the current | |
464 | * time-zone parameters for Win32 | |
465 | * (Aleksey Dukhnyakov) | |
466 | */ | |
467 | CFDataRef data; | |
468 | TIME_ZONE_INFORMATION tz; | |
469 | DWORD dw_result; | |
470 | dw_result=GetTimeZoneInformation(&tz); | |
471 | ||
472 | if ( dw_result == TIME_ZONE_ID_STANDARD || | |
473 | dw_result == TIME_ZONE_ID_DAYLIGHT ) { | |
474 | CFStringRef name = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)tz.StandardName, wcslen(tz.StandardName)); | |
475 | data = CFDataCreate(kCFAllocatorSystemDefault, (UInt8 *)&tz, sizeof(tz)); | |
476 | result = CFTimeZoneCreate(kCFAllocatorSystemDefault, name, data); | |
477 | CFRelease(name); | |
478 | CFRelease(data); | |
479 | if (result) return result; | |
480 | } | |
481 | return CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorSystemDefault, 0.0); | |
482 | } | |
483 | #else | |
484 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
485 | #endif | |
486 | ||
487 | CFTimeZoneRef CFTimeZoneCopySystem(void) { | |
488 | CFTimeZoneRef tz; | |
489 | __CFTimeZoneLockGlobal(); | |
490 | if (NULL == __CFTimeZoneSystem) { | |
491 | __CFTimeZoneUnlockGlobal(); | |
492 | tz = __CFTimeZoneCreateSystem(); | |
493 | __CFTimeZoneLockGlobal(); | |
494 | if (NULL == __CFTimeZoneSystem) { | |
495 | __CFTimeZoneSystem = tz; | |
496 | } else { | |
497 | if (tz) CFRelease(tz); | |
498 | } | |
499 | } | |
500 | tz = __CFTimeZoneSystem ? (CFTimeZoneRef)CFRetain(__CFTimeZoneSystem) : NULL; | |
501 | __CFTimeZoneUnlockGlobal(); | |
502 | return tz; | |
503 | } | |
504 | ||
505 | static CFIndex __noteCount = 0; | |
506 | ||
507 | void CFTimeZoneResetSystem(void) { | |
508 | __CFTimeZoneLockGlobal(); | |
509 | if (__CFTimeZoneDefault == __CFTimeZoneSystem) { | |
510 | if (__CFTimeZoneDefault) CFRelease(__CFTimeZoneDefault); | |
511 | __CFTimeZoneDefault = NULL; | |
512 | } | |
513 | CFTimeZoneRef tz = __CFTimeZoneSystem; | |
514 | __CFTimeZoneSystem = NULL; | |
515 | __CFTimeZoneUnlockGlobal(); | |
516 | if (tz) CFRelease(tz); | |
517 | } | |
518 | ||
519 | CFIndex _CFTimeZoneGetNoteCount(void) { | |
520 | return __noteCount; | |
521 | } | |
522 | ||
523 | CFTimeZoneRef CFTimeZoneCopyDefault(void) { | |
524 | CFTimeZoneRef tz; | |
525 | __CFTimeZoneLockGlobal(); | |
526 | if (NULL == __CFTimeZoneDefault) { | |
527 | __CFTimeZoneUnlockGlobal(); | |
528 | tz = CFTimeZoneCopySystem(); | |
529 | __CFTimeZoneLockGlobal(); | |
530 | if (NULL == __CFTimeZoneDefault) { | |
531 | __CFTimeZoneDefault = tz; | |
532 | } else { | |
533 | if (tz) CFRelease(tz); | |
534 | } | |
535 | } | |
536 | tz = __CFTimeZoneDefault ? (CFTimeZoneRef)CFRetain(__CFTimeZoneDefault) : NULL; | |
537 | __CFTimeZoneUnlockGlobal(); | |
538 | return tz; | |
539 | } | |
540 | ||
541 | void CFTimeZoneSetDefault(CFTimeZoneRef tz) { | |
542 | if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
543 | __CFTimeZoneLockGlobal(); | |
544 | if (tz != __CFTimeZoneDefault) { | |
545 | if (tz) CFRetain(tz); | |
546 | if (__CFTimeZoneDefault) CFRelease(__CFTimeZoneDefault); | |
547 | __CFTimeZoneDefault = tz; | |
548 | } | |
549 | __CFTimeZoneUnlockGlobal(); | |
550 | } | |
551 | ||
552 | static CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary(void); | |
553 | ||
554 | CFArrayRef CFTimeZoneCopyKnownNames(void) { | |
555 | CFArrayRef tzs; | |
556 | __CFTimeZoneLockGlobal(); | |
557 | if (NULL == __CFKnownTimeZoneList) { | |
558 | CFMutableArrayRef list; | |
559 | /* TimeZone information locate in the registry for Win32 | |
560 | * (Aleksey Dukhnyakov) | |
561 | */ | |
562 | #if DEPLOYMENT_TARGET_MACOSX | |
563 | list = __CFCopyRecursiveDirectoryList(); | |
564 | #else | |
565 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
566 | #endif | |
567 | // Remove undesirable ancient cruft | |
568 | CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); | |
569 | CFIndex idx; | |
570 | for (idx = CFArrayGetCount(list); idx--; ) { | |
571 | CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(list, idx); | |
572 | if (CFDictionaryContainsKey(dict, item)) { | |
573 | CFArrayRemoveValueAtIndex(list, idx); | |
574 | } | |
575 | } | |
576 | __CFKnownTimeZoneList = CFArrayCreateCopy(kCFAllocatorSystemDefault, list); | |
577 | CFRelease(list); | |
578 | } | |
579 | tzs = __CFKnownTimeZoneList ? (CFArrayRef)CFRetain(__CFKnownTimeZoneList) : NULL; | |
580 | __CFTimeZoneUnlockGlobal(); | |
581 | return tzs; | |
582 | } | |
583 | ||
584 | #if DEPLOYMENT_TARGET_MACOSX | |
585 | /* The criteria here are sort of: coverage for the U.S. and Europe, | |
586 | * large cities, abbreviation uniqueness, and perhaps a few others. | |
587 | * But do not make the list too large with obscure information. | |
588 | */ | |
589 | static const char *__CFTimeZoneAbbreviationDefaults = | |
590 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | |
591 | " <!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">" | |
592 | " <plist version=\"1.0\">" | |
593 | " <dict>" | |
594 | " <key>ADT</key> <string>America/Halifax</string>" | |
595 | " <key>AKDT</key> <string>America/Juneau</string>" | |
596 | " <key>AKST</key> <string>America/Juneau</string>" | |
597 | " <key>ART</key> <string>America/Argentina/Buenos_Aires</string>" | |
598 | " <key>AST</key> <string>America/Halifax</string>" | |
599 | " <key>BDT</key> <string>Asia/Dhaka</string>" | |
600 | " <key>BRST</key> <string>America/Sao_Paulo</string>" | |
601 | " <key>BRT</key> <string>America/Sao_Paulo</string>" | |
602 | " <key>BST</key> <string>Europe/London</string>" | |
603 | " <key>CAT</key> <string>Africa/Harare</string>" | |
604 | " <key>CDT</key> <string>America/Chicago</string>" | |
605 | " <key>CEST</key> <string>Europe/Paris</string>" | |
606 | " <key>CET</key> <string>Europe/Paris</string>" | |
607 | " <key>CLST</key> <string>America/Santiago</string>" | |
608 | " <key>CLT</key> <string>America/Santiago</string>" | |
609 | " <key>COT</key> <string>America/Bogota</string>" | |
610 | " <key>CST</key> <string>America/Chicago</string>" | |
611 | " <key>EAT</key> <string>Africa/Addis_Ababa</string>" | |
612 | " <key>EDT</key> <string>America/New_York</string>" | |
613 | " <key>EEST</key> <string>Europe/Istanbul</string>" | |
614 | " <key>EET</key> <string>Europe/Istanbul</string>" | |
615 | " <key>EST</key> <string>America/New_York</string>" | |
616 | " <key>GMT</key> <string>GMT</string>" | |
617 | " <key>GST</key> <string>Asia/Dubai</string>" | |
618 | " <key>HKT</key> <string>Asia/Hong_Kong</string>" | |
619 | " <key>HST</key> <string>Pacific/Honolulu</string>" | |
620 | " <key>ICT</key> <string>Asia/Bangkok</string>" | |
621 | " <key>IRST</key> <string>Asia/Tehran</string>" | |
622 | " <key>IST</key> <string>Asia/Calcutta</string>" | |
623 | " <key>JST</key> <string>Asia/Tokyo</string>" | |
624 | " <key>KST</key> <string>Asia/Seoul</string>" | |
625 | " <key>MDT</key> <string>America/Denver</string>" | |
626 | " <key>MSD</key> <string>Europe/Moscow</string>" | |
627 | " <key>MSK</key> <string>Europe/Moscow</string>" | |
628 | " <key>MST</key> <string>America/Denver</string>" | |
629 | " <key>NZDT</key> <string>Pacific/Auckland</string>" | |
630 | " <key>NZST</key> <string>Pacific/Auckland</string>" | |
631 | " <key>PDT</key> <string>America/Los_Angeles</string>" | |
632 | " <key>PET</key> <string>America/Lima</string>" | |
633 | " <key>PHT</key> <string>Asia/Manila</string>" | |
634 | " <key>PKT</key> <string>Asia/Karachi</string>" | |
635 | " <key>PST</key> <string>America/Los_Angeles</string>" | |
636 | " <key>SGT</key> <string>Asia/Singapore</string>" | |
637 | " <key>UTC</key> <string>UTC</string>" | |
638 | " <key>WAT</key> <string>Africa/Lagos</string>" | |
639 | " <key>WEST</key> <string>Europe/Lisbon</string>" | |
640 | " <key>WET</key> <string>Europe/Lisbon</string>" | |
641 | " <key>WIT</key> <string>Asia/Jakarta</string>" | |
642 | " </dict>" | |
643 | " </plist>"; | |
644 | #elif 0 || 0 | |
645 | static const char *__CFTimeZoneAbbreviationDefaults = | |
646 | /* Mappings to time zones in Windows Registry are best-guess */ | |
647 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | |
648 | " <!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">" | |
649 | " <plist version=\"1.0\">" | |
650 | " <dict>" | |
651 | " <key>ADT</key> <string>Atlantic Standard Time</string>" | |
652 | " <key>AKDT</key> <string>Alaskan Standard Time</string>" | |
653 | " <key>AKST</key> <string>Alaskan Standard Time</string>" | |
654 | " <key>ART</key> <string>SA Eastern Standard Time</string>" | |
655 | " <key>AST</key> <string>Atlantic Standard Time</string>" | |
656 | " <key>BDT</key> <string>Central Asia Standard Time</string>" | |
657 | " <key>BRST</key> <string>SA Eastern Standard Time</string>" | |
658 | " <key>BRT</key> <string>SA Eastern Standard Time</string>" | |
659 | " <key>BST</key> <string>GMT Standard Time</string>" | |
660 | " <key>CAT</key> <string>South Africa Standard Time</string>" | |
661 | " <key>CDT</key> <string>Central Standard Time</string>" | |
662 | " <key>CEST</key> <string>Central Europe Standard Time</string>" | |
663 | " <key>CET</key> <string>Central Europe Standard Time</string>" | |
664 | " <key>CLST</key> <string>SA Western Standard Time</string>" | |
665 | " <key>CLT</key> <string>SA Western Standard Time</string>" | |
666 | " <key>COT</key> <string>Central Standard Time</string>" | |
667 | " <key>CST</key> <string>Central Standard Time</string>" | |
668 | " <key>EAT</key> <string>E. Africa Standard Time</string>" | |
669 | " <key>EDT</key> <string>Eastern Standard Time</string>" | |
670 | " <key>EEST</key> <string>E. Europe Standard Time</string>" | |
671 | " <key>EET</key> <string>E. Europe Standard Time</string>" | |
672 | " <key>EST</key> <string>Eastern Standard Time</string>" | |
673 | " <key>GMT</key> <string>Greenwich Standard Time</string>" | |
674 | " <key>GST</key> <string>Arabian Standard Time</string>" | |
675 | " <key>HKT</key> <string>China Standard Time</string>" | |
676 | " <key>HST</key> <string>Hawaiian Standard Time</string>" | |
677 | " <key>ICT</key> <string>SE Asia Standard Time</string>" | |
678 | " <key>IRST</key> <string>Iran Standard Time</string>" | |
679 | " <key>IST</key> <string>India Standard Time</string>" | |
680 | " <key>JST</key> <string>Tokyo Standard Time</string>" | |
681 | " <key>KST</key> <string>Korea Standard Time</string>" | |
682 | " <key>MDT</key> <string>Mountain Standard Time</string>" | |
683 | " <key>MSD</key> <string>E. Europe Standard Time</string>" | |
684 | " <key>MSK</key> <string>E. Europe Standard Time</string>" | |
685 | " <key>MST</key> <string>Mountain Standard Time</string>" | |
686 | " <key>NZDT</key> <string>New Zealand Standard Time</string>" | |
687 | " <key>NZST</key> <string>New Zealand Standard Time</string>" | |
688 | " <key>PDT</key> <string>Pacific Standard Time</string>" | |
689 | " <key>PET</key> <string>SA Pacific Standard Time</string>" | |
690 | " <key>PHT</key> <string>Taipei Standard Time</string>" | |
691 | " <key>PKT</key> <string>West Asia Standard Time</string>" | |
692 | " <key>PST</key> <string>Pacific Standard Time</string>" | |
693 | " <key>SGT</key> <string>Singapore Standard Time</string>" | |
694 | " <key>UTC</key> <string>Greenwich Standard Time</string>" | |
695 | " <key>WAT</key> <string>W. Central Africa Standard Time</string>" | |
696 | " <key>WEST</key> <string>W. Europe Standard Time</string>" | |
697 | " <key>WET</key> <string>W. Europe Standard Time</string>" | |
698 | " <key>WIT</key> <string>SE Asia Standard Time</string>" | |
699 | " </dict>" | |
700 | " </plist>"; | |
701 | #else | |
702 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
703 | #endif | |
704 | ||
705 | CFDictionaryRef CFTimeZoneCopyAbbreviationDictionary(void) { | |
706 | CFDictionaryRef dict; | |
707 | __CFTimeZoneLockAbbreviations(); | |
708 | if (NULL == __CFTimeZoneAbbreviationDict) { | |
709 | CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)__CFTimeZoneAbbreviationDefaults, strlen(__CFTimeZoneAbbreviationDefaults)); | |
710 | __CFTimeZoneAbbreviationDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); | |
711 | CFRelease(data); | |
712 | } | |
713 | if (NULL == __CFTimeZoneAbbreviationDict) { | |
714 | __CFTimeZoneAbbreviationDict = CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, NULL, NULL); | |
715 | } | |
716 | dict = __CFTimeZoneAbbreviationDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneAbbreviationDict) : NULL; | |
717 | __CFTimeZoneUnlockAbbreviations(); | |
718 | return dict; | |
719 | } | |
720 | ||
721 | void CFTimeZoneSetAbbreviationDictionary(CFDictionaryRef dict) { | |
722 | __CFGenericValidateType(dict, CFDictionaryGetTypeID()); | |
723 | __CFTimeZoneLockGlobal(); | |
724 | if (dict != __CFTimeZoneAbbreviationDict) { | |
725 | if (dict) CFRetain(dict); | |
726 | if (__CFTimeZoneAbbreviationDict) { | |
727 | CFIndex count, idx; | |
728 | count = CFDictionaryGetCount(__CFTimeZoneAbbreviationDict); | |
729 | CFTypeRef *keys = (CFTypeRef *)malloc(sizeof(CFTypeRef *) * count); | |
730 | for (idx = 0; idx < count; idx++) { | |
731 | CFDictionaryRemoveValue(__CFTimeZoneCache, (CFStringRef)keys[idx]); | |
732 | } | |
733 | free(keys); | |
734 | CFRelease(__CFTimeZoneAbbreviationDict); | |
735 | } | |
736 | __CFTimeZoneAbbreviationDict = dict; | |
737 | } | |
738 | __CFTimeZoneUnlockGlobal(); | |
739 | } | |
740 | ||
741 | CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { | |
742 | // assert: (NULL != name && NULL != data); | |
743 | CFTimeZoneRef memory; | |
744 | uint32_t size; | |
745 | CFTZPeriod *tzp = NULL; | |
746 | CFIndex idx, cnt = 0; | |
747 | ||
748 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
749 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
750 | __CFGenericValidateType(name, CFStringGetTypeID()); | |
751 | __CFGenericValidateType(data, CFDataGetTypeID()); | |
752 | __CFTimeZoneLockGlobal(); | |
753 | if (NULL != __CFTimeZoneCache && CFDictionaryGetValueIfPresent(__CFTimeZoneCache, name, (const void **)&memory)) { | |
754 | __CFTimeZoneUnlockGlobal(); | |
755 | return (CFTimeZoneRef)CFRetain(memory); | |
756 | } | |
757 | if (!__CFParseTimeZoneData(allocator, data, &tzp, &cnt)) { | |
758 | __CFTimeZoneUnlockGlobal(); | |
759 | return NULL; | |
760 | } | |
761 | size = sizeof(struct __CFTimeZone) - sizeof(CFRuntimeBase); | |
762 | memory = (CFTimeZoneRef)_CFRuntimeCreateInstance(allocator, CFTimeZoneGetTypeID(), size, NULL); | |
763 | if (NULL == memory) { | |
764 | __CFTimeZoneUnlockGlobal(); | |
765 | for (idx = 0; idx < cnt; idx++) { | |
766 | if (NULL != tzp[idx].abbrev) CFRelease(tzp[idx].abbrev); | |
767 | } | |
768 | if (NULL != tzp) CFAllocatorDeallocate(allocator, tzp); | |
769 | return NULL; | |
770 | } | |
771 | ((struct __CFTimeZone *)memory)->_name = (CFStringRef)CFStringCreateCopy(allocator, name); | |
772 | ((struct __CFTimeZone *)memory)->_data = CFDataCreateCopy(allocator, data); | |
773 | ((struct __CFTimeZone *)memory)->_periods = tzp; | |
774 | ((struct __CFTimeZone *)memory)->_periodCnt = cnt; | |
775 | if (NULL == __CFTimeZoneCache) { | |
776 | __CFTimeZoneCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
777 | } | |
778 | CFDictionaryAddValue(__CFTimeZoneCache, ((struct __CFTimeZone *)memory)->_name, memory); | |
779 | __CFTimeZoneUnlockGlobal(); | |
780 | return memory; | |
781 | } | |
782 | ||
783 | #if DEPLOYMENT_TARGET_MACOSX | |
784 | static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t seconds, CFStringRef name, int isDST) { | |
785 | CFTimeZoneRef result; | |
786 | CFDataRef data; | |
787 | int32_t nameLen = CFStringGetLength(name); | |
788 | unsigned char dataBytes[52 + nameLen + 1]; | |
789 | memset(dataBytes, 0, sizeof(dataBytes)); | |
790 | ||
791 | // Put in correct magic bytes for timezone structures | |
792 | dataBytes[0] = 'T'; | |
793 | dataBytes[1] = 'Z'; | |
794 | dataBytes[2] = 'i'; | |
795 | dataBytes[3] = 'f'; | |
796 | ||
797 | __CFEntzcode(1, dataBytes + 20); | |
798 | __CFEntzcode(1, dataBytes + 24); | |
799 | __CFEntzcode(1, dataBytes + 36); | |
800 | __CFEntzcode(nameLen + 1, dataBytes + 40); | |
801 | __CFEntzcode(seconds, dataBytes + 44); | |
802 | dataBytes[48] = isDST ? 1 : 0; | |
803 | CFStringGetCString(name, (char *)dataBytes + 50, nameLen + 1, kCFStringEncodingASCII); | |
804 | data = CFDataCreate(allocator, dataBytes, 52 + nameLen + 1); | |
805 | result = CFTimeZoneCreate(allocator, name, data); | |
806 | CFRelease(data); | |
807 | return result; | |
808 | } | |
809 | #elif 0 || 0 | |
810 | static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t seconds, CFStringRef name, int isDST) { | |
811 | /* CFTimeZoneRef->_data will contain TIME_ZONE_INFORMATION structure | |
812 | * to find current timezone | |
813 | * (Aleksey Dukhnyakov) | |
814 | */ | |
815 | CFTimeZoneRef result; | |
816 | TIME_ZONE_INFORMATION tzi; | |
817 | CFDataRef data; | |
818 | CFIndex length = CFStringGetLength(name); | |
819 | ||
820 | memset(&tzi,0,sizeof(tzi)); | |
821 | tzi.Bias=(long)(-seconds/60); | |
822 | CFStringGetCharacters(name, CFRangeMake(0, length < 31 ? length : 31 ), (UniChar *)tzi.StandardName); | |
823 | data = CFDataCreate(allocator,(UInt8 *)&tzi, sizeof(tzi)); | |
824 | result = CFTimeZoneCreate(allocator, name, data); | |
825 | CFRelease(data); | |
826 | return result; | |
827 | } | |
828 | #else | |
829 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
830 | #endif | |
831 | ||
832 | // rounds offset to nearest minute | |
833 | CFTimeZoneRef CFTimeZoneCreateWithTimeIntervalFromGMT(CFAllocatorRef allocator, CFTimeInterval ti) { | |
834 | CFTimeZoneRef result; | |
835 | CFStringRef name; | |
836 | int32_t seconds, minute, hour; | |
837 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
838 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
839 | if (ti < -18.0 * 3600 || 18.0 * 3600 < ti) return NULL; | |
840 | ti = (ti < 0.0) ? ceil((ti / 60.0) - 0.5) * 60.0 : floor((ti / 60.0) + 0.5) * 60.0; | |
841 | seconds = (int32_t)ti; | |
842 | hour = (ti < 0) ? (-seconds / 3600) : (seconds / 3600); | |
843 | seconds -= ((ti < 0) ? -hour : hour) * 3600; | |
844 | minute = (ti < 0) ? (-seconds / 60) : (seconds / 60); | |
845 | if (fabs(ti) < 1.0) { | |
846 | name = (CFStringRef)CFRetain(CFSTR("GMT")); | |
847 | } else { | |
848 | name = CFStringCreateWithFormat(allocator, NULL, CFSTR("GMT%c%02d%02d"), (ti < 0.0 ? '-' : '+'), hour, minute); | |
849 | } | |
850 | result = __CFTimeZoneCreateFixed(allocator, (int32_t)ti, name, 0); | |
851 | CFRelease(name); | |
852 | return result; | |
853 | } | |
854 | ||
855 | CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef name, Boolean tryAbbrev) { | |
856 | CFTimeZoneRef result = NULL; | |
857 | CFStringRef tzName = NULL; | |
858 | CFDataRef data = NULL; | |
859 | ||
860 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
861 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
862 | __CFGenericValidateType(name, CFStringGetTypeID()); | |
863 | if (CFEqual(CFSTR(""), name)) { | |
864 | // empty string is not a time zone name, just abort now, | |
865 | // following stuff will fail anyway | |
866 | return NULL; | |
867 | } | |
868 | __CFTimeZoneLockGlobal(); | |
869 | if (NULL != __CFTimeZoneCache && CFDictionaryGetValueIfPresent(__CFTimeZoneCache, name, (const void **)&result)) { | |
870 | __CFTimeZoneUnlockGlobal(); | |
871 | return (CFTimeZoneRef)CFRetain(result); | |
872 | } | |
873 | __CFTimeZoneUnlockGlobal(); | |
874 | #if DEPLOYMENT_TARGET_MACOSX | |
875 | CFURLRef baseURL, tempURL; | |
876 | void *bytes; | |
877 | CFIndex length; | |
878 | ||
879 | baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR(TZZONEINFO), kCFURLPOSIXPathStyle, true); | |
880 | if (tryAbbrev) { | |
881 | CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); | |
882 | tzName = CFDictionaryGetValue(abbrevs, name); | |
883 | if (NULL != tzName) { | |
884 | tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); | |
885 | if (NULL != tempURL) { | |
886 | if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0)) { | |
887 | data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); | |
888 | } | |
889 | CFRelease(tempURL); | |
890 | } | |
891 | } | |
892 | CFRelease(abbrevs); | |
893 | } | |
894 | if (NULL == data) { | |
895 | CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); | |
896 | CFStringRef mapping = CFDictionaryGetValue(dict, name); | |
897 | if (mapping) { | |
898 | name = mapping; | |
899 | } else if (CFStringHasPrefix(name, CFSTR(TZZONEINFO))) { | |
900 | CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); | |
901 | CFStringDelete(unprefixed, CFRangeMake(0, sizeof(TZZONEINFO))); | |
902 | mapping = CFDictionaryGetValue(dict, unprefixed); | |
903 | if (mapping) { | |
904 | name = mapping; | |
905 | } | |
906 | CFRelease(unprefixed); | |
907 | } | |
908 | CFRelease(dict); | |
909 | if (CFEqual(CFSTR(""), name)) { | |
910 | return NULL; | |
911 | } | |
912 | } | |
913 | if (NULL == data) { | |
914 | tzName = name; | |
915 | tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); | |
916 | if (NULL != tempURL) { | |
917 | if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0)) { | |
918 | data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); | |
919 | } | |
920 | CFRelease(tempURL); | |
921 | } | |
922 | } | |
923 | CFRelease(baseURL); | |
924 | if (NULL != data) { | |
925 | result = CFTimeZoneCreate(allocator, tzName, data); | |
926 | if (name != tzName) { | |
927 | CFStringRef nameCopy = (CFStringRef)CFStringCreateCopy(allocator, name); | |
928 | __CFTimeZoneLockGlobal(); | |
929 | CFDictionaryAddValue(__CFTimeZoneCache, nameCopy, result); | |
930 | __CFTimeZoneUnlockGlobal(); | |
931 | CFRelease(nameCopy); | |
932 | } | |
933 | CFRelease(data); | |
934 | } | |
935 | return result; | |
936 | } | |
937 | #elif 0 || 0 | |
938 | /* Reading GMT offset and daylight flag from the registry | |
939 | * for TimeZone name | |
940 | * (Aleksey Dukhnyakov) | |
941 | */ | |
942 | { | |
943 | CFStringRef safeName = name; | |
944 | struct { | |
945 | LONG Bias; | |
946 | LONG StandardBias; | |
947 | LONG DaylightBias; | |
948 | SYSTEMTIME StandardDate; | |
949 | SYSTEMTIME DaylightDate; | |
950 | } tzi; | |
951 | TIME_ZONE_INFORMATION tzi_system; | |
952 | ||
953 | HKEY hkResult; | |
954 | DWORD dwType, dwSize=sizeof(tzi), | |
955 | dwSize_name1=sizeof(tzi_system.StandardName), | |
956 | dwSize_name2=sizeof(tzi_system.DaylightName); | |
957 | ||
958 | if (tryAbbrev) { | |
959 | CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); | |
960 | tzName = (CFStringRef)CFDictionaryGetValue(abbrevs, name); | |
961 | if (NULL == tzName) { | |
962 | CFRelease(abbrevs); | |
963 | return NULL; | |
964 | } | |
965 | name = tzName; | |
966 | CFRelease(abbrevs); | |
967 | } | |
968 | ||
969 | /* Open regestry and move down to the TimeZone information | |
970 | */ | |
971 | if (RegOpenKey(HKEY_LOCAL_MACHINE,_T(TZZONEINFO),&hkResult) != | |
972 | ERROR_SUCCESS ) { | |
973 | return NULL; | |
974 | } | |
975 | /* Move down to specific TimeZone name | |
976 | */ | |
977 | #if defined(UNICODE) | |
978 | UniChar *uniTimeZone = (UniChar*)CFStringGetCharactersPtr(name); | |
979 | if (uniTimeZone == NULL) { | |
980 | // We need to extract the bytes out of the CFStringRef and create our own | |
981 | // UNICODE string to pass to the Win32 API - RegOpenKey. | |
982 | UInt8 uniBuff[MAX_PATH+2]; // adding +2 to handle Unicode-null termination /0/0. | |
983 | CFIndex usedBuff = 0; | |
984 | CFIndex numChars = CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)), kCFStringEncodingUnicode, 0, FALSE, uniBuff, MAX_PATH, &usedBuff); | |
985 | if (numChars == 0) { | |
986 | return NULL; | |
987 | } else { | |
988 | // NULL-terminate the newly created Unicode string. | |
989 | uniBuff[usedBuff] = '\0'; | |
990 | uniBuff[usedBuff+1] = '\0'; | |
991 | } | |
992 | ||
993 | if (RegOpenKey(hkResult, (LPCWSTR)uniBuff ,&hkResult) != ERROR_SUCCESS ) { | |
994 | return NULL; | |
995 | } | |
996 | } else { | |
997 | if (RegOpenKey(hkResult, (LPCWSTR)uniTimeZone ,&hkResult) != ERROR_SUCCESS ) { | |
998 | return NULL; | |
999 | } | |
1000 | } | |
1001 | #else | |
1002 | if (RegOpenKey(hkResult,CFStringGetCStringPtr(name, CFStringGetSystemEncoding()),&hkResult) != ERROR_SUCCESS ) { | |
1003 | return NULL; | |
1004 | } | |
1005 | #endif | |
1006 | ||
1007 | /* TimeZone information(offsets, daylight flag, ...) assign to tzi structure | |
1008 | */ | |
1009 | if ( RegQueryValueEx(hkResult,_T("TZI"),NULL,&dwType,(LPBYTE)&tzi,&dwSize) != ERROR_SUCCESS && | |
1010 | RegQueryValueEx(hkResult,_T("Std"),NULL,&dwType,(LPBYTE)&tzi_system.StandardName,&dwSize_name1) != ERROR_SUCCESS && | |
1011 | RegQueryValueEx(hkResult,_T("Dlt"),NULL,&dwType,(LPBYTE)&tzi_system.DaylightName,&dwSize_name2) != ERROR_SUCCESS ) | |
1012 | { | |
1013 | return NULL; | |
1014 | } | |
1015 | ||
1016 | tzi_system.Bias=tzi.Bias; | |
1017 | tzi_system.StandardBias=tzi.StandardBias; | |
1018 | tzi_system.DaylightBias=tzi.DaylightBias; | |
1019 | tzi_system.StandardDate=tzi.StandardDate; | |
1020 | tzi_system.DaylightDate=tzi.DaylightDate; | |
1021 | ||
1022 | /* CFTimeZoneRef->_data will contain TIME_ZONE_INFORMATION structure | |
1023 | * to find current timezone | |
1024 | * (Aleksey Dukhnyakov) | |
1025 | */ | |
1026 | data = CFDataCreate(allocator,(UInt8 *)&tzi_system, sizeof(tzi_system)); | |
1027 | ||
1028 | RegCloseKey(hkResult); | |
1029 | result = CFTimeZoneCreate(allocator, name, data); | |
1030 | if (result) { | |
1031 | if (tryAbbrev) | |
1032 | result->_periods->abbrev = (CFStringRef)CFStringCreateCopy(allocator,safeName); | |
1033 | else { | |
1034 | } | |
1035 | } | |
1036 | CFRelease(data); | |
1037 | } | |
1038 | return result; | |
1039 | } | |
1040 | #else | |
1041 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
1042 | #endif | |
1043 | ||
1044 | CFStringRef CFTimeZoneGetName(CFTimeZoneRef tz) { | |
1045 | CF_OBJC_FUNCDISPATCH0(CFTimeZoneGetTypeID(), CFStringRef, tz, "name"); | |
1046 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1047 | return tz->_name; | |
1048 | } | |
1049 | ||
1050 | CFDataRef CFTimeZoneGetData(CFTimeZoneRef tz) { | |
1051 | CF_OBJC_FUNCDISPATCH0(CFTimeZoneGetTypeID(), CFDataRef, tz, "data"); | |
1052 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1053 | return tz->_data; | |
1054 | } | |
1055 | ||
1056 | CFTimeInterval CFTimeZoneGetSecondsFromGMT(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
1057 | #if DEPLOYMENT_TARGET_MACOSX | |
1058 | CFIndex idx; | |
1059 | CF_OBJC_FUNCDISPATCH1(CFTimeZoneGetTypeID(), CFTimeInterval, tz, "_secondsFromGMTForAbsoluteTime:", at); | |
1060 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1061 | idx = __CFBSearchTZPeriods(tz, at); | |
1062 | return __CFTZPeriodGMTOffset(&(tz->_periods[idx])); | |
1063 | #endif | |
1064 | } | |
1065 | ||
1066 | CFStringRef CFTimeZoneCopyAbbreviation(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
1067 | CFStringRef result; | |
1068 | CFIndex idx; | |
1069 | CF_OBJC_FUNCDISPATCH1(CFTimeZoneGetTypeID(), CFStringRef, tz, "_abbreviationForAbsoluteTime:", at); | |
1070 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1071 | #if DEPLOYMENT_TARGET_MACOSX | |
1072 | idx = __CFBSearchTZPeriods(tz, at); | |
1073 | #endif | |
1074 | result = __CFTZPeriodAbbreviation(&(tz->_periods[idx])); | |
1075 | return result ? (CFStringRef)CFRetain(result) : NULL; | |
1076 | } | |
1077 | ||
1078 | Boolean CFTimeZoneIsDaylightSavingTime(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
1079 | #if DEPLOYMENT_TARGET_MACOSX | |
1080 | CFIndex idx; | |
1081 | CF_OBJC_FUNCDISPATCH1(CFTimeZoneGetTypeID(), Boolean, tz, "_isDaylightSavingTimeForAbsoluteTime:", at); | |
1082 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1083 | idx = __CFBSearchTZPeriods(tz, at); | |
1084 | return __CFTZPeriodIsDST(&(tz->_periods[idx])); | |
1085 | #endif | |
1086 | } | |
1087 | ||
1088 | CFTimeInterval CFTimeZoneGetDaylightSavingTimeOffset(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
1089 | CF_OBJC_FUNCDISPATCH1(CFTimeZoneGetTypeID(), CFTimeInterval, tz, "_daylightSavingTimeOffsetForAbsoluteTime:", at); | |
1090 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1091 | CFIndex idx = __CFBSearchTZPeriods(tz, at); | |
1092 | if (__CFTZPeriodIsDST(&(tz->_periods[idx]))) { | |
1093 | CFTimeInterval offset = __CFTZPeriodGMTOffset(&(tz->_periods[idx])); | |
1094 | if (idx + 1 < tz->_periodCnt) { | |
1095 | return offset - __CFTZPeriodGMTOffset(&(tz->_periods[idx + 1])); | |
1096 | } else if (0 < idx) { | |
1097 | return offset - __CFTZPeriodGMTOffset(&(tz->_periods[idx - 1])); | |
1098 | } | |
1099 | } | |
1100 | return 0.0; | |
1101 | } | |
1102 | ||
1103 | CFAbsoluteTime CFTimeZoneGetNextDaylightSavingTimeTransition(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
1104 | CF_OBJC_FUNCDISPATCH1(CFTimeZoneGetTypeID(), CFTimeInterval, tz, "_nextDaylightSavingTimeTransitionAfterAbsoluteTime:", at); | |
1105 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1106 | CFIndex idx = __CFBSearchTZPeriods(tz, at); | |
1107 | if (tz->_periodCnt <= idx + 1) { | |
1108 | return 0.0; | |
1109 | } | |
1110 | return (CFAbsoluteTime)__CFTZPeriodStartSeconds(&(tz->_periods[idx + 1])); | |
1111 | } | |
1112 | ||
1113 | enum { | |
1114 | kCFTimeZoneNameStyleGeneric = 4, | |
1115 | kCFTimeZoneNameStyleShortGeneric = 5 | |
1116 | }; | |
1117 | ||
1118 | extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz); | |
1119 | ||
1120 | #define BUFFER_SIZE 768 | |
1121 | ||
1122 | CFStringRef CFTimeZoneCopyLocalizedName(CFTimeZoneRef tz, CFTimeZoneNameStyle style, CFLocaleRef locale) { | |
1123 | CF_OBJC_FUNCDISPATCH2(CFTimeZoneGetTypeID(), CFStringRef, tz, "localizedName:locale:", style, locale); | |
1124 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
1125 | __CFGenericValidateType(locale, CFLocaleGetTypeID()); | |
1126 | ||
1127 | ||
1128 | CFStringRef localeID = CFLocaleGetIdentifier(locale); | |
1129 | UCalendar *cal = __CFCalendarCreateUCalendar(NULL, localeID, tz); | |
1130 | if (NULL == cal) { | |
1131 | return NULL; | |
1132 | } | |
1133 | ||
1134 | char buffer[BUFFER_SIZE]; | |
1135 | const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII); | |
1136 | if (NULL == cstr) { | |
1137 | if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
1138 | } | |
1139 | if (NULL == cstr) { | |
1140 | ucal_close(cal); | |
1141 | return NULL; | |
1142 | } | |
1143 | ||
1144 | UChar ubuffer[BUFFER_SIZE]; | |
1145 | UErrorCode status = U_ZERO_ERROR; | |
1146 | int32_t cnt = ucal_getTimeZoneDisplayName(cal, (UCalendarDisplayNameType)style, cstr, ubuffer, BUFFER_SIZE, &status); | |
1147 | ucal_close(cal); | |
1148 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1149 | return CFStringCreateWithCharacters(CFGetAllocator(tz), (const UniChar *)ubuffer, cnt); | |
1150 | } | |
1151 | return NULL; | |
1152 | } | |
1153 | ||
1154 | static CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary(void) { | |
1155 | CFDictionaryRef dict; | |
1156 | __CFTimeZoneLockCompatibilityMapping(); | |
1157 | if (NULL == __CFTimeZoneCompatibilityMappingDict) { | |
1158 | __CFTimeZoneCompatibilityMappingDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 112, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1159 | ||
1160 | // Empty string means delete/ignore these | |
1161 | } | |
1162 | dict = __CFTimeZoneCompatibilityMappingDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneCompatibilityMappingDict) : NULL; | |
1163 | __CFTimeZoneUnlockCompatibilityMapping(); | |
1164 | return dict; | |
1165 | } | |
1166 | ||
1167 | #undef TZZONEINFO | |
1168 | #undef TZZONELINK | |
1169 |