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
@@ -183,6 +185,121 @@ 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 )(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
+
186
303
#elif TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD
187
304
static CFMutableArrayRef __CFCopyRecursiveDirectoryList () {
188
305
CFMutableArrayRef result = CFArrayCreateMutable (kCFAllocatorSystemDefault , 0 , & kCFTypeArrayCallBacks );
@@ -637,6 +754,13 @@ static void __InitTZStrings(void) {
637
754
});
638
755
}
639
756
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
+
640
764
#elif TARGET_OS_LINUX || TARGET_OS_BSD
641
765
static void __InitTZStrings (void ) {
642
766
__tzZoneInfo = CFSTR (TZDIR );
@@ -718,15 +842,6 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
718
842
CFRelease (name );
719
843
if (result ) return result ;
720
844
}
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
845
return CFTimeZoneCreateWithTimeIntervalFromGMT (kCFAllocatorSystemDefault , 0.0 );
731
846
}
732
847
@@ -808,6 +923,8 @@ CFArrayRef CFTimeZoneCopyKnownNames(void) {
808
923
*/
809
924
#if TARGET_OS_WIN32
810
925
list = __CFCopyWindowsTimeZoneList ();
926
+ #elif TARGET_OS_ANDROID
927
+ list = __CFCopyAndroidTimeZoneList ();
811
928
#else
812
929
list = __CFCopyRecursiveDirectoryList ();
813
930
#endif
@@ -1058,6 +1175,53 @@ Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFData
1058
1175
return success ;
1059
1176
}
1060
1177
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
+
1061
1225
Boolean _CFTimeZoneInit (CFTimeZoneRef timeZone , CFStringRef name , CFDataRef data ) {
1062
1226
if (!name || !__nameStringOK (name )) {
1063
1227
return false;
@@ -1093,9 +1257,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1093
1257
}
1094
1258
1095
1259
CFStringRef tzName = NULL ;
1096
- CFURLRef baseURL , tempURL ;
1097
- void * bytes ;
1098
- CFIndex length ;
1260
+ CFURLRef baseURL ;
1099
1261
Boolean result = false;
1100
1262
1101
1263
#if TARGET_OS_WIN32
@@ -1128,13 +1290,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1128
1290
CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary ();
1129
1291
tzName = CFDictionaryGetValue (abbrevs , name );
1130
1292
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 );
1138
1294
}
1139
1295
CFRelease (abbrevs );
1140
1296
@@ -1159,13 +1315,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data
1159
1315
}
1160
1316
if (NULL == data ) {
1161
1317
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 );
1169
1319
}
1170
1320
CFRelease (baseURL );
1171
1321
if (NULL != data ) {
0 commit comments