3030#include <unistd.h>
3131#if !TARGET_OS_ANDROID
3232#include <sys/fcntl.h>
33+ #else
34+ #include <sys/endian.h>
3335#endif
3436#endif
3537#if TARGET_OS_WIN32
@@ -70,7 +72,7 @@ struct tzhead {
7072
7173#include <time.h>
7274
73- #if !TARGET_OS_WIN32
75+ #if !TARGET_OS_WIN32 && ! TARGET_OS_ANDROID
7476static CFStringRef __tzZoneInfo = NULL ;
7577static char * __tzDir = NULL ;
7678static void __InitTZStrings (void );
@@ -183,11 +185,133 @@ static void __CFTimeZoneGetOffset(CFStringRef timezone, int32_t *offset) {
183185
184186 RegCloseKey (hKey );
185187}
188+ #elif TARGET_OS_ANDROID
189+ typedef Boolean (* __CFAndroidTimeZoneListEnumerateCallback )(const char name [40 ], int32_t start , int32_t length , FILE * fp , void * context );
190+
191+ static void __CFAndroidTimeZoneListEnumerate (__CFAndroidTimeZoneListEnumerateCallback callback , void * context ) {
192+ // The best reference should be Android Bionic's libc/tzcode/bionic.cpp
193+ static const char * tzDataFiles [] = {
194+ "/data/misc/zoneinfo/current/tzdata" ,
195+ "/system/usr/share/zoneinfo/tzdata"
196+ };
197+
198+ Boolean done = FALSE;
199+ for (int idx = 0 ; idx < sizeof (tzDataFiles )/sizeof (tzDataFiles [0 ]) && !done ; idx ++ ) {
200+ FILE * fp = fopen (tzDataFiles [idx ], "rb" );
201+ if (!fp ) {
202+ continue ;
203+ }
204+
205+ char header [24 ];
206+ if (fread (header , 1 , sizeof (header ), fp ) != sizeof (header )) {
207+ fclose (fp );
208+ continue ;
209+ }
210+ if (strncmp (header , "tzdata" , 6 ) != 0 ) {
211+ fclose (fp );
212+ continue ;
213+ }
214+
215+ int32_t indexOffset ;
216+ memcpy (& indexOffset , & header [12 ], sizeof (int32_t ));
217+ indexOffset = betoh32 (indexOffset );
218+
219+ int32_t dataOffset ;
220+ memcpy (& dataOffset , & header [16 ], sizeof (int32_t ));
221+ dataOffset = betoh32 (dataOffset );
222+
223+ if (indexOffset < 0 || dataOffset < indexOffset ) {
224+ fclose (fp );
225+ continue ;
226+ }
227+ if (fseek (fp , indexOffset , SEEK_SET ) != 0 ) {
228+ fclose (fp );
229+ continue ;
230+ }
231+
232+ char entry [52 ];
233+ size_t indexSize = dataOffset - indexOffset ;
234+ size_t zoneCount = indexSize / sizeof (entry );
235+ if (zoneCount * sizeof (entry ) != indexSize ) {
236+ fclose (fp );
237+ continue ;
238+ }
239+ for (size_t idx = 0 ; idx < zoneCount ; idx ++ ) {
240+ if (fread (entry , 1 , sizeof (entry ), fp ) != sizeof (entry )) {
241+ break ;
242+ }
243+ int32_t start ;
244+ memcpy (& start , & entry [40 ], sizeof (int32_t ));
245+ start = betoh32 (start );
246+ start += dataOffset ;
247+ int32_t length ;
248+ memcpy (& length , & entry [44 ], sizeof (int32_t ));
249+ length = betoh32 (length );
250+ if (start < 0 || length < 0 ) {
251+ break ;
252+ }
253+
254+ done = callback (entry , start , length , fp , context );
255+ if (done ) {
256+ break ;
257+ }
258+ }
259+
260+ fclose (fp );
261+ }
262+ }
263+
264+ static Boolean __CFCopyAndroidTimeZoneListCallback (const char name [40 ], int32_t start , int32_t length , FILE * fp , void * context ) {
265+ CFMutableArrayRef result = (CFMutableArrayRef )context ;
266+ CFStringRef timeZoneName = CFStringCreateWithCString (kCFAllocatorSystemDefault , name , kCFStringEncodingASCII );
267+ CFArrayAppendValue (result , timeZoneName );
268+ CFRelease (timeZoneName );
269+ return FALSE;
270+ }
271+
272+ struct __CFTimeZoneDataCreateContext {
273+ const char * tzNameCstr ;
274+ CFDataRef * dataPtr ;
275+ };
276+
277+ static Boolean __CFTimeZoneDataCreateCallback (const char name [40 ], int32_t start , int32_t length , FILE * fp , void * context ) {
278+ struct __CFTimeZoneDataCreateContext * ctx = (struct __CFTimeZoneDataCreateContext * )context ;
279+ char * tzNameCstr = ctx -> tzNameCstr ;
280+ CFDataRef * dataPtr = ctx -> dataPtr ;
281+
282+ if (strncmp (tzNameCstr , name , 40 ) == 0 ) {
283+ if (fseek (fp , start , SEEK_SET ) != 0 ) {
284+ return TRUE;
285+ }
286+ uint8_t * bytes = malloc (length );
287+ if (!bytes ) {
288+ return TRUE;
289+ }
290+ if (fread (bytes , 1 , length , fp ) != length ) {
291+ free (bytes );
292+ return TRUE;
293+ }
294+ * dataPtr = CFDataCreate (kCFAllocatorSystemDefault , bytes , length );
295+ free (bytes );
296+ return TRUE;
297+ }
298+
299+ return FALSE;
300+ }
301+
302+ static CFMutableArrayRef __CFCopyAndroidTimeZoneList () {
303+ CFMutableArrayRef result = CFArrayCreateMutable (kCFAllocatorSystemDefault , 0 , & kCFTypeArrayCallBacks );
304+ __CFAndroidTimeZoneListEnumerate (__CFCopyAndroidTimeZoneListCallback , result );
305+ return result ;
306+ }
307+
186308#elif TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD
187309static CFMutableArrayRef __CFCopyRecursiveDirectoryList () {
188310 CFMutableArrayRef result = CFArrayCreateMutable (kCFAllocatorSystemDefault , 0 , & kCFTypeArrayCallBacks );
311+ #if !TARGET_OS_ANDROID
189312 if (!__tzDir ) __InitTZStrings ();
190313 if (!__tzDir ) return result ;
314+ #endif
191315 int fd = open (__tzDir , O_RDONLY );
192316
193317 for (; 0 <= fd ;) {
@@ -637,6 +761,8 @@ static void __InitTZStrings(void) {
637761 });
638762}
639763
764+ #elif TARGET_OS_ANDROID
765+ // Nothing
640766#elif TARGET_OS_LINUX || TARGET_OS_BSD
641767static void __InitTZStrings (void ) {
642768 __tzZoneInfo = CFSTR (TZDIR );
@@ -690,6 +816,7 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
690816 if (result ) return result ;
691817 }
692818
819+ #if !TARGET_OS_ANDROID
693820 if (!__tzZoneInfo ) __InitTZStrings ();
694821 ret = readlink (TZDEFAULT , linkbuf , sizeof (linkbuf ));
695822 if (__tzZoneInfo && (0 < ret )) {
@@ -702,7 +829,9 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
702829 } else {
703830 name = CFStringCreateWithBytes (kCFAllocatorSystemDefault , (uint8_t * )linkbuf , strlen (linkbuf ), kCFStringEncodingUTF8 , false);
704831 }
705- } else {
832+ } else
833+ #endif
834+ {
706835 // TODO: This can still fail on Linux if the time zone is not recognized by ICU later
707836 // Try localtime
708837 tzset ();
@@ -718,15 +847,6 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
718847 CFRelease (name );
719848 if (result ) return result ;
720849 }
721- #if TARGET_OS_ANDROID
722- // Timezone database by name not available on Android.
723- // Approximate with gmtoff - could be general default.
724- struct tm info ;
725- time_t now = time (NULL );
726- if (NULL != localtime_r (& now , & info )) {
727- return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , info .tm_gmtoff );
728- }
729- #endif
730850 return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , 0.0 );
731851}
732852
@@ -808,6 +928,8 @@ CFArrayRef CFTimeZoneCopyKnownNames(void) {
808928 */
809929#if TARGET_OS_WIN32
810930 list = __CFCopyWindowsTimeZoneList ();
931+ #elif TARGET_OS_ANDROID
932+ list = __CFCopyAndroidTimeZoneList ();
811933#else
812934 list = __CFCopyRecursiveDirectoryList ();
813935#endif
@@ -1059,6 +1181,37 @@ Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFData
10591181}
10601182
10611183CFDataRef _CFTimeZoneDataCreate (CFURLRef baseURL , CFStringRef tzName ) {
1184+ #if TARGET_OS_ANDROID
1185+ CFDataRef data = NULL ;
1186+ char * buffer = NULL ;
1187+ const char * tzNameCstr = CFStringGetCStringPtr (tzName , kCFStringEncodingASCII );
1188+ if (!tzNameCstr ) {
1189+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding (CFStringGetLength (tzName ), kCFStringEncodingASCII ) + 2 ;
1190+ if (maxSize == kCFNotFound ) {
1191+ return NULL ;
1192+ }
1193+ buffer = malloc (maxSize );
1194+ if (!buffer ) {
1195+ return NULL ;
1196+ }
1197+ if (CFStringGetCString (tzName , buffer , maxSize , kCFStringEncodingASCII )) {
1198+ tzNameCstr = buffer ;
1199+ }
1200+ }
1201+ if (!tzNameCstr ) {
1202+ free (buffer );
1203+ return NULL ;
1204+ }
1205+
1206+ struct __CFTimeZoneDataCreateContext context = {
1207+ .tzNameCstr = tzNameCstr ,
1208+ .dataPtr = & data ,
1209+ };
1210+ __CFAndroidTimeZoneListEnumerate (__CFTimeZoneDataCreateCallback , & context );
1211+
1212+ free (buffer );
1213+ return data ;
1214+ #else
10621215 void * bytes ;
10631216 CFIndex length ;
10641217 CFDataRef data = NULL ;
@@ -1070,6 +1223,7 @@ CFDataRef _CFTimeZoneDataCreate(CFURLRef baseURL, CFStringRef tzName) {
10701223 CFRelease (tempURL );
10711224 }
10721225 return data ;
1226+ #endif
10731227}
10741228
10751229Boolean _CFTimeZoneInit (CFTimeZoneRef timeZone , CFStringRef name , CFDataRef data ) {
@@ -1107,7 +1261,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11071261 }
11081262
11091263 CFStringRef tzName = NULL ;
1110- CFURLRef baseURL ;
1264+ CFURLRef baseURL = NULL ;
11111265 Boolean result = false;
11121266
11131267#if TARGET_OS_WIN32
@@ -1133,9 +1287,11 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11331287
11341288 return FALSE;
11351289#else
1290+ #if !TARGET_OS_ANDROID
11361291 if (!__tzZoneInfo ) __InitTZStrings ();
11371292 if (!__tzZoneInfo ) return NULL ;
11381293 baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
1294+ #endif
11391295
11401296 CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
11411297 tzName = CFDictionaryGetValue (abbrevs , name );
@@ -1149,7 +1305,9 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11491305 CFStringRef mapping = CFDictionaryGetValue (dict , name );
11501306 if (mapping ) {
11511307 name = mapping ;
1152- } else if (CFStringHasPrefix (name , __tzZoneInfo )) {
1308+ }
1309+ #if !TARGET_OS_ANDROID
1310+ else if (CFStringHasPrefix (name , __tzZoneInfo )) {
11531311 CFMutableStringRef unprefixed = CFStringCreateMutableCopy (kCFAllocatorSystemDefault , CFStringGetLength (name ), name );
11541312 CFStringDelete (unprefixed , CFRangeMake (0 , CFStringGetLength (__tzZoneInfo )));
11551313 mapping = CFDictionaryGetValue (dict , unprefixed );
@@ -1158,6 +1316,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11581316 }
11591317 CFRelease (unprefixed );
11601318 }
1319+ #endif
11611320 CFRelease (dict );
11621321 if (CFEqual (CFSTR ("" ), name )) {
11631322 return false;
@@ -1167,7 +1326,9 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11671326 tzName = name ;
11681327 data = _CFTimeZoneDataCreate (baseURL , tzName );
11691328 }
1170- CFRelease (baseURL );
1329+ if (baseURL ) {
1330+ CFRelease (baseURL );
1331+ }
11711332 if (NULL != data ) {
11721333 result = _CFTimeZoneInitInternal (timeZone , tzName , data );
11731334 CFRelease (data );
@@ -1311,7 +1472,7 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
13111472 }
13121473 }
13131474 }
1314- CFURLRef baseURL ;
1475+ CFURLRef baseURL = NULL ;
13151476
13161477#if TARGET_OS_WIN32
13171478 CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
@@ -1335,9 +1496,12 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
13351496
13361497 return result ;
13371498#else
1499+ #if !TARGET_OS_ANDROID
13381500 if (!__tzZoneInfo ) __InitTZStrings ();
13391501 if (!__tzZoneInfo ) return NULL ;
1502+ #endif
13401503 baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
1504+ #endif
13411505 if (tryAbbrev ) {
13421506 CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
13431507 tzName = CFDictionaryGetValue (abbrevs , name );
@@ -1351,7 +1515,9 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
13511515 CFStringRef mapping = CFDictionaryGetValue (dict , name );
13521516 if (mapping ) {
13531517 name = mapping ;
1354- } else if (CFStringHasPrefix (name , __tzZoneInfo )) {
1518+ }
1519+ #if !TARGET_OS_ANDROID
1520+ else if (CFStringHasPrefix (name , __tzZoneInfo )) {
13551521 CFMutableStringRef unprefixed = CFStringCreateMutableCopy (kCFAllocatorSystemDefault , CFStringGetLength (name ), name );
13561522 CFStringDelete (unprefixed , CFRangeMake (0 , CFStringGetLength (__tzZoneInfo )));
13571523 mapping = CFDictionaryGetValue (dict , unprefixed );
@@ -1360,6 +1526,7 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
13601526 }
13611527 CFRelease (unprefixed );
13621528 }
1529+ #endif
13631530 CFRelease (dict );
13641531 if (CFEqual (CFSTR ("" ), name )) {
13651532 return NULL ;
@@ -1369,7 +1536,9 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
13691536 tzName = name ;
13701537 data = _CFTimeZoneDataCreate (baseURL , tzName );
13711538 }
1372- CFRelease (baseURL );
1539+ if (baseURL ) {
1540+ CFRelease (baseURL );
1541+ }
13731542 if (NULL != data ) {
13741543 result = CFTimeZoneCreate (allocator , tzName , data );
13751544 if (name != tzName ) {
0 commit comments