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
@@ -183,6 +185,121 @@ static void __CFTimeZoneGetOffset(CFStringRef timezone, int32_t *offset) {
183185
184186 RegCloseKey (hKey );
185187}
188+ #elif TARGET_OS_ANDROID
189+ typedef Boolean (* __CFAndroidTimeZoneListEnumerateCallback )(char name [40 ], int32_t start , int32_t length , FILE * fp , void * context );
190+ static void __CFAndroidTimeZoneListEnumerate (__CFAndroidTimeZoneListEnumerateCallback callback , void * context ) {
191+ // The best reference should be Android Bionic's libc/tzcode/bionic.cpp
192+ static const char * tzDataFiles [] = {
193+ "/data/misc/zoneinfo/current/tzdata" ,
194+ "/system/usr/share/zoneinfo/tzdata"
195+ };
196+
197+ Boolean done = FALSE;
198+ for (int idx = 0 ; idx < sizeof (tzDataFiles )/sizeof (tzDataFiles [0 ]) && !done ; idx ++ ) {
199+ FILE * fp = fopen (tzDataFiles [idx ], "rb" );
200+ if (!fp ) {
201+ continue ;
202+ }
203+
204+ char header [24 ];
205+ if (fread (header , 1 , sizeof (header ), fp ) != sizeof (header )) {
206+ fclose (fp );
207+ continue ;
208+ }
209+ if (strncmp (header , "tzdata" , 6 ) != 0 ) {
210+ fclose (fp );
211+ continue ;
212+ }
213+ int32_t indexOffset ;
214+ memcpy (& indexOffset , & header [12 ], sizeof (int32_t ));
215+ indexOffset = betoh32 (indexOffset );
216+ int32_t dataOffset ;
217+ memcpy (& dataOffset , & header [16 ], sizeof (int32_t ));
218+ dataOffset = betoh32 (dataOffset );
219+ if (indexOffset < 0 || dataOffset < indexOffset ) {
220+ fclose (fp );
221+ continue ;
222+ }
223+ if (fseek (fp , indexOffset , SEEK_SET ) != 0 ) {
224+ fclose (fp );
225+ continue ;
226+ }
227+
228+ char entry [52 ];
229+ size_t indexSize = dataOffset - indexOffset ;
230+ size_t zoneCount = indexSize / sizeof (entry );
231+ if (zoneCount * sizeof (entry ) != indexSize ) {
232+ fclose (fp );
233+ continue ;
234+ }
235+ for (size_t idx = 0 ; idx < zoneCount ; idx ++ ) {
236+ if (fread (entry , 1 , sizeof (entry ), fp ) != sizeof (entry )) {
237+ break ;
238+ }
239+ int32_t start ;
240+ memcpy (& start , & entry [40 ], sizeof (int32_t ));
241+ start = betoh32 (start );
242+ start += dataOffset ;
243+ int32_t length ;
244+ memcpy (& length , & entry [44 ], sizeof (int32_t ));
245+ length = betoh32 (length );
246+ if (start < 0 || length < 0 ) {
247+ break ;
248+ }
249+
250+ done = callback (entry , start , length , fp , context );
251+ if (done ) {
252+ break ;
253+ }
254+ }
255+
256+ fclose (fp );
257+ }
258+ }
259+
260+ static Boolean __CFCopyAndroidTimeZoneListCallback (char name [40 ], int32_t start , int32_t length , FILE * fp , void * context ) {
261+ CFMutableArrayRef result = (CFMutableArrayRef )context ;
262+ size_t lenght = strnlen (name , 40 );
263+ CFStringRef timeZoneName = CFStringCreateWithCString (kCFAllocatorSystemDefault , name , kCFStringEncodingASCII );
264+ CFArrayAppendValue (result , timeZoneName );
265+ CFRelease (timeZoneName );
266+ return FALSE;
267+ }
268+
269+ struct __CFTimeZoneDataCreateContext {
270+ const char * tzNameCstr ;
271+ CFDataRef * dataPtr ;
272+ };
273+
274+ static Boolean __CFTimeZoneDataCreateCallback (char name [40 ], int32_t start , int32_t length , FILE * fp , void * context ) {
275+ struct __CFTimeZoneDataCreateContext * ctx = (struct __CFTimeZoneDataCreateContext * )context ;
276+ char * tzNameCstr = ctx -> tzNameCstr ;
277+ CFDataRef * dataPtr = ctx -> dataPtr ;
278+
279+ if (strncmp (tzNameCstr , name , 40 ) == 0 ) {
280+ if (fseek (fp , start , SEEK_SET ) != 0 ) {
281+ return TRUE;
282+ }
283+ uint8_t * bytes = malloc (length );
284+ if (!bytes ) {
285+ return TRUE;
286+ }
287+ if (fread (bytes , 1 , length , fp ) != length ) {
288+ return TRUE;
289+ }
290+ * dataPtr = CFDataCreate (kCFAllocatorSystemDefault , bytes , length );
291+ return TRUE;
292+ }
293+
294+ return FALSE;
295+ }
296+
297+ static CFMutableArrayRef __CFCopyAndroidTimeZoneList () {
298+ CFMutableArrayRef result = CFArrayCreateMutable (kCFAllocatorSystemDefault , 0 , & kCFTypeArrayCallBacks );
299+ __CFAndroidTimeZoneListEnumerate (__CFCopyAndroidTimeZoneListCallback , result );
300+ return result ;
301+ }
302+
186303#elif TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD
187304static CFMutableArrayRef __CFCopyRecursiveDirectoryList () {
188305 CFMutableArrayRef result = CFArrayCreateMutable (kCFAllocatorSystemDefault , 0 , & kCFTypeArrayCallBacks );
@@ -637,6 +754,13 @@ static void __InitTZStrings(void) {
637754 });
638755}
639756
757+ #elif TARGET_OS_ANDROID
758+ static void __InitTZStrings (void ) {
759+ // NOTE: Android doesn't use this values. Only as markers.
760+ __tzZoneInfo = CFSTR ("/system/usr/share/timezone/" );
761+ __tzDir = "/system/usr/share/timezone/tzdata" ;
762+ }
763+
640764#elif TARGET_OS_LINUX || TARGET_OS_BSD
641765static void __InitTZStrings (void ) {
642766 __tzZoneInfo = CFSTR (TZDIR );
@@ -718,15 +842,6 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
718842 CFRelease (name );
719843 if (result ) return result ;
720844 }
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
730845 return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , 0.0 );
731846}
732847
@@ -808,6 +923,8 @@ CFArrayRef CFTimeZoneCopyKnownNames(void) {
808923 */
809924#if TARGET_OS_WIN32
810925 list = __CFCopyWindowsTimeZoneList ();
926+ #elif TARGET_OS_ANDROID
927+ list = __CFCopyAndroidTimeZoneList ();
811928#else
812929 list = __CFCopyRecursiveDirectoryList ();
813930#endif
@@ -1058,6 +1175,53 @@ Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFData
10581175 return success ;
10591176}
10601177
1178+ CFDataRef _CFTimeZoneDataCreate (CFURLRef baseURL , CFStringRef tzName ) {
1179+ #if TARGET_OS_ANDROID
1180+ CFDataRef data = NULL ;
1181+ char * buffer = NULL ;
1182+ const char * tzNameCstr = CFStringGetCStringPtr (tzName , kCFStringEncodingASCII );
1183+ if (!tzNameCstr ) {
1184+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding (CFStringGetLength (tzName ), kCFStringEncodingASCII ) + 2 ;
1185+ if (maxSize == kCFNotFound ) {
1186+ return NULL ;
1187+ }
1188+ buffer = malloc (maxSize );
1189+ if (!buffer ) {
1190+ return NULL ;
1191+ }
1192+ if (CFStringGetCString (tzName , buffer , maxSize , kCFStringEncodingASCII )) {
1193+ tzNameCstr = buffer ;
1194+ }
1195+ }
1196+ if (!tzNameCstr ) {
1197+ return NULL ;
1198+ }
1199+
1200+ struct __CFTimeZoneDataCreateContext context = {
1201+ .tzNameCstr = tzNameCstr ,
1202+ .dataPtr = & data ,
1203+ };
1204+ __CFAndroidTimeZoneListEnumerate (__CFTimeZoneDataCreateCallback , & context );
1205+
1206+ if (buffer ) {
1207+ free (buffer );
1208+ }
1209+ return data ;
1210+ #else
1211+ void * bytes ;
1212+ CFIndex length ;
1213+ CFDataRef data = NULL ;
1214+ CFURLRef tempURL = CFURLCreateCopyAppendingPathComponent (kCFAllocatorSystemDefault , baseURL , tzName , false);
1215+ if (NULL != tempURL ) {
1216+ if (_CFReadBytesFromFile (kCFAllocatorSystemDefault , tempURL , & bytes , & length , 0 , 0 )) {
1217+ data = CFDataCreateWithBytesNoCopy (kCFAllocatorSystemDefault , bytes , length , kCFAllocatorSystemDefault );
1218+ }
1219+ CFRelease (tempURL );
1220+ }
1221+ return data ;
1222+ #endif
1223+ }
1224+
10611225Boolean _CFTimeZoneInit (CFTimeZoneRef timeZone , CFStringRef name , CFDataRef data ) {
10621226 if (!name || !__nameStringOK (name )) {
10631227 return false;
@@ -1093,9 +1257,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
10931257 }
10941258
10951259 CFStringRef tzName = NULL ;
1096- CFURLRef baseURL , tempURL ;
1097- void * bytes ;
1098- CFIndex length ;
1260+ CFURLRef baseURL ;
10991261 Boolean result = false;
11001262
11011263#if TARGET_OS_WIN32
@@ -1128,13 +1290,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11281290 CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
11291291 tzName = CFDictionaryGetValue (abbrevs , name );
11301292 if (NULL != tzName ) {
1131- tempURL = CFURLCreateCopyAppendingPathComponent (kCFAllocatorSystemDefault , baseURL , tzName , false);
1132- if (NULL != tempURL ) {
1133- if (_CFReadBytesFromFile (kCFAllocatorSystemDefault , tempURL , & bytes , & length , 0 , 0 )) {
1134- data = CFDataCreateWithBytesNoCopy (kCFAllocatorSystemDefault , bytes , length , kCFAllocatorSystemDefault );
1135- }
1136- CFRelease (tempURL );
1137- }
1293+ data = _CFTimeZoneDataCreate (baseURL , tzName );
11381294 }
11391295 CFRelease (abbrevs );
11401296
@@ -1159,13 +1315,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
11591315 }
11601316 if (NULL == data ) {
11611317 tzName = name ;
1162- tempURL = CFURLCreateCopyAppendingPathComponent (kCFAllocatorSystemDefault , baseURL , tzName , false);
1163- if (NULL != tempURL ) {
1164- if (_CFReadBytesFromFile (kCFAllocatorSystemDefault , tempURL , & bytes , & length , 0 , 0 )) {
1165- data = CFDataCreateWithBytesNoCopy (kCFAllocatorSystemDefault , bytes , length , kCFAllocatorSystemDefault );
1166- }
1167- CFRelease (tempURL );
1168- }
1318+ data = _CFTimeZoneDataCreate (baseURL , tzName );
11691319 }
11701320 CFRelease (baseURL );
11711321 if (NULL != data ) {
0 commit comments