30
30
#include <unistd.h>
31
31
#if !TARGET_OS_ANDROID
32
32
#include <sys/fcntl.h>
33
+ #else
34
+ #include <sys/endian.h>
33
35
#endif
34
36
#endif
35
37
#if TARGET_OS_WIN32
@@ -70,7 +72,7 @@ struct tzhead {
70
72
71
73
#include <time.h>
72
74
73
- #if !TARGET_OS_WIN32
75
+ #if !TARGET_OS_WIN32 && ! TARGET_OS_ANDROID
74
76
static CFStringRef __tzZoneInfo = NULL ;
75
77
static char * __tzDir = NULL ;
76
78
static void __InitTZStrings (void );
@@ -183,11 +185,133 @@ static void __CFTimeZoneGetOffset(CFStringRef timezone, int32_t *offset) {
183
185
184
186
RegCloseKey (hKey );
185
187
}
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
+
186
308
#elif TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD
187
309
static CFMutableArrayRef __CFCopyRecursiveDirectoryList () {
188
310
CFMutableArrayRef result = CFArrayCreateMutable (kCFAllocatorSystemDefault , 0 , & kCFTypeArrayCallBacks );
311
+ #if !TARGET_OS_ANDROID
189
312
if (!__tzDir ) __InitTZStrings ();
190
313
if (!__tzDir ) return result ;
314
+ #endif
191
315
int fd = open (__tzDir , O_RDONLY );
192
316
193
317
for (; 0 <= fd ;) {
@@ -637,7 +761,7 @@ static void __InitTZStrings(void) {
637
761
});
638
762
}
639
763
640
- #elif TARGET_OS_LINUX || TARGET_OS_BSD
764
+ #elif TARGET_OS_LINUX || TARGET_OS_BSD && ! TARGET_OS_ANDROID
641
765
static void __InitTZStrings (void ) {
642
766
__tzZoneInfo = CFSTR (TZDIR );
643
767
__tzDir = TZDIR "zone.tab" ;
@@ -690,7 +814,9 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
690
814
if (result ) return result ;
691
815
}
692
816
817
+ #if !TARGET_OS_ANDROID
693
818
if (!__tzZoneInfo ) __InitTZStrings ();
819
+ #endif
694
820
ret = readlink (TZDEFAULT , linkbuf , sizeof (linkbuf ));
695
821
if (__tzZoneInfo && (0 < ret )) {
696
822
linkbuf [ret ] = '\0' ;
@@ -718,15 +844,6 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
718
844
CFRelease (name );
719
845
if (result ) return result ;
720
846
}
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
730
847
return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , 0.0 );
731
848
}
732
849
@@ -808,6 +925,8 @@ CFArrayRef CFTimeZoneCopyKnownNames(void) {
808
925
*/
809
926
#if TARGET_OS_WIN32
810
927
list = __CFCopyWindowsTimeZoneList ();
928
+ #elif TARGET_OS_ANDROID
929
+ list = __CFCopyAndroidTimeZoneList ();
811
930
#else
812
931
list = __CFCopyRecursiveDirectoryList ();
813
932
#endif
@@ -1059,6 +1178,37 @@ Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFData
1059
1178
}
1060
1179
1061
1180
CFDataRef _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
1062
1212
void * bytes ;
1063
1213
CFIndex length ;
1064
1214
CFDataRef data = NULL ;
@@ -1070,6 +1220,7 @@ CFDataRef _CFTimeZoneDataCreate(CFURLRef baseURL, CFStringRef tzName) {
1070
1220
CFRelease (tempURL );
1071
1221
}
1072
1222
return data ;
1223
+ #endif
1073
1224
}
1074
1225
1075
1226
Boolean _CFTimeZoneInit (CFTimeZoneRef timeZone , CFStringRef name , CFDataRef data ) {
@@ -1133,8 +1284,10 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1133
1284
1134
1285
return FALSE;
1135
1286
#else
1287
+ #if !TARGET_OS_ANDROID
1136
1288
if (!__tzZoneInfo ) __InitTZStrings ();
1137
1289
if (!__tzZoneInfo ) return NULL ;
1290
+ #endif
1138
1291
baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
1139
1292
1140
1293
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
@@ -1337,8 +1490,10 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
1337
1490
1338
1491
return result ;
1339
1492
#else
1493
+ #if !TARGET_OS_ANDROID
1340
1494
if (!__tzZoneInfo ) __InitTZStrings ();
1341
1495
if (!__tzZoneInfo ) return NULL ;
1496
+ #endif
1342
1497
baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
1343
1498
if (tryAbbrev ) {
1344
1499
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
0 commit comments