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,7 +761,7 @@ static void __InitTZStrings(void) {
637761 });
638762}
639763
640- #elif TARGET_OS_LINUX || TARGET_OS_BSD
764+ #elif TARGET_OS_LINUX || TARGET_OS_BSD && ! TARGET_OS_ANDROID
641765static void __InitTZStrings (void ) {
642766 __tzZoneInfo = CFSTR (TZDIR );
643767 __tzDir = TZDIR "zone.tab" ;
@@ -690,7 +814,9 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
690814 if (result ) return result ;
691815 }
692816
817+ #if !TARGET_OS_ANDROID
693818 if (!__tzZoneInfo ) __InitTZStrings ();
819+ #endif
694820 ret = readlink (TZDEFAULT , linkbuf , sizeof (linkbuf ));
695821 if (__tzZoneInfo && (0 < ret )) {
696822 linkbuf [ret ] = '\0' ;
@@ -718,15 +844,6 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
718844 CFRelease (name );
719845 if (result ) return result ;
720846 }
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
730847 return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , 0.0 );
731848}
732849
@@ -808,6 +925,8 @@ CFArrayRef CFTimeZoneCopyKnownNames(void) {
808925 */
809926#if TARGET_OS_WIN32
810927 list = __CFCopyWindowsTimeZoneList ();
928+ #elif TARGET_OS_ANDROID
929+ list = __CFCopyAndroidTimeZoneList ();
811930#else
812931 list = __CFCopyRecursiveDirectoryList ();
813932#endif
@@ -1059,6 +1178,37 @@ Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFData
10591178}
10601179
10611180CFDataRef _CFTimeZoneDataCreate (CFURLRef baseURL , CFStringRef tzName ) {
1181+ #if TARGET_OS_ANDROID
1182+ CFDataRef data = NULL ;
1183+ char * buffer = NULL ;
1184+ const char * tzNameCstr = CFStringGetCStringPtr (tzName , kCFStringEncodingASCII );
1185+ if (!tzNameCstr ) {
1186+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding (CFStringGetLength (tzName ), kCFStringEncodingASCII ) + 2 ;
1187+ if (maxSize == kCFNotFound ) {
1188+ return NULL ;
1189+ }
1190+ buffer = malloc (maxSize );
1191+ if (!buffer ) {
1192+ return NULL ;
1193+ }
1194+ if (CFStringGetCString (tzName , buffer , maxSize , kCFStringEncodingASCII )) {
1195+ tzNameCstr = buffer ;
1196+ }
1197+ }
1198+ if (!tzNameCstr ) {
1199+ free (buffer );
1200+ return NULL ;
1201+ }
1202+
1203+ struct __CFTimeZoneDataCreateContext context = {
1204+ .tzNameCstr = tzNameCstr ,
1205+ .dataPtr = & data ,
1206+ };
1207+ __CFAndroidTimeZoneListEnumerate (__CFTimeZoneDataCreateCallback , & context );
1208+
1209+ free (buffer );
1210+ return data ;
1211+ #else
10621212 void * bytes ;
10631213 CFIndex length ;
10641214 CFDataRef data = NULL ;
@@ -1070,6 +1220,7 @@ CFDataRef _CFTimeZoneDataCreate(CFURLRef baseURL, CFStringRef tzName) {
10701220 CFRelease (tempURL );
10711221 }
10721222 return data ;
1223+ #endif
10731224}
10741225
10751226Boolean _CFTimeZoneInit (CFTimeZoneRef timeZone , CFStringRef name , CFDataRef data ) {
@@ -1133,8 +1284,10 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11331284
11341285 return FALSE;
11351286#else
1287+ #if !TARGET_OS_ANDROID
11361288 if (!__tzZoneInfo ) __InitTZStrings ();
11371289 if (!__tzZoneInfo ) return NULL ;
1290+ #endif
11381291 baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
11391292
11401293 CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
@@ -1337,8 +1490,10 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
13371490
13381491 return result ;
13391492#else
1493+ #if !TARGET_OS_ANDROID
13401494 if (!__tzZoneInfo ) __InitTZStrings ();
13411495 if (!__tzZoneInfo ) return NULL ;
1496+ #endif
13421497 baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
13431498 if (tryAbbrev ) {
13441499 CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
0 commit comments