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,6 +761,8 @@ static void __InitTZStrings(void) {
637
761
});
638
762
}
639
763
764
+ #elif TARGET_OS_ANDROID
765
+ // Nothing
640
766
#elif TARGET_OS_LINUX || TARGET_OS_BSD
641
767
static void __InitTZStrings (void ) {
642
768
__tzZoneInfo = CFSTR (TZDIR );
@@ -690,6 +816,7 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
690
816
if (result ) return result ;
691
817
}
692
818
819
+ #if !TARGET_OS_ANDROID
693
820
if (!__tzZoneInfo ) __InitTZStrings ();
694
821
ret = readlink (TZDEFAULT , linkbuf , sizeof (linkbuf ));
695
822
if (__tzZoneInfo && (0 < ret )) {
@@ -702,7 +829,9 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
702
829
} else {
703
830
name = CFStringCreateWithBytes (kCFAllocatorSystemDefault , (uint8_t * )linkbuf , strlen (linkbuf ), kCFStringEncodingUTF8 , false);
704
831
}
705
- } else {
832
+ } else
833
+ #endif
834
+ {
706
835
// TODO: This can still fail on Linux if the time zone is not recognized by ICU later
707
836
// Try localtime
708
837
tzset ();
@@ -718,15 +847,6 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
718
847
CFRelease (name );
719
848
if (result ) return result ;
720
849
}
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
850
return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , 0.0 );
731
851
}
732
852
@@ -808,6 +928,8 @@ CFArrayRef CFTimeZoneCopyKnownNames(void) {
808
928
*/
809
929
#if TARGET_OS_WIN32
810
930
list = __CFCopyWindowsTimeZoneList ();
931
+ #elif TARGET_OS_ANDROID
932
+ list = __CFCopyAndroidTimeZoneList ();
811
933
#else
812
934
list = __CFCopyRecursiveDirectoryList ();
813
935
#endif
@@ -1059,6 +1181,37 @@ Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFData
1059
1181
}
1060
1182
1061
1183
CFDataRef _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
1062
1215
void * bytes ;
1063
1216
CFIndex length ;
1064
1217
CFDataRef data = NULL ;
@@ -1070,6 +1223,7 @@ CFDataRef _CFTimeZoneDataCreate(CFURLRef baseURL, CFStringRef tzName) {
1070
1223
CFRelease (tempURL );
1071
1224
}
1072
1225
return data ;
1226
+ #endif
1073
1227
}
1074
1228
1075
1229
Boolean _CFTimeZoneInit (CFTimeZoneRef timeZone , CFStringRef name , CFDataRef data ) {
@@ -1107,7 +1261,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1107
1261
}
1108
1262
1109
1263
CFStringRef tzName = NULL ;
1110
- CFURLRef baseURL ;
1264
+ CFURLRef baseURL = NULL ;
1111
1265
Boolean result = false;
1112
1266
1113
1267
#if TARGET_OS_WIN32
@@ -1133,9 +1287,11 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1133
1287
1134
1288
return FALSE;
1135
1289
#else
1290
+ #if !TARGET_OS_ANDROID
1136
1291
if (!__tzZoneInfo ) __InitTZStrings ();
1137
1292
if (!__tzZoneInfo ) return NULL ;
1138
1293
baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
1294
+ #endif
1139
1295
1140
1296
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
1141
1297
tzName = CFDictionaryGetValue (abbrevs , name );
@@ -1149,7 +1305,9 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1149
1305
CFStringRef mapping = CFDictionaryGetValue (dict , name );
1150
1306
if (mapping ) {
1151
1307
name = mapping ;
1152
- } else if (CFStringHasPrefix (name , __tzZoneInfo )) {
1308
+ }
1309
+ #if !TARGET_OS_ANDROID
1310
+ else if (CFStringHasPrefix (name , __tzZoneInfo )) {
1153
1311
CFMutableStringRef unprefixed = CFStringCreateMutableCopy (kCFAllocatorSystemDefault , CFStringGetLength (name ), name );
1154
1312
CFStringDelete (unprefixed , CFRangeMake (0 , CFStringGetLength (__tzZoneInfo )));
1155
1313
mapping = CFDictionaryGetValue (dict , unprefixed );
@@ -1158,6 +1316,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1158
1316
}
1159
1317
CFRelease (unprefixed );
1160
1318
}
1319
+ #endif
1161
1320
CFRelease (dict );
1162
1321
if (CFEqual (CFSTR ("" ), name )) {
1163
1322
return false;
@@ -1167,7 +1326,9 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1167
1326
tzName = name ;
1168
1327
data = _CFTimeZoneDataCreate (baseURL , tzName );
1169
1328
}
1170
- CFRelease (baseURL );
1329
+ if (baseURL ) {
1330
+ CFRelease (baseURL );
1331
+ }
1171
1332
if (NULL != data ) {
1172
1333
result = _CFTimeZoneInitInternal (timeZone , tzName , data );
1173
1334
CFRelease (data );
@@ -1311,7 +1472,7 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
1311
1472
}
1312
1473
}
1313
1474
}
1314
- CFURLRef baseURL ;
1475
+ CFURLRef baseURL = NULL ;
1315
1476
1316
1477
#if TARGET_OS_WIN32
1317
1478
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
@@ -1335,9 +1496,12 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
1335
1496
1336
1497
return result ;
1337
1498
#else
1499
+ #if !TARGET_OS_ANDROID
1338
1500
if (!__tzZoneInfo ) __InitTZStrings ();
1339
1501
if (!__tzZoneInfo ) return NULL ;
1502
+ #endif
1340
1503
baseURL = CFURLCreateWithFileSystemPath (kCFAllocatorSystemDefault , __tzZoneInfo , kCFURLPOSIXPathStyle , true);
1504
+ #endif
1341
1505
if (tryAbbrev ) {
1342
1506
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
1343
1507
tzName = CFDictionaryGetValue (abbrevs , name );
@@ -1351,7 +1515,9 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
1351
1515
CFStringRef mapping = CFDictionaryGetValue (dict , name );
1352
1516
if (mapping ) {
1353
1517
name = mapping ;
1354
- } else if (CFStringHasPrefix (name , __tzZoneInfo )) {
1518
+ }
1519
+ #if !TARGET_OS_ANDROID
1520
+ else if (CFStringHasPrefix (name , __tzZoneInfo )) {
1355
1521
CFMutableStringRef unprefixed = CFStringCreateMutableCopy (kCFAllocatorSystemDefault , CFStringGetLength (name ), name );
1356
1522
CFStringDelete (unprefixed , CFRangeMake (0 , CFStringGetLength (__tzZoneInfo )));
1357
1523
mapping = CFDictionaryGetValue (dict , unprefixed );
@@ -1360,6 +1526,7 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
1360
1526
}
1361
1527
CFRelease (unprefixed );
1362
1528
}
1529
+ #endif
1363
1530
CFRelease (dict );
1364
1531
if (CFEqual (CFSTR ("" ), name )) {
1365
1532
return NULL ;
@@ -1369,7 +1536,9 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam
1369
1536
tzName = name ;
1370
1537
data = _CFTimeZoneDataCreate (baseURL , tzName );
1371
1538
}
1372
- CFRelease (baseURL );
1539
+ if (baseURL ) {
1540
+ CFRelease (baseURL );
1541
+ }
1373
1542
if (NULL != data ) {
1374
1543
result = CFTimeZoneCreate (allocator , tzName , data );
1375
1544
if (name != tzName ) {
0 commit comments