]>
Commit | Line | Data |
---|---|---|
bd5b749c | 1 | /* |
e29e285d | 2 | * Copyright (c) 2015 Apple Inc. All rights reserved. |
bd5b749c A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d7384798 | 5 | * |
bd5b749c A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
d7384798 | 12 | * |
bd5b749c A |
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. | |
d7384798 | 20 | * |
bd5b749c A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
f64f9b69 | 23 | |
bd5b749c | 24 | /* CFTimeZone.c |
d7384798 | 25 | Copyright (c) 1998-2014, Apple Inc. All rights reserved. |
bd5b749c A |
26 | Responsibility: Christopher Kane |
27 | */ | |
28 | ||
cf7d2af9 | 29 | |
bd5b749c A |
30 | #include <CoreFoundation/CFTimeZone.h> |
31 | #include <CoreFoundation/CFPropertyList.h> | |
cf7d2af9 A |
32 | #include <CoreFoundation/CFDateFormatter.h> |
33 | #include <CoreFoundation/CFPriv.h> | |
bd5b749c A |
34 | #include "CFInternal.h" |
35 | #include <math.h> | |
36 | #include <limits.h> | |
37 | #include <sys/stat.h> | |
38 | #include <fcntl.h> | |
39 | #include <stdlib.h> | |
40 | #include <string.h> | |
41 | #include <unicode/ucal.h> | |
cf7d2af9 | 42 | #include <CoreFoundation/CFDateFormatter.h> |
8ca704e1 | 43 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX |
bd5b749c A |
44 | #include <dirent.h> |
45 | #include <unistd.h> | |
46 | #include <sys/fcntl.h> | |
8ca704e1 A |
47 | #endif |
48 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
bd5b749c | 49 | #include <tzfile.h> |
8ca704e1 A |
50 | #elif DEPLOYMENT_TARGET_LINUX |
51 | #ifndef TZDIR | |
52 | #define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ | |
53 | #endif /* !defined TZDIR */ | |
54 | ||
55 | #ifndef TZDEFAULT | |
56 | #define TZDEFAULT "/etc/localtime" | |
57 | #endif /* !defined TZDEFAULT */ | |
58 | ||
59 | struct tzhead { | |
60 | char tzh_magic[4]; /* TZ_MAGIC */ | |
61 | char tzh_reserved[16]; /* reserved for future use */ | |
62 | char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ | |
63 | char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ | |
64 | char tzh_leapcnt[4]; /* coded number of leap seconds */ | |
65 | char tzh_timecnt[4]; /* coded number of transition times */ | |
66 | char tzh_typecnt[4]; /* coded number of local time types */ | |
67 | char tzh_charcnt[4]; /* coded number of abbr. chars */ | |
68 | }; | |
bd5b749c | 69 | #endif |
cf7d2af9 | 70 | |
8ca704e1 | 71 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX |
bd5b749c A |
72 | #define TZZONELINK TZDEFAULT |
73 | #define TZZONEINFO TZDIR "/" | |
cf7d2af9 A |
74 | #elif DEPLOYMENT_TARGET_WINDOWS |
75 | static CFStringRef __tzZoneInfo = NULL; | |
76 | static char *__tzDir = NULL; | |
77 | static void __InitTZStrings(void); | |
78 | #else | |
79 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
bd5b749c A |
80 | #endif |
81 | ||
8ca704e1 A |
82 | #if DEPLOYMENT_TARGET_LINUX |
83 | // Symbol aliases | |
84 | CF_EXPORT CFStringRef const kCFDateFormatterTimeZone __attribute__((weak, alias ("kCFDateFormatterTimeZoneKey"))); | |
85 | #endif | |
86 | ||
bd5b749c A |
87 | CONST_STRING_DECL(kCFTimeZoneSystemTimeZoneDidChangeNotification, "kCFTimeZoneSystemTimeZoneDidChangeNotification") |
88 | ||
89 | static CFTimeZoneRef __CFTimeZoneSystem = NULL; | |
90 | static CFTimeZoneRef __CFTimeZoneDefault = NULL; | |
91 | static CFDictionaryRef __CFTimeZoneAbbreviationDict = NULL; | |
d7384798 | 92 | static CFLock_t __CFTimeZoneAbbreviationLock = CFLockInit; |
bd5b749c | 93 | static CFMutableDictionaryRef __CFTimeZoneCompatibilityMappingDict = NULL; |
d7384798 | 94 | static CFLock_t __CFTimeZoneCompatibilityMappingLock = CFLockInit; |
bd5b749c A |
95 | static CFArrayRef __CFKnownTimeZoneList = NULL; |
96 | static CFMutableDictionaryRef __CFTimeZoneCache = NULL; | |
d7384798 | 97 | static CFLock_t __CFTimeZoneGlobalLock = CFLockInit; |
bd5b749c | 98 | |
cf7d2af9 A |
99 | #if DEPLOYMENT_TARGET_WINDOWS |
100 | static CFDictionaryRef __CFTimeZoneWinToOlsonDict = NULL; | |
d7384798 | 101 | static CFLock_t __CFTimeZoneWinToOlsonLock = CFLockInit; |
cf7d2af9 A |
102 | #endif |
103 | ||
bd5b749c | 104 | CF_INLINE void __CFTimeZoneLockGlobal(void) { |
d7384798 | 105 | __CFLock(&__CFTimeZoneGlobalLock); |
bd5b749c A |
106 | } |
107 | ||
108 | CF_INLINE void __CFTimeZoneUnlockGlobal(void) { | |
d7384798 | 109 | __CFUnlock(&__CFTimeZoneGlobalLock); |
bd5b749c A |
110 | } |
111 | ||
112 | CF_INLINE void __CFTimeZoneLockAbbreviations(void) { | |
d7384798 | 113 | __CFLock(&__CFTimeZoneAbbreviationLock); |
bd5b749c A |
114 | } |
115 | ||
116 | CF_INLINE void __CFTimeZoneUnlockAbbreviations(void) { | |
d7384798 | 117 | __CFUnlock(&__CFTimeZoneAbbreviationLock); |
bd5b749c A |
118 | } |
119 | ||
120 | CF_INLINE void __CFTimeZoneLockCompatibilityMapping(void) { | |
d7384798 | 121 | __CFLock(&__CFTimeZoneCompatibilityMappingLock); |
bd5b749c A |
122 | } |
123 | ||
124 | CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { | |
d7384798 | 125 | __CFUnlock(&__CFTimeZoneCompatibilityMappingLock); |
bd5b749c A |
126 | } |
127 | ||
cf7d2af9 A |
128 | #if DEPLOYMENT_TARGET_WINDOWS |
129 | /* This function should be used for WIN32 instead of | |
130 | * __CFCopyRecursiveDirectoryList function. | |
131 | * It takes TimeZone names from the registry | |
132 | * (Aleksey Dukhnyakov) | |
133 | */ | |
134 | static CFMutableArrayRef __CFCopyWindowsTimeZoneList() { | |
135 | CFMutableArrayRef result = NULL; | |
136 | HKEY hkResult; | |
137 | TCHAR lpName[MAX_PATH+1]; | |
138 | DWORD dwIndex, retCode; | |
139 | ||
140 | if (RegOpenKey(HKEY_LOCAL_MACHINE,_T(TZZONEINFO),&hkResult) != | |
141 | ERROR_SUCCESS ) | |
142 | return NULL; | |
143 | ||
144 | result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
145 | for (dwIndex=0; (retCode = RegEnumKey(hkResult,dwIndex,lpName,MAX_PATH)) != ERROR_NO_MORE_ITEMS ; dwIndex++) { | |
146 | if (retCode != ERROR_SUCCESS) { | |
147 | RegCloseKey(hkResult); | |
148 | CFRelease(result); | |
149 | return NULL; | |
150 | } else { | |
151 | #if defined(UNICODE) | |
152 | CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)lpName, (_tcslen(lpName) * sizeof(UniChar)), kCFStringEncodingUnicode, false); | |
153 | #else | |
154 | CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, lpName, _tcslen(lpName), CFStringGetSystemEncoding(), false); | |
155 | #endif | |
156 | CFArrayAppendValue(result, string); | |
157 | CFRelease(string); | |
158 | } | |
159 | } | |
160 | ||
161 | RegCloseKey(hkResult); | |
162 | return result; | |
163 | } | |
8ca704e1 | 164 | #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
bd5b749c A |
165 | static CFMutableArrayRef __CFCopyRecursiveDirectoryList() { |
166 | CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
cf7d2af9 A |
167 | #if DEPLOYMENT_TARGET_WINDOWS |
168 | if (!__tzDir) __InitTZStrings(); | |
169 | if (!__tzDir) return result; | |
170 | int fd = open(__tzDir, O_RDONLY); | |
171 | #else | |
bd5b749c | 172 | int fd = open(TZDIR "/zone.tab", O_RDONLY); |
cf7d2af9 A |
173 | #endif |
174 | ||
bd5b749c A |
175 | for (; 0 <= fd;) { |
176 | uint8_t buffer[4096]; | |
177 | ssize_t len = read(fd, buffer, sizeof(buffer)); | |
178 | if (len <= 0) break; | |
179 | if (len < sizeof(buffer)) { | |
180 | // assumes that partial read only occurs at the end of the file | |
181 | buffer[len] = '\n'; | |
182 | len++; | |
183 | } | |
184 | const uint8_t *bytes = buffer; | |
185 | for (;;) { | |
186 | const uint8_t *nextl = memchr(bytes, '\n', len); | |
187 | if (!nextl) break; | |
188 | nextl++; | |
189 | if ('#' == *bytes) { | |
190 | len -= (nextl - bytes); | |
191 | bytes = nextl; | |
192 | continue; | |
193 | } | |
194 | const uint8_t *tab1 = memchr(bytes, '\t', (nextl - bytes)); | |
195 | if (!tab1) { | |
196 | len -= (nextl - bytes); | |
197 | bytes = nextl; | |
198 | continue; | |
199 | } | |
200 | tab1++; | |
201 | len -= (tab1 - bytes); | |
202 | bytes = tab1; | |
203 | const uint8_t *tab2 = memchr(bytes, '\t', (nextl - bytes)); | |
204 | if (!tab2) { | |
205 | len -= (nextl - bytes); | |
206 | bytes = nextl; | |
207 | continue; | |
208 | } | |
209 | tab2++; | |
210 | len -= (tab2 - bytes); | |
211 | bytes = tab2; | |
212 | const uint8_t *tab3 = memchr(bytes, '\t', (nextl - bytes)); | |
213 | int nmlen = tab3 ? (tab3 - bytes) : (nextl - 1 - bytes); | |
214 | CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, bytes, nmlen, kCFStringEncodingUTF8, false); | |
215 | CFArrayAppendValue(result, string); | |
216 | CFRelease(string); | |
217 | len -= (nextl - bytes); | |
218 | bytes = nextl; | |
219 | } | |
220 | lseek(fd, -len, SEEK_CUR); | |
221 | } | |
222 | close(fd); | |
223 | return result; | |
224 | } | |
225 | #else | |
226 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
227 | #endif | |
228 | ||
229 | typedef struct _CFTZPeriod { | |
230 | int32_t startSec; | |
231 | CFStringRef abbrev; | |
232 | uint32_t info; | |
233 | } CFTZPeriod; | |
234 | ||
235 | struct __CFTimeZone { | |
236 | CFRuntimeBase _base; | |
237 | CFStringRef _name; /* immutable */ | |
238 | CFDataRef _data; /* immutable */ | |
239 | CFTZPeriod *_periods; /* immutable */ | |
240 | int32_t _periodCnt; /* immutable */ | |
241 | }; | |
242 | ||
243 | /* startSec is the whole integer seconds from a CFAbsoluteTime, giving dates | |
244 | * between 1933 and 2069; info outside these years is discarded on read-in */ | |
245 | /* Bits 31-18 of the info are unused */ | |
246 | /* Bit 17 of the info is used for the is-DST state */ | |
247 | /* Bit 16 of the info is used for the sign of the offset (1 == negative) */ | |
248 | /* Bits 15-0 of the info are used for abs(offset) in seconds from GMT */ | |
249 | ||
250 | CF_INLINE void __CFTZPeriodInit(CFTZPeriod *period, int32_t startTime, CFStringRef abbrev, int32_t offset, Boolean isDST) { | |
251 | period->startSec = startTime; | |
252 | period->abbrev = abbrev ? (CFStringRef)CFRetain(abbrev) : NULL; | |
253 | __CFBitfieldSetValue(period->info, 15, 0, abs(offset)); | |
254 | __CFBitfieldSetValue(period->info, 16, 16, (offset < 0 ? 1 : 0)); | |
255 | __CFBitfieldSetValue(period->info, 17, 17, (isDST ? 1 : 0)); | |
256 | } | |
257 | ||
258 | CF_INLINE int32_t __CFTZPeriodStartSeconds(const CFTZPeriod *period) { | |
259 | return period->startSec; | |
260 | } | |
261 | ||
262 | CF_INLINE CFStringRef __CFTZPeriodAbbreviation(const CFTZPeriod *period) { | |
263 | return period->abbrev; | |
264 | } | |
265 | ||
266 | CF_INLINE int32_t __CFTZPeriodGMTOffset(const CFTZPeriod *period) { | |
267 | int32_t v = __CFBitfieldGetValue(period->info, 15, 0); | |
268 | if (__CFBitfieldGetValue(period->info, 16, 16)) v = -v; | |
269 | return v; | |
270 | } | |
271 | ||
272 | CF_INLINE Boolean __CFTZPeriodIsDST(const CFTZPeriod *period) { | |
273 | return (Boolean)__CFBitfieldGetValue(period->info, 17, 17); | |
274 | } | |
275 | ||
276 | static CFComparisonResult __CFCompareTZPeriods(const void *val1, const void *val2, void *context) { | |
277 | CFTZPeriod *tzp1 = (CFTZPeriod *)val1; | |
278 | CFTZPeriod *tzp2 = (CFTZPeriod *)val2; | |
279 | // we treat equal as less than, as the code which uses the | |
280 | // result of the bsearch doesn't expect exact matches | |
281 | // (they're pretty rare, so no point in over-coding for them) | |
282 | if (__CFTZPeriodStartSeconds(tzp1) <= __CFTZPeriodStartSeconds(tzp2)) return kCFCompareLessThan; | |
283 | return kCFCompareGreaterThan; | |
284 | } | |
285 | ||
286 | static CFIndex __CFBSearchTZPeriods(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
287 | CFTZPeriod elem; | |
cf7d2af9 | 288 | __CFTZPeriodInit(&elem, (int32_t)floor(at + 1.0), NULL, 0, false); |
bd5b749c A |
289 | CFIndex idx = CFBSearch(&elem, sizeof(CFTZPeriod), tz->_periods, tz->_periodCnt, __CFCompareTZPeriods, NULL); |
290 | if (tz->_periodCnt <= idx) { | |
291 | idx = tz->_periodCnt; | |
292 | } else if (0 == idx) { | |
293 | idx = 1; | |
294 | } | |
295 | return idx - 1; | |
296 | } | |
297 | ||
298 | ||
299 | CF_INLINE int32_t __CFDetzcode(const unsigned char *bufp) { | |
300 | int32_t result = (bufp[0] & 0x80) ? ~0L : 0L; | |
301 | result = (result << 8) | (bufp[0] & 0xff); | |
302 | result = (result << 8) | (bufp[1] & 0xff); | |
303 | result = (result << 8) | (bufp[2] & 0xff); | |
304 | result = (result << 8) | (bufp[3] & 0xff); | |
305 | return result; | |
306 | } | |
307 | ||
308 | CF_INLINE void __CFEntzcode(int32_t value, unsigned char *bufp) { | |
309 | bufp[0] = (value >> 24) & 0xff; | |
310 | bufp[1] = (value >> 16) & 0xff; | |
311 | bufp[2] = (value >> 8) & 0xff; | |
312 | bufp[3] = (value >> 0) & 0xff; | |
313 | } | |
314 | ||
bd5b749c A |
315 | static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, CFTZPeriod **tzpp, CFIndex *cntp) { |
316 | int32_t len, timecnt, typecnt, charcnt, idx, cnt; | |
317 | const uint8_t *p, *timep, *typep, *ttisp, *charp; | |
318 | CFStringRef *abbrs; | |
319 | Boolean result = true; | |
320 | ||
321 | p = CFDataGetBytePtr(data); | |
322 | len = CFDataGetLength(data); | |
323 | if (len < (int32_t)sizeof(struct tzhead)) { | |
324 | return false; | |
325 | } | |
326 | ||
327 | if (!(p[0] == 'T' && p[1] == 'Z' && p[2] == 'i' && p[3] == 'f')) return false; /* Don't parse without TZif at head of file */ | |
328 | ||
329 | p += 20 + 4 + 4 + 4; /* skip reserved, ttisgmtcnt, ttisstdcnt, leapcnt */ | |
330 | timecnt = __CFDetzcode(p); | |
331 | p += 4; | |
332 | typecnt = __CFDetzcode(p); | |
333 | p += 4; | |
334 | charcnt = __CFDetzcode(p); | |
335 | p += 4; | |
336 | if (typecnt <= 0 || timecnt < 0 || charcnt < 0) { | |
337 | return false; | |
338 | } | |
339 | if (1024 < timecnt || 32 < typecnt || 128 < charcnt) { | |
340 | // reject excessive timezones to avoid arithmetic overflows for | |
341 | // security reasons and to reject potentially corrupt files | |
342 | return false; | |
343 | } | |
344 | if (len - (int32_t)sizeof(struct tzhead) < (4 + 1) * timecnt + (4 + 1 + 1) * typecnt + charcnt) { | |
345 | return false; | |
346 | } | |
347 | timep = p; | |
348 | typep = timep + 4 * timecnt; | |
349 | ttisp = typep + timecnt; | |
350 | charp = ttisp + (4 + 1 + 1) * typecnt; | |
351 | cnt = (0 < timecnt) ? timecnt : 1; | |
352 | *tzpp = CFAllocatorAllocate(allocator, cnt * sizeof(CFTZPeriod), 0); | |
353 | if (__CFOASafe) __CFSetLastAllocationEventName(*tzpp, "CFTimeZone (store)"); | |
354 | memset(*tzpp, 0, cnt * sizeof(CFTZPeriod)); | |
355 | abbrs = CFAllocatorAllocate(allocator, (charcnt + 1) * sizeof(CFStringRef), 0); | |
356 | if (__CFOASafe) __CFSetLastAllocationEventName(abbrs, "CFTimeZone (temp)"); | |
357 | for (idx = 0; idx < charcnt + 1; idx++) { | |
358 | abbrs[idx] = NULL; | |
359 | } | |
360 | for (idx = 0; idx < cnt; idx++) { | |
361 | CFAbsoluteTime at; | |
362 | int32_t itime, offset; | |
363 | uint8_t type, dst, abbridx; | |
364 | ||
365 | at = (CFAbsoluteTime)(__CFDetzcode(timep) + 0.0) - kCFAbsoluteTimeIntervalSince1970; | |
366 | if (0 == timecnt) itime = INT_MIN; | |
367 | else if (at < (CFAbsoluteTime)INT_MIN) itime = INT_MIN; | |
368 | else if ((CFAbsoluteTime)INT_MAX < at) itime = INT_MAX; | |
369 | else itime = (int32_t)at; | |
370 | timep += 4; /* harmless if 0 == timecnt */ | |
371 | type = (0 < timecnt) ? (uint8_t)*typep++ : 0; | |
372 | if (typecnt <= type) { | |
373 | result = false; | |
374 | break; | |
375 | } | |
376 | offset = __CFDetzcode(ttisp + 6 * type); | |
377 | dst = (uint8_t)*(ttisp + 6 * type + 4); | |
378 | if (0 != dst && 1 != dst) { | |
379 | result = false; | |
380 | break; | |
381 | } | |
382 | abbridx = (uint8_t)*(ttisp + 6 * type + 5); | |
383 | if (charcnt < abbridx) { | |
384 | result = false; | |
385 | break; | |
386 | } | |
387 | if (NULL == abbrs[abbridx]) { | |
388 | abbrs[abbridx] = CFStringCreateWithCString(allocator, (char *)&charp[abbridx], kCFStringEncodingASCII); | |
389 | } | |
390 | __CFTZPeriodInit(*tzpp + idx, itime, abbrs[abbridx], offset, (dst ? true : false)); | |
391 | } | |
392 | for (idx = 0; idx < charcnt + 1; idx++) { | |
393 | if (NULL != abbrs[idx]) { | |
394 | CFRelease(abbrs[idx]); | |
395 | } | |
396 | } | |
397 | CFAllocatorDeallocate(allocator, abbrs); | |
398 | if (result) { | |
399 | // dump all but the last INT_MIN and the first INT_MAX | |
400 | for (idx = 0; idx < cnt; idx++) { | |
401 | if (((*tzpp + idx)->startSec == INT_MIN) && (idx + 1 < cnt) && (((*tzpp + idx + 1)->startSec == INT_MIN))) { | |
402 | if (NULL != (*tzpp + idx)->abbrev) CFRelease((*tzpp + idx)->abbrev); | |
403 | cnt--; | |
404 | memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); | |
405 | idx--; | |
406 | } | |
407 | } | |
408 | // Don't combine these loops! Watch the idx decrementing... | |
409 | for (idx = 0; idx < cnt; idx++) { | |
410 | if (((*tzpp + idx)->startSec == INT_MAX) && (0 < idx) && (((*tzpp + idx - 1)->startSec == INT_MAX))) { | |
411 | if (NULL != (*tzpp + idx)->abbrev) CFRelease((*tzpp + idx)->abbrev); | |
412 | cnt--; | |
413 | memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); | |
414 | idx--; | |
415 | } | |
416 | } | |
417 | CFQSortArray(*tzpp, cnt, sizeof(CFTZPeriod), __CFCompareTZPeriods, NULL); | |
418 | // if the first period is in DST and there is more than one period, drop it | |
419 | if (1 < cnt && __CFTZPeriodIsDST(*tzpp + 0)) { | |
420 | if (NULL != (*tzpp + 0)->abbrev) CFRelease((*tzpp + 0)->abbrev); | |
421 | cnt--; | |
422 | memmove((*tzpp + 0), (*tzpp + 0 + 1), sizeof(CFTZPeriod) * (cnt - 0)); | |
423 | } | |
424 | *cntp = cnt; | |
425 | } else { | |
426 | CFAllocatorDeallocate(allocator, *tzpp); | |
427 | *tzpp = NULL; | |
428 | } | |
429 | return result; | |
430 | } | |
bd5b749c A |
431 | |
432 | static Boolean __CFTimeZoneEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
433 | CFTimeZoneRef tz1 = (CFTimeZoneRef)cf1; | |
434 | CFTimeZoneRef tz2 = (CFTimeZoneRef)cf2; | |
435 | if (!CFEqual(CFTimeZoneGetName(tz1), CFTimeZoneGetName(tz2))) return false; | |
436 | if (!CFEqual(CFTimeZoneGetData(tz1), CFTimeZoneGetData(tz2))) return false; | |
437 | return true; | |
438 | } | |
439 | ||
440 | static CFHashCode __CFTimeZoneHash(CFTypeRef cf) { | |
441 | CFTimeZoneRef tz = (CFTimeZoneRef)cf; | |
442 | return CFHash(CFTimeZoneGetName(tz)); | |
443 | } | |
444 | ||
445 | static CFStringRef __CFTimeZoneCopyDescription(CFTypeRef cf) { | |
446 | CFTimeZoneRef tz = (CFTimeZoneRef)cf; | |
447 | CFStringRef result, abbrev; | |
448 | CFAbsoluteTime at; | |
449 | at = CFAbsoluteTimeGetCurrent(); | |
450 | abbrev = CFTimeZoneCopyAbbreviation(tz, at); | |
451 | 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"); | |
452 | CFRelease(abbrev); | |
453 | return result; | |
454 | } | |
455 | ||
456 | static void __CFTimeZoneDeallocate(CFTypeRef cf) { | |
457 | CFTimeZoneRef tz = (CFTimeZoneRef)cf; | |
458 | CFAllocatorRef allocator = CFGetAllocator(tz); | |
459 | CFIndex idx; | |
460 | if (tz->_name) CFRelease(tz->_name); | |
461 | if (tz->_data) CFRelease(tz->_data); | |
462 | for (idx = 0; idx < tz->_periodCnt; idx++) { | |
463 | if (NULL != tz->_periods[idx].abbrev) CFRelease(tz->_periods[idx].abbrev); | |
464 | } | |
465 | if (NULL != tz->_periods) CFAllocatorDeallocate(allocator, tz->_periods); | |
466 | } | |
467 | ||
468 | static CFTypeID __kCFTimeZoneTypeID = _kCFRuntimeNotATypeID; | |
469 | ||
470 | static const CFRuntimeClass __CFTimeZoneClass = { | |
471 | 0, | |
472 | "CFTimeZone", | |
473 | NULL, // init | |
474 | NULL, // copy | |
475 | __CFTimeZoneDeallocate, | |
476 | __CFTimeZoneEqual, | |
477 | __CFTimeZoneHash, | |
478 | NULL, // | |
479 | __CFTimeZoneCopyDescription | |
480 | }; | |
481 | ||
bd5b749c | 482 | CFTypeID CFTimeZoneGetTypeID(void) { |
d7384798 A |
483 | static dispatch_once_t initOnce; |
484 | dispatch_once(&initOnce, ^{ __kCFTimeZoneTypeID = _CFRuntimeRegisterClass(&__CFTimeZoneClass); }); | |
bd5b749c A |
485 | return __kCFTimeZoneTypeID; |
486 | } | |
487 | ||
8ca704e1 | 488 | #if DEPLOYMENT_TARGET_WINDOWS |
cf7d2af9 A |
489 | static const char *__CFTimeZoneWinToOlsonDefaults = |
490 | /* Mappings to time zones in Windows Registry are best-guess */ | |
491 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | |
492 | " <!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">" | |
493 | " <plist version=\"1.0\">" | |
494 | " <dict>" | |
495 | " <key>Afghanistan</key> <string>Asia/Kabul</string>" | |
496 | " <key>Afghanistan Standard Time</key> <string>Asia/Kabul</string>" | |
497 | " <key>Alaskan</key> <string>America/Anchorage</string>" | |
498 | " <key>Alaskan Standard Time</key> <string>America/Anchorage</string>" | |
499 | " <key>Arab</key> <string>Asia/Riyadh</string>" | |
500 | " <key>Arab Standard Time</key> <string>Asia/Riyadh</string>" | |
501 | " <key>Arabian</key> <string>Asia/Muscat</string>" | |
502 | " <key>Arabian Standard Time</key> <string>Asia/Muscat</string>" | |
503 | " <key>Arabic Standard Time</key> <string>Asia/Baghdad</string>" | |
504 | " <key>Atlantic</key> <string>America/Halifax</string>" | |
505 | " <key>Atlantic Standard Time</key> <string>America/Halifax</string>" | |
506 | " <key>AUS Central</key> <string>Australia/Darwin</string>" | |
507 | " <key>AUS Central Standard Time</key> <string>Australia/Darwin</string>" | |
508 | " <key>AUS Eastern</key> <string>Australia/Sydney</string>" | |
509 | " <key>AUS Eastern Standard Time</key> <string>Australia/Sydney</string>" | |
510 | " <key>Azerbaijan Standard Time</key> <string>Asia/Baku</string>" | |
511 | " <key>Azores</key> <string>Atlantic/Azores</string>" | |
512 | " <key>Azores Standard Time</key> <string>Atlantic/Azores</string>" | |
513 | " <key>Bangkok</key> <string>Asia/Bangkok</string>" | |
514 | " <key>Bangkok Standard Time</key> <string>Asia/Bangkok</string>" | |
515 | " <key>Beijing</key> <string>Asia/Shanghai</string>" | |
516 | " <key>Canada Central</key> <string>America/Regina</string>" | |
517 | " <key>Canada Central Standard Time</key> <string>America/Regina</string>" | |
518 | " <key>Cape Verde Standard Time</key> <string>Atlantic/Cape_Verde</string>" | |
519 | " <key>Caucasus</key> <string>Asia/Yerevan</string>" | |
520 | " <key>Caucasus Standard Time</key> <string>Asia/Yerevan</string>" | |
521 | " <key>Cen. Australia</key> <string>Australia/Adelaide</string>" | |
522 | " <key>Cen. Australia Standard Time</key> <string>Australia/Adelaide</string>" | |
523 | " <key>Central</key> <string>America/Chicago</string>" | |
524 | " <key>Central America Standard Time</key> <string>America/Regina</string>" | |
525 | " <key>Central Asia</key> <string>Asia/Dhaka</string>" | |
526 | " <key>Central Asia Standard Time</key> <string>Asia/Dhaka</string>" | |
527 | " <key>Central Brazilian Standard Time</key> <string>America/Manaus</string>" | |
528 | " <key>Central Europe</key> <string>Europe/Prague</string>" | |
529 | " <key>Central Europe Standard Time</key> <string>Europe/Prague</string>" | |
530 | " <key>Central European</key> <string>Europe/Belgrade</string>" | |
531 | " <key>Central European Standard Time</key> <string>Europe/Belgrade</string>" | |
532 | " <key>Central Pacific</key> <string>Pacific/Guadalcanal</string>" | |
533 | " <key>Central Pacific Standard Time</key> <string>Pacific/Guadalcanal</string>" | |
534 | " <key>Central Standard Time</key> <string>America/Chicago</string>" | |
535 | " <key>Central Standard Time (Mexico)</key> <string>America/Mexico_City</string>" | |
536 | " <key>China</key> <string>Asia/Shanghai</string>" | |
537 | " <key>China Standard Time</key> <string>Asia/Shanghai</string>" | |
538 | " <key>Dateline</key> <string>GMT-1200</string>" | |
539 | " <key>Dateline Standard Time</key> <string>GMT-1200</string>" | |
540 | " <key>E. Africa</key> <string>Africa/Nairobi</string>" | |
541 | " <key>E. Africa Standard Time</key> <string>Africa/Nairobi</string>" | |
542 | " <key>E. Australia</key> <string>Australia/Brisbane</string>" | |
543 | " <key>E. Australia Standard Time</key> <string>Australia/Brisbane</string>" | |
544 | " <key>E. Europe</key> <string>Europe/Minsk</string>" | |
545 | " <key>E. Europe Standard Time</key> <string>Europe/Minsk</string>" | |
546 | " <key>E. South America</key> <string>America/Sao_Paulo</string>" | |
547 | " <key>E. South America Standard Time</key> <string>America/Sao_Paulo</string>" | |
548 | " <key>Eastern</key> <string>America/New_York</string>" | |
549 | " <key>Eastern Standard Time</key> <string>America/New_York</string>" | |
550 | " <key>Egypt</key> <string>Africa/Cairo</string>" | |
551 | " <key>Egypt Standard Time</key> <string>Africa/Cairo</string>" | |
552 | " <key>Ekaterinburg</key> <string>Asia/Yekaterinburg</string>" | |
553 | " <key>Ekaterinburg Standard Time</key> <string>Asia/Yekaterinburg</string>" | |
554 | " <key>Fiji</key> <string>Pacific/Fiji</string>" | |
555 | " <key>Fiji Standard Time</key> <string>Pacific/Fiji</string>" | |
556 | " <key>FLE</key> <string>Europe/Helsinki</string>" | |
557 | " <key>FLE Standard Time</key> <string>Europe/Helsinki</string>" | |
558 | " <key>Georgian Standard Time</key> <string>Asia/Tbilisi</string>" | |
559 | " <key>GFT</key> <string>Europe/Athens</string>" | |
560 | " <key>GFT Standard Time</key> <string>Europe/Athens</string>" | |
561 | " <key>GMT</key> <string>Europe/London</string>" | |
562 | " <key>GMT Standard Time</key> <string>Europe/London</string>" | |
563 | " <key>Greenland Standard Time</key> <string>America/Godthab</string>" | |
564 | " <key>Greenwich</key> <string>GMT</string>" | |
565 | " <key>Greenwich Standard Time</key> <string>GMT</string>" | |
566 | " <key>GTB</key> <string>Europe/Athens</string>" | |
567 | " <key>GTB Standard Time</key> <string>Europe/Athens</string>" | |
568 | " <key>Hawaiian</key> <string>Pacific/Honolulu</string>" | |
569 | " <key>Hawaiian Standard Time</key> <string>Pacific/Honolulu</string>" | |
570 | " <key>India</key> <string>Asia/Calcutta</string>" | |
571 | " <key>India Standard Time</key> <string>Asia/Calcutta</string>" | |
572 | " <key>Iran</key> <string>Asia/Tehran</string>" | |
573 | " <key>Iran Standard Time</key> <string>Asia/Tehran</string>" | |
574 | " <key>Israel</key> <string>Asia/Jerusalem</string>" | |
575 | " <key>Israel Standard Time</key> <string>Asia/Jerusalem</string>" | |
576 | " <key>Jordan Standard Time</key> <string>Asia/Amman</string>" | |
577 | " <key>Korea</key> <string>Asia/Seoul</string>" | |
578 | " <key>Korea Standard Time</key> <string>Asia/Seoul</string>" | |
579 | " <key>Mexico</key> <string>America/Mexico_City</string>" | |
580 | " <key>Mexico Standard Time</key> <string>America/Mexico_City</string>" | |
581 | " <key>Mexico Standard Time 2</key> <string>America/Chihuahua</string>" | |
582 | " <key>Mid-Atlantic</key> <string>Atlantic/South_Georgia</string>" | |
583 | " <key>Mid-Atlantic Standard Time</key> <string>Atlantic/South_Georgia</string>" | |
584 | " <key>Middle East Standard Time</key> <string>Asia/Beirut</string>" | |
585 | " <key>Mountain</key> <string>America/Denver</string>" | |
586 | " <key>Mountain Standard Time</key> <string>America/Denver</string>" | |
587 | " <key>Mountain Standard Time (Mexico)</key> <string>America/Chihuahua</string>" | |
588 | " <key>Myanmar Standard Time</key> <string>Asia/Rangoon</string>" | |
589 | " <key>N. Central Asia Standard Time</key> <string>Asia/Novosibirsk</string>" | |
590 | " <key>Namibia Standard Time</key> <string>Africa/Windhoek</string>" | |
591 | " <key>Nepal Standard Time</key> <string>Asia/Katmandu</string>" | |
592 | " <key>New Zealand</key> <string>Pacific/Auckland</string>" | |
593 | " <key>New Zealand Standard Time</key> <string>Pacific/Auckland</string>" | |
594 | " <key>Newfoundland</key> <string>America/St_Johns</string>" | |
595 | " <key>Newfoundland Standard Time</key> <string>America/St_Johns</string>" | |
596 | " <key>North Asia East Standard Time</key> <string>Asia/Ulaanbaatar</string>" | |
597 | " <key>North Asia Standard Time</key> <string>Asia/Krasnoyarsk</string>" | |
598 | " <key>Pacific</key> <string>America/Los_Angeles</string>" | |
599 | " <key>Pacific SA</key> <string>America/Santiago</string>" | |
600 | " <key>Pacific SA Standard Time</key> <string>America/Santiago</string>" | |
601 | " <key>Pacific Standard Time</key> <string>America/Los_Angeles</string>" | |
602 | " <key>Pacific Standard Time (Mexico)</key> <string>America/Tijuana</string>" | |
603 | " <key>Prague Bratislava</key> <string>Europe/Prague</string>" | |
604 | " <key>Romance</key> <string>Europe/Paris</string>" | |
605 | " <key>Romance Standard Time</key> <string>Europe/Paris</string>" | |
606 | " <key>Russian</key> <string>Europe/Moscow</string>" | |
607 | " <key>Russian Standard Time</key> <string>Europe/Moscow</string>" | |
608 | " <key>SA Eastern</key> <string>America/Buenos_Aires</string>" | |
609 | " <key>SA Eastern Standard Time</key> <string>America/Buenos_Aires</string>" | |
610 | " <key>SA Pacific</key> <string>America/Bogota</string>" | |
611 | " <key>SA Pacific Standard Time</key> <string>America/Bogota</string>" | |
612 | " <key>SA Western</key> <string>America/Caracas</string>" | |
613 | " <key>SA Western Standard Time</key> <string>America/Caracas</string>" | |
614 | " <key>Samoa</key> <string>Pacific/Apia</string>" | |
615 | " <key>Samoa Standard Time</key> <string>Pacific/Apia</string>" | |
616 | " <key>Saudi Arabia</key> <string>Asia/Riyadh</string>" | |
617 | " <key>Saudi Arabia Standard Time</key> <string>Asia/Riyadh</string>" | |
618 | " <key>SE Asia Standard Time</key> <string>Asia/Bangkok</string>" | |
619 | " <key>Singapore</key> <string>Asia/Singapore</string>" | |
620 | " <key>Singapore Standard Time</key> <string>Asia/Singapore</string>" | |
621 | " <key>South Africa</key> <string>Africa/Harare</string>" | |
622 | " <key>South Africa Standard Time</key> <string>Africa/Harare</string>" | |
623 | " <key>Sri Lanka</key> <string>Asia/Colombo</string>" | |
624 | " <key>Sri Lanka Standard Time</key> <string>Asia/Colombo</string>" | |
625 | " <key>Sydney Standard Time</key> <string>Australia/Sydney</string>" | |
626 | " <key>Taipei</key> <string>Asia/Taipei</string>" | |
627 | " <key>Taipei Standard Time</key> <string>Asia/Taipei</string>" | |
628 | " <key>Tasmania</key> <string>Australia/Hobart</string>" | |
629 | " <key>Tasmania Standard Time</key> <string>Australia/Hobart</string>" | |
630 | " <key>Tasmania Standard Time</key> <string>Australia/Hobart</string>" | |
631 | " <key>Tokyo</key> <string>Asia/Tokyo</string>" | |
632 | " <key>Tokyo Standard Time</key> <string>Asia/Tokyo</string>" | |
633 | " <key>Tonga Standard Time</key> <string>Pacific/Tongatapu</string>" | |
634 | " <key>US Eastern</key> <string>America/Indianapolis</string>" | |
635 | " <key>US Eastern Standard Time</key> <string>America/Indianapolis</string>" | |
636 | " <key>US Mountain</key> <string>America/Phoenix</string>" | |
637 | " <key>US Mountain Standard Time</key> <string>America/Phoenix</string>" | |
638 | " <key>Vladivostok</key> <string>Asia/Vladivostok</string>" | |
639 | " <key>Vladivostok Standard Time</key> <string>Asia/Vladivostok</string>" | |
640 | " <key>W. Australia</key> <string>Australia/Perth</string>" | |
641 | " <key>W. Australia Standard Time</key> <string>Australia/Perth</string>" | |
642 | " <key>W. Central Africa Standard Time</key> <string>Africa/Luanda</string>" | |
643 | " <key>W. Europe</key> <string>Europe/Berlin</string>" | |
644 | " <key>W. Europe Standard Time</key> <string>Europe/Berlin</string>" | |
645 | " <key>Warsaw</key> <string>Europe/Warsaw</string>" | |
646 | " <key>West Asia</key> <string>Asia/Karachi</string>" | |
647 | " <key>West Asia Standard Time</key> <string>Asia/Karachi</string>" | |
648 | " <key>West Pacific</key> <string>Pacific/Guam</string>" | |
649 | " <key>West Pacific Standard Time</key> <string>Pacific/Guam</string>" | |
650 | " <key>Western Brazilian Standard Time</key> <string>America/Rio_Branco</string>" | |
651 | " <key>Yakutsk</key> <string>Asia/Yakutsk</string>" | |
652 | " </dict>" | |
653 | " </plist>"; | |
bd5b749c | 654 | |
cf7d2af9 | 655 | CF_INLINE void __CFTimeZoneLockWinToOlson(void) { |
d7384798 | 656 | __CFLock(&__CFTimeZoneWinToOlsonLock); |
cf7d2af9 A |
657 | } |
658 | ||
659 | CF_INLINE void __CFTimeZoneUnlockWinToOlson(void) { | |
d7384798 | 660 | __CFUnlock(&__CFTimeZoneWinToOlsonLock); |
cf7d2af9 A |
661 | } |
662 | ||
663 | CFDictionaryRef CFTimeZoneCopyWinToOlsonDictionary(void) { | |
664 | CFDictionaryRef dict; | |
665 | __CFTimeZoneLockWinToOlson(); | |
666 | if (NULL == __CFTimeZoneWinToOlsonDict) { | |
667 | CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)__CFTimeZoneWinToOlsonDefaults, strlen(__CFTimeZoneWinToOlsonDefaults)); | |
668 | __CFTimeZoneWinToOlsonDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); | |
669 | CFRelease(data); | |
670 | } | |
671 | if (NULL == __CFTimeZoneWinToOlsonDict) { | |
672 | __CFTimeZoneWinToOlsonDict = CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, NULL, NULL); | |
673 | } | |
674 | dict = __CFTimeZoneWinToOlsonDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneWinToOlsonDict) : NULL; | |
675 | __CFTimeZoneUnlockWinToOlson(); | |
676 | return dict; | |
677 | } | |
678 | ||
679 | void CFTimeZoneSetWinToOlsonDictionary(CFDictionaryRef dict) { | |
680 | __CFGenericValidateType(dict, CFDictionaryGetTypeID()); | |
681 | __CFTimeZoneLockWinToOlson(); | |
682 | if (dict != __CFTimeZoneWinToOlsonDict) { | |
683 | if (dict) CFRetain(dict); | |
684 | if (__CFTimeZoneWinToOlsonDict) CFRelease(__CFTimeZoneWinToOlsonDict); | |
685 | __CFTimeZoneWinToOlsonDict = dict; | |
686 | } | |
687 | __CFTimeZoneUnlockWinToOlson(); | |
688 | } | |
689 | ||
690 | CFTimeZoneRef CFTimeZoneCreateWithWindowsName(CFAllocatorRef allocator, CFStringRef winName) { | |
691 | if (!winName) return NULL; | |
692 | ||
693 | CFDictionaryRef winToOlson = CFTimeZoneCopyWinToOlsonDictionary(); | |
694 | if (!winToOlson) return NULL; | |
695 | ||
696 | CFStringRef olsonName = CFDictionaryGetValue(winToOlson, winName); | |
697 | CFTimeZoneRef retval = NULL; | |
698 | if (olsonName) { | |
699 | retval = CFTimeZoneCreateWithName(allocator, olsonName, false); | |
700 | } | |
701 | CFRelease(winToOlson); | |
702 | return retval; | |
703 | } | |
bd5b749c | 704 | |
cf7d2af9 A |
705 | extern CFStringRef _CFGetWindowsAppleSystemLibraryDirectory(void); |
706 | void __InitTZStrings(void) { | |
d7384798 A |
707 | static CFLock_t __CFTZDirLock = CFLockInit; |
708 | __CFLock(&__CFTZDirLock); | |
cf7d2af9 A |
709 | if (!__tzZoneInfo) { |
710 | CFStringRef winDir = _CFGetWindowsAppleSystemLibraryDirectory(); | |
711 | __tzZoneInfo = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@\\etc\\zoneinfo"), winDir); | |
712 | } | |
713 | if (!__tzDir && __tzZoneInfo) { | |
714 | int length = CFStringGetLength(__tzZoneInfo) + sizeof("\\zone.tab") + 1; | |
715 | __tzDir = malloc(length); // If we don't use ascii, we'll need to malloc more space | |
716 | if (!__tzDir || !CFStringGetCString(__tzZoneInfo, __tzDir, length, kCFStringEncodingASCII)) { | |
717 | free(__tzDir); | |
718 | } else { | |
719 | strcat(__tzDir, "\\zone.tab"); | |
720 | } | |
721 | } | |
d7384798 | 722 | __CFUnlock(&__CFTZDirLock); |
cf7d2af9 A |
723 | } |
724 | #endif | |
725 | ||
cf7d2af9 A |
726 | static CFTimeZoneRef __CFTimeZoneCreateSystem(void) { |
727 | CFTimeZoneRef result = NULL; | |
728 | ||
8ca704e1 | 729 | #if DEPLOYMENT_TARGET_WINDOWS |
cf7d2af9 A |
730 | CFStringRef name = NULL; |
731 | TIME_ZONE_INFORMATION tzi = { 0 }; | |
732 | DWORD rval = GetTimeZoneInformation(&tzi); | |
733 | if (rval != TIME_ZONE_ID_INVALID) { | |
734 | LPWSTR standardName = (LPWSTR)&tzi.StandardName; | |
735 | CFStringRef cfStandardName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (UInt8 *)standardName, wcslen(standardName)*sizeof(WCHAR), kCFStringEncodingUTF16LE, false); | |
736 | if (cfStandardName) { | |
737 | CFDictionaryRef winToOlson = CFTimeZoneCopyWinToOlsonDictionary(); | |
738 | if (winToOlson) { | |
739 | name = CFDictionaryGetValue(winToOlson, cfStandardName); | |
740 | if (name) CFRetain(name); | |
741 | CFRelease(winToOlson); | |
742 | } | |
743 | CFRelease(cfStandardName); | |
744 | } | |
745 | } else { | |
746 | CFLog(kCFLogLevelError, CFSTR("Couldn't get time zone information error %d"), GetLastError()); | |
747 | } | |
748 | if (name) { | |
749 | #else | |
750 | const char *tzenv; | |
bd5b749c A |
751 | int ret; |
752 | char linkbuf[CFMaxPathSize]; | |
753 | ||
cf7d2af9 | 754 | tzenv = __CFgetenv("TZFILE"); |
bd5b749c A |
755 | if (NULL != tzenv) { |
756 | CFStringRef name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)tzenv, strlen(tzenv), kCFStringEncodingUTF8, false); | |
757 | result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, false); | |
758 | CFRelease(name); | |
759 | if (result) return result; | |
760 | } | |
cf7d2af9 | 761 | tzenv = __CFgetenv("TZ"); |
bd5b749c A |
762 | if (NULL != tzenv) { |
763 | CFStringRef name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)tzenv, strlen(tzenv), kCFStringEncodingUTF8, false); | |
764 | result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, true); | |
765 | CFRelease(name); | |
766 | if (result) return result; | |
767 | } | |
768 | ret = readlink(TZZONELINK, linkbuf, sizeof(linkbuf)); | |
769 | if (0 < ret) { | |
770 | CFStringRef name; | |
771 | linkbuf[ret] = '\0'; | |
772 | if (strncmp(linkbuf, TZZONEINFO, sizeof(TZZONEINFO) - 1) == 0) { | |
773 | name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf + sizeof(TZZONEINFO) - 1, strlen(linkbuf) - sizeof(TZZONEINFO) + 1, kCFStringEncodingUTF8, false); | |
774 | } else { | |
775 | name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf, strlen(linkbuf), kCFStringEncodingUTF8, false); | |
776 | } | |
cf7d2af9 A |
777 | #endif |
778 | ||
bd5b749c A |
779 | result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, false); |
780 | CFRelease(name); | |
781 | if (result) return result; | |
782 | } | |
783 | return CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorSystemDefault, 0.0); | |
784 | } | |
bd5b749c A |
785 | |
786 | CFTimeZoneRef CFTimeZoneCopySystem(void) { | |
787 | CFTimeZoneRef tz; | |
788 | __CFTimeZoneLockGlobal(); | |
789 | if (NULL == __CFTimeZoneSystem) { | |
790 | __CFTimeZoneUnlockGlobal(); | |
791 | tz = __CFTimeZoneCreateSystem(); | |
792 | __CFTimeZoneLockGlobal(); | |
793 | if (NULL == __CFTimeZoneSystem) { | |
794 | __CFTimeZoneSystem = tz; | |
795 | } else { | |
796 | if (tz) CFRelease(tz); | |
797 | } | |
798 | } | |
799 | tz = __CFTimeZoneSystem ? (CFTimeZoneRef)CFRetain(__CFTimeZoneSystem) : NULL; | |
800 | __CFTimeZoneUnlockGlobal(); | |
801 | return tz; | |
802 | } | |
803 | ||
804 | static CFIndex __noteCount = 0; | |
805 | ||
806 | void CFTimeZoneResetSystem(void) { | |
807 | __CFTimeZoneLockGlobal(); | |
808 | if (__CFTimeZoneDefault == __CFTimeZoneSystem) { | |
809 | if (__CFTimeZoneDefault) CFRelease(__CFTimeZoneDefault); | |
810 | __CFTimeZoneDefault = NULL; | |
811 | } | |
812 | CFTimeZoneRef tz = __CFTimeZoneSystem; | |
813 | __CFTimeZoneSystem = NULL; | |
814 | __CFTimeZoneUnlockGlobal(); | |
815 | if (tz) CFRelease(tz); | |
816 | } | |
817 | ||
818 | CFIndex _CFTimeZoneGetNoteCount(void) { | |
819 | return __noteCount; | |
820 | } | |
821 | ||
822 | CFTimeZoneRef CFTimeZoneCopyDefault(void) { | |
823 | CFTimeZoneRef tz; | |
824 | __CFTimeZoneLockGlobal(); | |
825 | if (NULL == __CFTimeZoneDefault) { | |
826 | __CFTimeZoneUnlockGlobal(); | |
827 | tz = CFTimeZoneCopySystem(); | |
828 | __CFTimeZoneLockGlobal(); | |
829 | if (NULL == __CFTimeZoneDefault) { | |
830 | __CFTimeZoneDefault = tz; | |
831 | } else { | |
832 | if (tz) CFRelease(tz); | |
833 | } | |
834 | } | |
835 | tz = __CFTimeZoneDefault ? (CFTimeZoneRef)CFRetain(__CFTimeZoneDefault) : NULL; | |
836 | __CFTimeZoneUnlockGlobal(); | |
837 | return tz; | |
838 | } | |
839 | ||
840 | void CFTimeZoneSetDefault(CFTimeZoneRef tz) { | |
841 | if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); | |
842 | __CFTimeZoneLockGlobal(); | |
843 | if (tz != __CFTimeZoneDefault) { | |
844 | if (tz) CFRetain(tz); | |
845 | if (__CFTimeZoneDefault) CFRelease(__CFTimeZoneDefault); | |
846 | __CFTimeZoneDefault = tz; | |
847 | } | |
848 | __CFTimeZoneUnlockGlobal(); | |
849 | } | |
850 | ||
851 | static CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary(void); | |
852 | ||
853 | CFArrayRef CFTimeZoneCopyKnownNames(void) { | |
854 | CFArrayRef tzs; | |
855 | __CFTimeZoneLockGlobal(); | |
856 | if (NULL == __CFKnownTimeZoneList) { | |
857 | CFMutableArrayRef list; | |
858 | /* TimeZone information locate in the registry for Win32 | |
859 | * (Aleksey Dukhnyakov) | |
860 | */ | |
bd5b749c | 861 | list = __CFCopyRecursiveDirectoryList(); |
bd5b749c A |
862 | // Remove undesirable ancient cruft |
863 | CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); | |
864 | CFIndex idx; | |
865 | for (idx = CFArrayGetCount(list); idx--; ) { | |
866 | CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(list, idx); | |
867 | if (CFDictionaryContainsKey(dict, item)) { | |
868 | CFArrayRemoveValueAtIndex(list, idx); | |
869 | } | |
870 | } | |
871 | __CFKnownTimeZoneList = CFArrayCreateCopy(kCFAllocatorSystemDefault, list); | |
872 | CFRelease(list); | |
873 | } | |
874 | tzs = __CFKnownTimeZoneList ? (CFArrayRef)CFRetain(__CFKnownTimeZoneList) : NULL; | |
875 | __CFTimeZoneUnlockGlobal(); | |
876 | return tzs; | |
877 | } | |
878 | ||
bd5b749c A |
879 | /* The criteria here are sort of: coverage for the U.S. and Europe, |
880 | * large cities, abbreviation uniqueness, and perhaps a few others. | |
881 | * But do not make the list too large with obscure information. | |
882 | */ | |
883 | static const char *__CFTimeZoneAbbreviationDefaults = | |
884 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | |
885 | " <!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">" | |
886 | " <plist version=\"1.0\">" | |
887 | " <dict>" | |
888 | " <key>ADT</key> <string>America/Halifax</string>" | |
889 | " <key>AKDT</key> <string>America/Juneau</string>" | |
890 | " <key>AKST</key> <string>America/Juneau</string>" | |
891 | " <key>ART</key> <string>America/Argentina/Buenos_Aires</string>" | |
892 | " <key>AST</key> <string>America/Halifax</string>" | |
893 | " <key>BDT</key> <string>Asia/Dhaka</string>" | |
894 | " <key>BRST</key> <string>America/Sao_Paulo</string>" | |
895 | " <key>BRT</key> <string>America/Sao_Paulo</string>" | |
896 | " <key>BST</key> <string>Europe/London</string>" | |
897 | " <key>CAT</key> <string>Africa/Harare</string>" | |
898 | " <key>CDT</key> <string>America/Chicago</string>" | |
899 | " <key>CEST</key> <string>Europe/Paris</string>" | |
900 | " <key>CET</key> <string>Europe/Paris</string>" | |
901 | " <key>CLST</key> <string>America/Santiago</string>" | |
902 | " <key>CLT</key> <string>America/Santiago</string>" | |
903 | " <key>COT</key> <string>America/Bogota</string>" | |
904 | " <key>CST</key> <string>America/Chicago</string>" | |
905 | " <key>EAT</key> <string>Africa/Addis_Ababa</string>" | |
906 | " <key>EDT</key> <string>America/New_York</string>" | |
907 | " <key>EEST</key> <string>Europe/Istanbul</string>" | |
908 | " <key>EET</key> <string>Europe/Istanbul</string>" | |
909 | " <key>EST</key> <string>America/New_York</string>" | |
910 | " <key>GMT</key> <string>GMT</string>" | |
911 | " <key>GST</key> <string>Asia/Dubai</string>" | |
912 | " <key>HKT</key> <string>Asia/Hong_Kong</string>" | |
913 | " <key>HST</key> <string>Pacific/Honolulu</string>" | |
914 | " <key>ICT</key> <string>Asia/Bangkok</string>" | |
915 | " <key>IRST</key> <string>Asia/Tehran</string>" | |
916 | " <key>IST</key> <string>Asia/Calcutta</string>" | |
917 | " <key>JST</key> <string>Asia/Tokyo</string>" | |
918 | " <key>KST</key> <string>Asia/Seoul</string>" | |
919 | " <key>MDT</key> <string>America/Denver</string>" | |
920 | " <key>MSD</key> <string>Europe/Moscow</string>" | |
921 | " <key>MSK</key> <string>Europe/Moscow</string>" | |
922 | " <key>MST</key> <string>America/Denver</string>" | |
923 | " <key>NZDT</key> <string>Pacific/Auckland</string>" | |
924 | " <key>NZST</key> <string>Pacific/Auckland</string>" | |
925 | " <key>PDT</key> <string>America/Los_Angeles</string>" | |
926 | " <key>PET</key> <string>America/Lima</string>" | |
927 | " <key>PHT</key> <string>Asia/Manila</string>" | |
928 | " <key>PKT</key> <string>Asia/Karachi</string>" | |
929 | " <key>PST</key> <string>America/Los_Angeles</string>" | |
930 | " <key>SGT</key> <string>Asia/Singapore</string>" | |
931 | " <key>UTC</key> <string>UTC</string>" | |
932 | " <key>WAT</key> <string>Africa/Lagos</string>" | |
933 | " <key>WEST</key> <string>Europe/Lisbon</string>" | |
934 | " <key>WET</key> <string>Europe/Lisbon</string>" | |
935 | " <key>WIT</key> <string>Asia/Jakarta</string>" | |
936 | " </dict>" | |
937 | " </plist>"; | |
bd5b749c A |
938 | |
939 | CFDictionaryRef CFTimeZoneCopyAbbreviationDictionary(void) { | |
940 | CFDictionaryRef dict; | |
941 | __CFTimeZoneLockAbbreviations(); | |
942 | if (NULL == __CFTimeZoneAbbreviationDict) { | |
943 | CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)__CFTimeZoneAbbreviationDefaults, strlen(__CFTimeZoneAbbreviationDefaults)); | |
944 | __CFTimeZoneAbbreviationDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); | |
945 | CFRelease(data); | |
946 | } | |
947 | if (NULL == __CFTimeZoneAbbreviationDict) { | |
948 | __CFTimeZoneAbbreviationDict = CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, NULL, NULL); | |
949 | } | |
950 | dict = __CFTimeZoneAbbreviationDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneAbbreviationDict) : NULL; | |
951 | __CFTimeZoneUnlockAbbreviations(); | |
952 | return dict; | |
953 | } | |
954 | ||
8ca704e1 A |
955 | void _removeFromCache(const void *key, const void *value, void *context) { |
956 | CFDictionaryRemoveValue(__CFTimeZoneCache, (CFStringRef)key); | |
957 | } | |
958 | ||
bd5b749c A |
959 | void CFTimeZoneSetAbbreviationDictionary(CFDictionaryRef dict) { |
960 | __CFGenericValidateType(dict, CFDictionaryGetTypeID()); | |
961 | __CFTimeZoneLockGlobal(); | |
962 | if (dict != __CFTimeZoneAbbreviationDict) { | |
963 | if (dict) CFRetain(dict); | |
964 | if (__CFTimeZoneAbbreviationDict) { | |
8ca704e1 | 965 | CFDictionaryApplyFunction(__CFTimeZoneAbbreviationDict, _removeFromCache, NULL); |
bd5b749c A |
966 | CFRelease(__CFTimeZoneAbbreviationDict); |
967 | } | |
968 | __CFTimeZoneAbbreviationDict = dict; | |
969 | } | |
970 | __CFTimeZoneUnlockGlobal(); | |
971 | } | |
972 | ||
973 | CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { | |
974 | // assert: (NULL != name && NULL != data); | |
975 | CFTimeZoneRef memory; | |
976 | uint32_t size; | |
977 | CFTZPeriod *tzp = NULL; | |
978 | CFIndex idx, cnt = 0; | |
979 | ||
980 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
981 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
982 | __CFGenericValidateType(name, CFStringGetTypeID()); | |
983 | __CFGenericValidateType(data, CFDataGetTypeID()); | |
984 | __CFTimeZoneLockGlobal(); | |
985 | if (NULL != __CFTimeZoneCache && CFDictionaryGetValueIfPresent(__CFTimeZoneCache, name, (const void **)&memory)) { | |
986 | __CFTimeZoneUnlockGlobal(); | |
987 | return (CFTimeZoneRef)CFRetain(memory); | |
988 | } | |
989 | if (!__CFParseTimeZoneData(allocator, data, &tzp, &cnt)) { | |
990 | __CFTimeZoneUnlockGlobal(); | |
991 | return NULL; | |
992 | } | |
993 | size = sizeof(struct __CFTimeZone) - sizeof(CFRuntimeBase); | |
994 | memory = (CFTimeZoneRef)_CFRuntimeCreateInstance(allocator, CFTimeZoneGetTypeID(), size, NULL); | |
995 | if (NULL == memory) { | |
996 | __CFTimeZoneUnlockGlobal(); | |
997 | for (idx = 0; idx < cnt; idx++) { | |
998 | if (NULL != tzp[idx].abbrev) CFRelease(tzp[idx].abbrev); | |
999 | } | |
1000 | if (NULL != tzp) CFAllocatorDeallocate(allocator, tzp); | |
1001 | return NULL; | |
1002 | } | |
1003 | ((struct __CFTimeZone *)memory)->_name = (CFStringRef)CFStringCreateCopy(allocator, name); | |
1004 | ((struct __CFTimeZone *)memory)->_data = CFDataCreateCopy(allocator, data); | |
1005 | ((struct __CFTimeZone *)memory)->_periods = tzp; | |
1006 | ((struct __CFTimeZone *)memory)->_periodCnt = cnt; | |
1007 | if (NULL == __CFTimeZoneCache) { | |
1008 | __CFTimeZoneCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1009 | } | |
1010 | CFDictionaryAddValue(__CFTimeZoneCache, ((struct __CFTimeZone *)memory)->_name, memory); | |
1011 | __CFTimeZoneUnlockGlobal(); | |
1012 | return memory; | |
1013 | } | |
1014 | ||
bd5b749c A |
1015 | static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t seconds, CFStringRef name, int isDST) { |
1016 | CFTimeZoneRef result; | |
1017 | CFDataRef data; | |
1018 | int32_t nameLen = CFStringGetLength(name); | |
1019 | unsigned char dataBytes[52 + nameLen + 1]; | |
1020 | memset(dataBytes, 0, sizeof(dataBytes)); | |
1021 | ||
1022 | // Put in correct magic bytes for timezone structures | |
1023 | dataBytes[0] = 'T'; | |
1024 | dataBytes[1] = 'Z'; | |
1025 | dataBytes[2] = 'i'; | |
1026 | dataBytes[3] = 'f'; | |
1027 | ||
1028 | __CFEntzcode(1, dataBytes + 20); | |
1029 | __CFEntzcode(1, dataBytes + 24); | |
1030 | __CFEntzcode(1, dataBytes + 36); | |
1031 | __CFEntzcode(nameLen + 1, dataBytes + 40); | |
1032 | __CFEntzcode(seconds, dataBytes + 44); | |
1033 | dataBytes[48] = isDST ? 1 : 0; | |
1034 | CFStringGetCString(name, (char *)dataBytes + 50, nameLen + 1, kCFStringEncodingASCII); | |
1035 | data = CFDataCreate(allocator, dataBytes, 52 + nameLen + 1); | |
1036 | result = CFTimeZoneCreate(allocator, name, data); | |
1037 | CFRelease(data); | |
1038 | return result; | |
1039 | } | |
bd5b749c | 1040 | |
bd5b749c A |
1041 | |
1042 | // rounds offset to nearest minute | |
1043 | CFTimeZoneRef CFTimeZoneCreateWithTimeIntervalFromGMT(CFAllocatorRef allocator, CFTimeInterval ti) { | |
1044 | CFTimeZoneRef result; | |
1045 | CFStringRef name; | |
1046 | int32_t seconds, minute, hour; | |
1047 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
1048 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
1049 | if (ti < -18.0 * 3600 || 18.0 * 3600 < ti) return NULL; | |
1050 | ti = (ti < 0.0) ? ceil((ti / 60.0) - 0.5) * 60.0 : floor((ti / 60.0) + 0.5) * 60.0; | |
1051 | seconds = (int32_t)ti; | |
1052 | hour = (ti < 0) ? (-seconds / 3600) : (seconds / 3600); | |
1053 | seconds -= ((ti < 0) ? -hour : hour) * 3600; | |
1054 | minute = (ti < 0) ? (-seconds / 60) : (seconds / 60); | |
1055 | if (fabs(ti) < 1.0) { | |
1056 | name = (CFStringRef)CFRetain(CFSTR("GMT")); | |
1057 | } else { | |
1058 | name = CFStringCreateWithFormat(allocator, NULL, CFSTR("GMT%c%02d%02d"), (ti < 0.0 ? '-' : '+'), hour, minute); | |
1059 | } | |
1060 | result = __CFTimeZoneCreateFixed(allocator, (int32_t)ti, name, 0); | |
1061 | CFRelease(name); | |
1062 | return result; | |
1063 | } | |
1064 | ||
1065 | CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef name, Boolean tryAbbrev) { | |
1066 | CFTimeZoneRef result = NULL; | |
1067 | CFStringRef tzName = NULL; | |
1068 | CFDataRef data = NULL; | |
1069 | ||
1070 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
1071 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
1072 | __CFGenericValidateType(name, CFStringGetTypeID()); | |
1073 | if (CFEqual(CFSTR(""), name)) { | |
1074 | // empty string is not a time zone name, just abort now, | |
1075 | // following stuff will fail anyway | |
1076 | return NULL; | |
1077 | } | |
1078 | __CFTimeZoneLockGlobal(); | |
1079 | if (NULL != __CFTimeZoneCache && CFDictionaryGetValueIfPresent(__CFTimeZoneCache, name, (const void **)&result)) { | |
1080 | __CFTimeZoneUnlockGlobal(); | |
1081 | return (CFTimeZoneRef)CFRetain(result); | |
1082 | } | |
1083 | __CFTimeZoneUnlockGlobal(); | |
cf7d2af9 A |
1084 | CFIndex len = CFStringGetLength(name); |
1085 | if (6 == len || 8 == len) { | |
1086 | UniChar buffer[8]; | |
1087 | CFStringGetCharacters(name, CFRangeMake(0, len), buffer); | |
1088 | if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { | |
1089 | if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { | |
1090 | int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); | |
1091 | if (-14 <= hours && hours <= 14) { | |
1092 | CFTimeInterval ti = hours * 3600.0; | |
1093 | if (6 == len) { | |
1094 | return CFTimeZoneCreateWithTimeIntervalFromGMT(allocator, ('-' == buffer[3] ? -1.0 : 1.0) * ti); | |
1095 | } else { | |
1096 | if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { | |
1097 | int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); | |
1098 | if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { | |
1099 | ti = ti + minutes * 60.0; | |
1100 | return CFTimeZoneCreateWithTimeIntervalFromGMT(allocator, ('-' == buffer[3] ? -1.0 : 1.0) * ti); | |
1101 | } | |
1102 | } | |
1103 | } | |
1104 | } | |
1105 | } | |
1106 | } | |
1107 | } | |
bd5b749c A |
1108 | CFURLRef baseURL, tempURL; |
1109 | void *bytes; | |
1110 | CFIndex length; | |
1111 | ||
8ca704e1 | 1112 | #if DEPLOYMENT_TARGET_WINDOWS |
cf7d2af9 A |
1113 | if (!__tzZoneInfo) __InitTZStrings(); |
1114 | if (!__tzZoneInfo) return NULL; | |
1115 | baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); | |
1116 | #else | |
bd5b749c | 1117 | baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR(TZZONEINFO), kCFURLPOSIXPathStyle, true); |
cf7d2af9 | 1118 | #endif |
bd5b749c A |
1119 | if (tryAbbrev) { |
1120 | CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); | |
1121 | tzName = CFDictionaryGetValue(abbrevs, name); | |
1122 | if (NULL != tzName) { | |
1123 | tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); | |
1124 | if (NULL != tempURL) { | |
856091c5 | 1125 | if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { |
bd5b749c A |
1126 | data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); |
1127 | } | |
1128 | CFRelease(tempURL); | |
1129 | } | |
1130 | } | |
1131 | CFRelease(abbrevs); | |
1132 | } | |
1133 | if (NULL == data) { | |
1134 | CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); | |
1135 | CFStringRef mapping = CFDictionaryGetValue(dict, name); | |
1136 | if (mapping) { | |
1137 | name = mapping; | |
8ca704e1 | 1138 | #if DEPLOYMENT_TARGET_WINDOWS |
cf7d2af9 A |
1139 | } else if (CFStringHasPrefix(name, __tzZoneInfo)) { |
1140 | CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); | |
1141 | CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); | |
1142 | #else | |
bd5b749c A |
1143 | } else if (CFStringHasPrefix(name, CFSTR(TZZONEINFO))) { |
1144 | CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); | |
1145 | CFStringDelete(unprefixed, CFRangeMake(0, sizeof(TZZONEINFO))); | |
cf7d2af9 | 1146 | #endif |
bd5b749c A |
1147 | mapping = CFDictionaryGetValue(dict, unprefixed); |
1148 | if (mapping) { | |
1149 | name = mapping; | |
1150 | } | |
1151 | CFRelease(unprefixed); | |
1152 | } | |
1153 | CFRelease(dict); | |
1154 | if (CFEqual(CFSTR(""), name)) { | |
1155 | return NULL; | |
1156 | } | |
1157 | } | |
1158 | if (NULL == data) { | |
1159 | tzName = name; | |
1160 | tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); | |
1161 | if (NULL != tempURL) { | |
856091c5 | 1162 | if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { |
bd5b749c A |
1163 | data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); |
1164 | } | |
1165 | CFRelease(tempURL); | |
1166 | } | |
1167 | } | |
1168 | CFRelease(baseURL); | |
1169 | if (NULL != data) { | |
1170 | result = CFTimeZoneCreate(allocator, tzName, data); | |
1171 | if (name != tzName) { | |
1172 | CFStringRef nameCopy = (CFStringRef)CFStringCreateCopy(allocator, name); | |
1173 | __CFTimeZoneLockGlobal(); | |
1174 | CFDictionaryAddValue(__CFTimeZoneCache, nameCopy, result); | |
1175 | __CFTimeZoneUnlockGlobal(); | |
1176 | CFRelease(nameCopy); | |
1177 | } | |
1178 | CFRelease(data); | |
1179 | } | |
1180 | return result; | |
1181 | } | |
bd5b749c A |
1182 | |
1183 | CFStringRef CFTimeZoneGetName(CFTimeZoneRef tz) { | |
856091c5 | 1184 | CF_OBJC_FUNCDISPATCHV(CFTimeZoneGetTypeID(), CFStringRef, (NSTimeZone *)tz, name); |
bd5b749c A |
1185 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1186 | return tz->_name; | |
1187 | } | |
1188 | ||
1189 | CFDataRef CFTimeZoneGetData(CFTimeZoneRef tz) { | |
856091c5 | 1190 | CF_OBJC_FUNCDISPATCHV(CFTimeZoneGetTypeID(), CFDataRef, (NSTimeZone *)tz, data); |
bd5b749c A |
1191 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1192 | return tz->_data; | |
1193 | } | |
1194 | ||
cf7d2af9 A |
1195 | /* This function converts CFAbsoluteTime to (Win32) SYSTEMTIME |
1196 | * (Aleksey Dukhnyakov) | |
1197 | */ | |
1198 | #if DEPLOYMENT_TARGET_WINDOWS | |
1199 | BOOL __CFTimeZoneGetWin32SystemTime(SYSTEMTIME * sys_time, CFAbsoluteTime time) | |
1200 | { | |
1201 | LONGLONG l; | |
1202 | FILETIME * ftime=(FILETIME*)&l; | |
1203 | ||
1204 | /* seconds between 1601 and 1970 : 11644473600, | |
1205 | * seconds between 1970 and 2001 : 978307200, | |
1206 | * FILETIME - number of 100-nanosecond intervals since January 1, 1601 | |
1207 | */ | |
1208 | l=(LONGLONG)(time+11644473600LL+978307200)*10000000; | |
1209 | if (FileTimeToSystemTime(ftime,sys_time)) | |
1210 | return TRUE; | |
1211 | else | |
1212 | return FALSE; | |
1213 | } | |
cf7d2af9 A |
1214 | #endif |
1215 | ||
bd5b749c | 1216 | CFTimeInterval CFTimeZoneGetSecondsFromGMT(CFTimeZoneRef tz, CFAbsoluteTime at) { |
bd5b749c | 1217 | CFIndex idx; |
bd5b749c A |
1218 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1219 | idx = __CFBSearchTZPeriods(tz, at); | |
1220 | return __CFTZPeriodGMTOffset(&(tz->_periods[idx])); | |
bd5b749c A |
1221 | } |
1222 | ||
1223 | CFStringRef CFTimeZoneCopyAbbreviation(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
1224 | CFStringRef result; | |
1225 | CFIndex idx; | |
bd5b749c | 1226 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
bd5b749c | 1227 | idx = __CFBSearchTZPeriods(tz, at); |
bd5b749c A |
1228 | result = __CFTZPeriodAbbreviation(&(tz->_periods[idx])); |
1229 | return result ? (CFStringRef)CFRetain(result) : NULL; | |
1230 | } | |
1231 | ||
1232 | Boolean CFTimeZoneIsDaylightSavingTime(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
bd5b749c | 1233 | CFIndex idx; |
bd5b749c A |
1234 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1235 | idx = __CFBSearchTZPeriods(tz, at); | |
1236 | return __CFTZPeriodIsDST(&(tz->_periods[idx])); | |
bd5b749c A |
1237 | } |
1238 | ||
1239 | CFTimeInterval CFTimeZoneGetDaylightSavingTimeOffset(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
856091c5 | 1240 | CF_OBJC_FUNCDISPATCHV(CFTimeZoneGetTypeID(), CFTimeInterval, (NSTimeZone *)tz, _daylightSavingTimeOffsetForAbsoluteTime:at); |
bd5b749c A |
1241 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1242 | CFIndex idx = __CFBSearchTZPeriods(tz, at); | |
1243 | if (__CFTZPeriodIsDST(&(tz->_periods[idx]))) { | |
1244 | CFTimeInterval offset = __CFTZPeriodGMTOffset(&(tz->_periods[idx])); | |
1245 | if (idx + 1 < tz->_periodCnt) { | |
1246 | return offset - __CFTZPeriodGMTOffset(&(tz->_periods[idx + 1])); | |
1247 | } else if (0 < idx) { | |
1248 | return offset - __CFTZPeriodGMTOffset(&(tz->_periods[idx - 1])); | |
1249 | } | |
1250 | } | |
1251 | return 0.0; | |
1252 | } | |
1253 | ||
1254 | CFAbsoluteTime CFTimeZoneGetNextDaylightSavingTimeTransition(CFTimeZoneRef tz, CFAbsoluteTime at) { | |
856091c5 | 1255 | CF_OBJC_FUNCDISPATCHV(CFTimeZoneGetTypeID(), CFTimeInterval, (NSTimeZone *)tz, _nextDaylightSavingTimeTransitionAfterAbsoluteTime:at); |
bd5b749c A |
1256 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1257 | CFIndex idx = __CFBSearchTZPeriods(tz, at); | |
1258 | if (tz->_periodCnt <= idx + 1) { | |
1259 | return 0.0; | |
1260 | } | |
1261 | return (CFAbsoluteTime)__CFTZPeriodStartSeconds(&(tz->_periods[idx + 1])); | |
1262 | } | |
1263 | ||
bd5b749c A |
1264 | extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz); |
1265 | ||
1266 | #define BUFFER_SIZE 768 | |
1267 | ||
1268 | CFStringRef CFTimeZoneCopyLocalizedName(CFTimeZoneRef tz, CFTimeZoneNameStyle style, CFLocaleRef locale) { | |
856091c5 | 1269 | CF_OBJC_FUNCDISPATCHV(CFTimeZoneGetTypeID(), CFStringRef, (NSTimeZone *)tz, localizedName:(NSTimeZoneNameStyle)style locale:(NSLocale *)locale); |
bd5b749c A |
1270 | __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); |
1271 | __CFGenericValidateType(locale, CFLocaleGetTypeID()); | |
1272 | ||
cf7d2af9 A |
1273 | if (style == kCFTimeZoneNameStyleGeneric || style == kCFTimeZoneNameStyleShortGeneric) { |
1274 | CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorSystemDefault, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle); | |
1275 | CFDateFormatterSetProperty(df, kCFDateFormatterTimeZone, tz); | |
1276 | CFDateFormatterSetFormat(df, (style == kCFTimeZoneNameStyleGeneric) ? CFSTR("vvvv") : CFSTR("v")); | |
1277 | CFStringRef str = CFDateFormatterCreateStringWithAbsoluteTime(CFGetAllocator(tz), df, 0.0); | |
1278 | CFRelease(df); | |
1279 | return str; | |
1280 | } | |
bd5b749c A |
1281 | |
1282 | CFStringRef localeID = CFLocaleGetIdentifier(locale); | |
1283 | UCalendar *cal = __CFCalendarCreateUCalendar(NULL, localeID, tz); | |
1284 | if (NULL == cal) { | |
1285 | return NULL; | |
1286 | } | |
1287 | ||
1288 | char buffer[BUFFER_SIZE]; | |
1289 | const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII); | |
1290 | if (NULL == cstr) { | |
1291 | if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; | |
1292 | } | |
1293 | if (NULL == cstr) { | |
1294 | ucal_close(cal); | |
1295 | return NULL; | |
1296 | } | |
1297 | ||
1298 | UChar ubuffer[BUFFER_SIZE]; | |
1299 | UErrorCode status = U_ZERO_ERROR; | |
1300 | int32_t cnt = ucal_getTimeZoneDisplayName(cal, (UCalendarDisplayNameType)style, cstr, ubuffer, BUFFER_SIZE, &status); | |
1301 | ucal_close(cal); | |
1302 | if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { | |
1303 | return CFStringCreateWithCharacters(CFGetAllocator(tz), (const UniChar *)ubuffer, cnt); | |
1304 | } | |
1305 | return NULL; | |
1306 | } | |
1307 | ||
1308 | static CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary(void) { | |
1309 | CFDictionaryRef dict; | |
1310 | __CFTimeZoneLockCompatibilityMapping(); | |
1311 | if (NULL == __CFTimeZoneCompatibilityMappingDict) { | |
1312 | __CFTimeZoneCompatibilityMappingDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 112, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1313 | ||
1314 | // Empty string means delete/ignore these | |
1315 | } | |
1316 | dict = __CFTimeZoneCompatibilityMappingDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneCompatibilityMappingDict) : NULL; | |
1317 | __CFTimeZoneUnlockCompatibilityMapping(); | |
1318 | return dict; | |
1319 | } | |
1320 | ||
1321 | #undef TZZONEINFO | |
1322 | #undef TZZONELINK | |
1323 |