1 /* Copyright (C) 2007-2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ** GNU General Public License for more details.
11 */
12 #include "android/utils/debug.h"
13 #include "android/utils/timezone.h"
14 #include "android/utils/bufprint.h"
15 #include "android/android.h"
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include "qemu-common.h"
20
21 #define DEBUG 1
22
23 #if 1
24 # define D_ACTIVE VERBOSE_CHECK(timezone)
25 #else
26 # define D_ACTIVE DEBUG
27 #endif
28
29 #if DEBUG
30 # define D(...) do { if (D_ACTIVE) fprintf(stderr, __VA_ARGS__); } while (0)
31 #else
32 # define D(...) ((void)0)
33 #endif
34
35
36
37 static const char* get_zoneinfo_timezone( void ); /* forward */
38
39 static char android_timezone0[256];
40 static const char* android_timezone;
41 static int android_timezone_init;
42
43 static int
check_timezone_is_zoneinfo(const char * tz)44 check_timezone_is_zoneinfo(const char* tz)
45 {
46 const char* slash1 = NULL, *slash2 = NULL;
47
48 if (tz == NULL)
49 return 0;
50
51 /* the name must be of the form Area/Location or Area/Location/SubLocation */
52 slash1 = strchr( tz, '/' );
53 if (slash1 == NULL || slash1[1] == 0)
54 return 0;
55
56 slash2 = strchr( slash1+1, '/');
57 if (slash2 != NULL) {
58 if (slash2[1] == 0 || strchr(slash2+1, '/') != NULL)
59 return 0;
60 }
61
62 return 1;
63 }
64
65 int
timezone_set(const char * tzname)66 timezone_set( const char* tzname )
67 {
68 int len;
69
70 if ( !check_timezone_is_zoneinfo(tzname) )
71 return -1;
72
73 len = strlen(tzname);
74 if (len > sizeof(android_timezone0)-1)
75 return -1;
76
77 strcpy( android_timezone0, tzname );
78 android_timezone = android_timezone0;
79 android_timezone_init = 1;
80
81 return 0;
82 }
83
84
85 char*
bufprint_zoneinfo_timezone(char * p,char * end)86 bufprint_zoneinfo_timezone( char* p, char* end )
87 {
88 const char* tz = get_zoneinfo_timezone();
89
90 if (tz == NULL || !check_timezone_is_zoneinfo(tz))
91 return bufprint(p, end, "Unknown/Unknown");
92 else
93 return bufprint(p, end, "%s", tz);
94 }
95
96 /* on OS X, the timezone directory is always /usr/share/zoneinfo
97 * this makes things easy.
98 */
99 #if defined(__APPLE__)
100
101 #include <unistd.h>
102 #include <limits.h>
103 #define LOCALTIME_FILE "/etc/localtime"
104 #define ZONEINFO_DIR "/usr/share/zoneinfo/"
105 static const char*
get_zoneinfo_timezone(void)106 get_zoneinfo_timezone( void )
107 {
108 if (!android_timezone_init) {
109 const char* tz = getenv("TZ");
110 char buff[PATH_MAX+1];
111
112 android_timezone_init = 1;
113 if (tz == NULL) {
114 int len = readlink(LOCALTIME_FILE, buff, sizeof(buff));
115 if (len < 0) {
116 dprint( "### WARNING: Could not read %s, something is very wrong on your system",
117 LOCALTIME_FILE);
118 return NULL;
119 }
120
121 buff[len] = 0;
122 D("%s: %s points to %s\n", __FUNCTION__, LOCALTIME_FILE, buff);
123 if ( memcmp(buff, ZONEINFO_DIR, sizeof(ZONEINFO_DIR)-1) ) {
124 dprint( "### WARNING: %s does not point to %s, can't determine zoneinfo timezone name",
125 LOCALTIME_FILE, ZONEINFO_DIR );
126 return NULL;
127 }
128 tz = buff + sizeof(ZONEINFO_DIR)-1;
129 if ( !check_timezone_is_zoneinfo(tz) ) {
130 dprint( "### WARNING: %s does not point to zoneinfo-compatible timezone name\n", LOCALTIME_FILE );
131 return NULL;
132 }
133 }
134 snprintf(android_timezone0, sizeof(android_timezone0), "%s", tz );
135 android_timezone = android_timezone0;
136 }
137 D( "found timezone %s", android_timezone );
138 return android_timezone;
139 }
140
141 #endif /* __APPLE__ */
142
143 /* on Linux, with glibc2, the zoneinfo directory can be changed with TZDIR environment variable
144 * but should be /usr/share/zoneinfo by default. /etc/localtime is not guaranteed to exist on
145 * all platforms, so if it doesn't, try $TZDIR/localtime, then /usr/share/zoneinfo/locatime
146 * ugly, isn't it ?
147 *
148 * besides, modern Linux distribution don't make /etc/localtime a symlink but a straight copy of
149 * the original timezone file. the only way to know which zoneinfo name to retrieve is to compare
150 * it with all files in $TZDIR (at least those that match Area/Location or Area/Location/SubLocation
151 */
152 #if defined(__linux__) || defined (__FreeBSD__)
153
154 #include <unistd.h>
155 #include <limits.h>
156 #include <sys/stat.h>
157 #include <dirent.h>
158 #include <fcntl.h>
159 #include <errno.h>
160 #include <string.h>
161
162 #define ZONEINFO_DIR "/usr/share/zoneinfo/"
163 #define LOCALTIME_FILE1 "/etc/localtime"
164
165 typedef struct {
166 const char* localtime;
167 struct stat localtime_st;
168 char* path_end;
169 char* path_root;
170 char path[ PATH_MAX ];
171 } ScanDataRec;
172
173 static int
compare_timezone_to_localtime(ScanDataRec * scan,const char * path)174 compare_timezone_to_localtime( ScanDataRec* scan,
175 const char* path )
176 {
177 struct stat st;
178 int fd1, fd2, result = 0;
179
180 D( "%s: comparing %s:", __FUNCTION__, path );
181
182 if ( stat( path, &st ) < 0 ) {
183 D( " can't stat: %s\n", strerror(errno) );
184 return 0;
185 }
186
187 if ( st.st_size != scan->localtime_st.st_size ) {
188 D( " size mistmatch (%zd != %zd)\n", (size_t)st.st_size, (size_t)scan->localtime_st.st_size );
189 return 0;
190 }
191
192 fd1 = open( scan->localtime, O_RDONLY );
193 if (fd1 < 0) {
194 D(" can't open %s: %s\n", scan->localtime, strerror(errno) );
195 return 0;
196 }
197 fd2 = open( path, O_RDONLY );
198 if (fd2 < 0) {
199 D(" can't open %s: %s\n", path, strerror(errno) );
200 close(fd1);
201 return 0;
202 }
203 do {
204 off_t nn;
205
206 for (nn = 0; nn < st.st_size; nn++) {
207 char temp[2];
208 int ret;
209
210 do { ret = read(fd1, &temp[0], 1); } while (ret < 0 && errno == EINTR);
211 if (ret < 0) break;
212
213 do { ret = read(fd2, &temp[1], 1); } while (ret < 0 && errno == EINTR);
214 if (ret < 0) break;
215
216 if (temp[0] != temp[1])
217 break;
218 }
219
220 result = (nn == st.st_size);
221
222 } while (0);
223
224 D( result ? " MATCH\n" : "no match\n" );
225
226 close(fd2);
227 close(fd1);
228
229 return result;
230 }
231
232 static const char*
scan_timezone_dir(ScanDataRec * scan,char * top,int depth)233 scan_timezone_dir( ScanDataRec* scan,
234 char* top,
235 int depth )
236 {
237 DIR* d = opendir( scan->path );
238 const char* result = NULL;
239
240 D( "%s: entering '%s\n", __FUNCTION__, scan->path );
241 if (d != NULL) {
242 struct dirent* ent;
243 while ((ent = readdir(d)) != NULL) {
244 struct stat ent_st;
245 char* p = top;
246
247 if (ent->d_name[0] == '.') /* avoid hidden and special files */
248 continue;
249
250 p = bufprint( p, scan->path_end, "/%s", ent->d_name );
251 if (p >= scan->path_end)
252 continue;
253
254 //D( "%s: scanning '%s'\n", __FUNCTION__, scan->path );
255
256 if ( stat( scan->path, &ent_st ) < 0 )
257 continue;
258
259 if ( S_ISDIR(ent_st.st_mode) && depth < 2 )
260 {
261 //D( "%s: directory '%s'\n", __FUNCTION__, scan->path );
262 result = scan_timezone_dir( scan, p, depth + 1 );
263 if (result != NULL)
264 break;
265 }
266 else if ( S_ISREG(ent_st.st_mode) && (depth >= 1 && depth <= 2) )
267 {
268 char* name = scan->path_root + 1;
269
270 if ( check_timezone_is_zoneinfo( name ) )
271 {
272 if (compare_timezone_to_localtime( scan, scan->path ))
273 {
274 result = strdup( name );
275 D( "%s: found '%s'\n", __FUNCTION__, result );
276 break;
277 }
278 }
279 else
280 {
281 //D( "%s: ignoring '%s'\n", __FUNCTION__, scan->path );
282 }
283 }
284 }
285 closedir(d);
286 }
287 return result;
288 }
289
290 static const char*
get_zoneinfo_timezone(void)291 get_zoneinfo_timezone( void )
292 {
293 if (!android_timezone_init)
294 {
295 const char* tz = getenv( "TZ" );
296
297 android_timezone_init = 1;
298
299 if ( tz != NULL && !check_timezone_is_zoneinfo(tz) ) {
300 D( "%s: ignoring non zoneinfo formatted TZ environment variable: '%s'\n",
301 __FUNCTION__, tz );
302 tz = NULL;
303 }
304
305 if (tz == NULL) {
306 char* tzdir = NULL;
307 int tzdirlen = 0;
308 char* localtime = NULL;
309 int len;
310 char temp[ PATH_MAX ];
311
312 /* determine the correct timezone directory */
313 {
314 const char* env = getenv("TZDIR");
315 const char* zoneinfo_dir = ZONEINFO_DIR;
316
317 if (env == NULL)
318 env = zoneinfo_dir;
319
320 if ( access( env, R_OK ) != 0 ) {
321 if ( env == zoneinfo_dir ) {
322 fprintf( stderr,
323 "### WARNING: could not find %s directory. unable to determine host timezone\n", env );
324 } else {
325 D( "%s: TZDIR does not point to valid directory, using %s instead\n",
326 __FUNCTION__, zoneinfo_dir );
327 env = zoneinfo_dir;
328 }
329 return NULL;
330 }
331 tzdir = strdup(env);
332 }
333
334 /* remove trailing slash, if any */
335 len = strlen(tzdir);
336 if (len > 0 && tzdir[len-1] == '/') {
337 tzdir[len-1] = 0;
338 len -= 1;
339 }
340 tzdirlen = len;
341 D( "%s: found timezone dir as %s\n", __FUNCTION__, tzdir );
342
343 /* try to find the localtime file */
344 localtime = LOCALTIME_FILE1;
345 if ( access( localtime, R_OK ) != 0 ) {
346 char *p = temp, *end = p + sizeof(temp);
347
348 p = bufprint( p, end, "%s/%s", tzdir, "localtime" );
349 if (p >= end || access( temp, R_OK ) != 0 ) {
350 fprintf( stderr, "### WARNING: could not find %s or %s. unable to determine host timezone\n",
351 LOCALTIME_FILE1, temp );
352 goto Exit;
353 }
354 localtime = temp;
355 }
356 localtime = strdup(localtime);
357 D( "%s: found localtime file as %s\n", __FUNCTION__, localtime );
358
359 #if 1
360 /* if the localtime file is a link, make a quick check */
361 len = readlink( localtime, temp, sizeof(temp)-1 );
362 if (len >= 0 && len > tzdirlen + 2) {
363 temp[len] = 0;
364
365 /* verify that the link points to tzdir/<something> where <something> is a valid zoneinfo name */
366 if ( !memcmp( temp, tzdir, tzdirlen ) && temp[tzdirlen] == '/' ) {
367 if ( check_timezone_is_zoneinfo( temp + tzdirlen + 1 ) ) {
368 /* we have it ! */
369 tz = temp + tzdirlen + 1;
370 D( "%s: found zoneinfo timezone %s from %s symlink\n", __FUNCTION__, tz, localtime );
371 goto Exit;
372 }
373 D( "%s: %s link points to non-zoneinfo filename %s, comparing contents\n",
374 __FUNCTION__, localtime, temp );
375 }
376 }
377 #endif
378
379 /* otherwise, parse all files under tzdir and see if we have something that looks like it */
380 {
381 ScanDataRec scan[1];
382
383 if ( stat( localtime, &scan->localtime_st ) < 0 ) {
384 fprintf( stderr, "### WARNING: can't access '%s', unable to determine host timezone\n",
385 localtime );
386 goto Exit;
387 }
388
389 scan->localtime = localtime;
390 scan->path_end = scan->path + sizeof(scan->path);
391 scan->path_root = bufprint( scan->path, scan->path_end, "%s", tzdir );
392
393 tz = scan_timezone_dir( scan, scan->path_root, 0 );
394 }
395
396 Exit:
397 if (tzdir)
398 free(tzdir);
399 if (localtime)
400 free(localtime);
401
402 if (tz == NULL)
403 return NULL;
404
405 snprintf(android_timezone0, sizeof(android_timezone0), "%s", tz);
406 android_timezone = android_timezone0;
407 }
408 D( "found timezone %s\n", android_timezone );
409 }
410 return android_timezone;
411 }
412
413 #endif /* __linux__ */
414
415
416 /* on Windows, we need to translate the Windows timezone into a ZoneInfo one */
417 #ifdef _WIN32
418 #include <time.h>
419
420 typedef struct {
421 const char* win_name;
422 const char* zoneinfo_name;
423 } Win32Timezone;
424
425 /* table generated from http://unicode.org/cldr/data/diff/supplemental/windows_tzid.html */
426 static const Win32Timezone _win32_timezones[] = {
427 { "AUS Central Standard Time" , "Australia/Darwin" },
428 { "AUS Eastern Standard Time" , "Australia/Sydney" },
429 { "Acre Standard Time" , "America/Rio_Branco" },
430 { "Afghanistan Standard Time" , "Asia/Kabul" },
431 { "Africa_Central Standard Time" , "Africa/Kigali" },
432 { "Africa_Eastern Standard Time" , "Africa/Kampala" },
433 { "Africa_FarWestern Standard Time" , "Africa/El_Aaiun" },
434 { "Africa_Southern Standard Time" , "Africa/Johannesburg" },
435 { "Africa_Western Standard Time" , "Africa/Niamey" },
436 { "Aktyubinsk Standard Time" , "Asia/Aqtobe" },
437 { "Alaska Standard Time" , "America/Juneau" },
438 { "Alaska_Hawaii Standard Time" , "America/Anchorage" },
439 { "Alaskan Standard Time" , "America/Anchorage" },
440 { "Almaty Standard Time" , "Asia/Almaty" },
441 { "Amazon Standard Time" , "America/Manaus" },
442 { "America_Central Standard Time" , "America/Winnipeg" },
443 { "America_Eastern Standard Time" , "America/Panama" },
444 { "America_Mountain Standard Time" , "America/Edmonton" },
445 { "America_Pacific Standard Time" , "America/Vancouver" },
446 { "Anadyr Standard Time" , "Asia/Anadyr" },
447 { "Aqtau Standard Time" , "Asia/Aqtau" },
448 { "Aqtobe Standard Time" , "Asia/Aqtobe" },
449 { "Arab Standard Time" , "Asia/Riyadh" },
450 { "Arabian Standard Time" , "Asia/Bahrain" },
451 { "Arabic Standard Time" , "Asia/Baghdad" },
452 { "Argentina Standard Time" , "America/Buenos_Aires" },
453 { "Argentina_Western Standard Time" , "America/Mendoza" },
454 { "Armenia Standard Time" , "Asia/Yerevan" },
455 { "Ashkhabad Standard Time" , "Asia/Ashgabat" },
456 { "Atlantic Standard Time" , "America/Curacao" },
457 { "Australia_Central Standard Time" , "Australia/Adelaide" },
458 { "Australia_CentralWestern Standard Time", "Australia/Eucla" },
459 { "Australia_Eastern Standard Time" , "Australia/Sydney" },
460 { "Australia_Western Standard Time" , "Australia/Perth" },
461 { "Azerbaijan Standard Time" , "Asia/Baku" },
462 { "Azores Standard Time" , "Atlantic/Azores" },
463 { "Baku Standard Time" , "Asia/Baku" },
464 { "Bangladesh Standard Time" , "Asia/Dhaka" },
465 { "Bering Standard Time" , "America/Adak" },
466 { "Bhutan Standard Time" , "Asia/Thimphu" },
467 { "Bolivia Standard Time" , "America/La_Paz" },
468 { "Borneo Standard Time" , "Asia/Kuching" },
469 { "Brasilia Standard Time" , "America/Sao_Paulo" },
470 { "British Standard Time" , "Europe/London" },
471 { "Brunei Standard Time" , "Asia/Brunei" },
472 { "Canada Central Standard Time" , "America/Regina" },
473 { "Cape Verde Standard Time" , "Atlantic/Cape_Verde" },
474 { "Cape_Verde Standard Time" , "Atlantic/Cape_Verde" },
475 { "Caucasus Standard Time" , "Asia/Yerevan" },
476 { "Cen. Australia Standard Time" , "Australia/Adelaide" },
477 { "Central Standard Time" , "America/Chicago" },
478 { "Central America Standard Time" , "America/Guatemala" },
479 { "Central Asia Standard Time" , "Asia/Dhaka" },
480 { "Central Brazilian Standard Time" , "America/Manaus" },
481 { "Central Europe Standard Time" , "Europe/Prague" },
482 { "Central European Standard Time" , "Europe/Warsaw" },
483 { "Central Pacific Standard Time" , "Pacific/Guadalcanal" },
484 { "Central Standard Time (Mexico)" , "America/Mexico_City" },
485 { "Chamorro Standard Time" , "Pacific/Guam" },
486 { "Changbai Standard Time" , "Asia/Harbin" },
487 { "Chatham Standard Time" , "Pacific/Chatham" },
488 { "Chile Standard Time" , "America/Santiago" },
489 { "China Standard Time" , "Asia/Taipei" },
490 { "Choibalsan Standard Time" , "Asia/Choibalsan" },
491 { "Christmas Standard Time" , "Indian/Christmas" },
492 { "Cocos Standard Time" , "Indian/Cocos" },
493 { "Colombia Standard Time" , "America/Bogota" },
494 { "Cook Standard Time" , "Pacific/Rarotonga" },
495 { "Cuba Standard Time" , "America/Havana" },
496 { "Dacca Standard Time" , "Asia/Dhaka" },
497 { "Dateline Standard Time" , "Pacific/Kwajalein" },
498 { "Davis Standard Time" , "Antarctica/Davis" },
499 { "Dominican Standard Time" , "America/Santo_Domingo" },
500 { "DumontDUrville Standard Time" , "Antarctica/DumontDUrville" },
501 { "Dushanbe Standard Time" , "Asia/Dushanbe" },
502 { "Dutch_Guiana Standard Time" , "America/Paramaribo" },
503 { "E. Africa Standard Time" , "Africa/Nairobi" },
504 { "E. Australia Standard Time" , "Australia/Brisbane" },
505 { "E. Europe Standard Time" , "Europe/Minsk" },
506 { "E. South America Standard Time" , "America/Sao_Paulo" },
507 { "East_Timor Standard Time" , "Asia/Dili" },
508 { "Easter Standard Time" , "Pacific/Easter" },
509 { "Eastern Standard Time" , "America/New_York" },
510 { "Ecuador Standard Time" , "America/Guayaquil" },
511 { "Egypt Standard Time" , "Africa/Cairo" },
512 { "Ekaterinburg Standard Time" , "Asia/Yekaterinburg" },
513 { "Europe_Central Standard Time" , "Europe/Oslo" },
514 { "Europe_Eastern Standard Time" , "Europe/Vilnius" },
515 { "Europe_Western Standard Time" , "Atlantic/Canary" },
516 { "FLE Standard Time" , "Europe/Helsinki" },
517 { "Falkland Standard Time" , "Atlantic/Stanley" },
518 { "Fiji Standard Time" , "Pacific/Fiji" },
519 { "French_Guiana Standard Time" , "America/Cayenne" },
520 { "French_Southern Standard Time" , "Indian/Kerguelen" },
521 { "Frunze Standard Time" , "Asia/Bishkek" },
522 { "GMT Standard Time" , "Europe/Dublin" },
523 { "GTB Standard Time" , "Europe/Istanbul" },
524 { "Galapagos Standard Time" , "Pacific/Galapagos" },
525 { "Gambier Standard Time" , "Pacific/Gambier" },
526 { "Georgia Standard Time" , "Asia/Tbilisi" },
527 { "Georgian Standard Time" , "Asia/Tbilisi" },
528 { "Gilbert_Islands Standard Time" , "Pacific/Tarawa" },
529 { "Goose_Bay Standard Time" , "America/Goose_Bay" },
530 { "Greenland Standard Time" , "America/Godthab" },
531 { "Greenland_Central Standard Time" , "America/Scoresbysund" },
532 { "Greenland_Eastern Standard Time" , "America/Scoresbysund" },
533 { "Greenland_Western Standard Time" , "America/Godthab" },
534 { "Greenwich Standard Time" , "Africa/Casablanca" },
535 { "Guam Standard Time" , "Pacific/Guam" },
536 { "Gulf Standard Time" , "Asia/Muscat" },
537 { "Guyana Standard Time" , "America/Guyana" },
538 { "Hawaii_Aleutian Standard Time" , "Pacific/Honolulu" },
539 { "Hawaiian Standard Time" , "Pacific/Honolulu" },
540 { "Hong_Kong Standard Time" , "Asia/Hong_Kong" },
541 { "Hovd Standard Time" , "Asia/Hovd" },
542 { "India Standard Time" , "Asia/Calcutta" },
543 { "Indian_Ocean Standard Time" , "Indian/Chagos" },
544 { "Indochina Standard Time" , "Asia/Vientiane" },
545 { "Indonesia_Central Standard Time" , "Asia/Makassar" },
546 { "Indonesia_Eastern Standard Time" , "Asia/Jayapura" },
547 { "Indonesia_Western Standard Time" , "Asia/Jakarta" },
548 { "Iran Standard Time" , "Asia/Tehran" },
549 { "Irish Standard Time" , "Europe/Dublin" },
550 { "Irkutsk Standard Time" , "Asia/Irkutsk" },
551 { "Israel Standard Time" , "Asia/Jerusalem" },
552 { "Japan Standard Time" , "Asia/Tokyo" },
553 { "Jordan Standard Time" , "Asia/Amman" },
554 { "Kamchatka Standard Time" , "Asia/Kamchatka" },
555 { "Karachi Standard Time" , "Asia/Karachi" },
556 { "Kashgar Standard Time" , "Asia/Kashgar" },
557 { "Kazakhstan_Eastern Standard Time" , "Asia/Almaty" },
558 { "Kazakhstan_Western Standard Time" , "Asia/Aqtobe" },
559 { "Kizilorda Standard Time" , "Asia/Qyzylorda" },
560 { "Korea Standard Time" , "Asia/Seoul" },
561 { "Kosrae Standard Time" , "Pacific/Kosrae" },
562 { "Krasnoyarsk Standard Time" , "Asia/Krasnoyarsk" },
563 { "Kuybyshev Standard Time" , "Europe/Samara" },
564 { "Kwajalein Standard Time" , "Pacific/Kwajalein" },
565 { "Kyrgystan Standard Time" , "Asia/Bishkek" },
566 { "Lanka Standard Time" , "Asia/Colombo" },
567 { "Liberia Standard Time" , "Africa/Monrovia" },
568 { "Line_Islands Standard Time" , "Pacific/Kiritimati" },
569 { "Long_Shu Standard Time" , "Asia/Chongqing" },
570 { "Lord_Howe Standard Time" , "Australia/Lord_Howe" },
571 { "Macau Standard Time" , "Asia/Macau" },
572 { "Magadan Standard Time" , "Asia/Magadan" },
573 { "Malaya Standard Time" , "Asia/Kuala_Lumpur" },
574 { "Malaysia Standard Time" , "Asia/Kuching" },
575 { "Maldives Standard Time" , "Indian/Maldives" },
576 { "Marquesas Standard Time" , "Pacific/Marquesas" },
577 { "Marshall_Islands Standard Time" , "Pacific/Majuro" },
578 { "Mauritius Standard Time" , "Indian/Mauritius" },
579 { "Mawson Standard Time" , "Antarctica/Mawson" },
580 { "Mexico Standard Time" , "America/Mexico_City" },
581 { "Mexico Standard Time 2 Standard Time" , "America/Chihuahua" },
582 { "Mid-Atlantic Standard Time" , "America/Noronha" },
583 { "Middle East Standard Time" , "Asia/Beirut" },
584 { "Mongolia Standard Time" , "Asia/Ulaanbaatar" },
585 { "Montevideo Standard Time" , "America/Montevideo" },
586 { "Moscow Standard Time" , "Europe/Moscow" },
587 { "Mountain Standard Time" , "America/Denver" },
588 { "Mountain Standard Time (Mexico)" , "America/Chihuahua" },
589 { "Myanmar Standard Time" , "Asia/Rangoon" },
590 { "N. Central Asia Standard Time" , "Asia/Novosibirsk" },
591 { "Namibia Standard Time" , "Africa/Windhoek" },
592 { "Nauru Standard Time" , "Pacific/Nauru" },
593 { "Nepal Standard Time" , "Asia/Katmandu" },
594 { "New Zealand Standard Time" , "Pacific/Auckland" },
595 { "New_Caledonia Standard Time" , "Pacific/Noumea" },
596 { "New_Zealand Standard Time" , "Pacific/Auckland" },
597 { "Newfoundland Standard Time" , "America/St_Johns" },
598 { "Niue Standard Time" , "Pacific/Niue" },
599 { "Norfolk Standard Time" , "Pacific/Norfolk" },
600 { "Noronha Standard Time" , "America/Noronha" },
601 { "North Asia Standard Time" , "Asia/Krasnoyarsk" },
602 { "North Asia East Standard Time" , "Asia/Ulaanbaatar" },
603 { "North_Mariana Standard Time" , "Pacific/Saipan" },
604 { "Novosibirsk Standard Time" , "Asia/Novosibirsk" },
605 { "Omsk Standard Time" , "Asia/Omsk" },
606 { "Oral Standard Time" , "Asia/Oral" },
607 { "Pacific Standard Time" , "America/Los_Angeles" },
608 { "Pacific SA Standard Time" , "America/Santiago" },
609 { "Pacific Standard Time (Mexico)" , "America/Tijuana" },
610 { "Pakistan Standard Time" , "Asia/Karachi" },
611 { "Palau Standard Time" , "Pacific/Palau" },
612 { "Papua_New_Guinea Standard Time" , "Pacific/Port_Moresby" },
613 { "Paraguay Standard Time" , "America/Asuncion" },
614 { "Peru Standard Time" , "America/Lima" },
615 { "Philippines Standard Time" , "Asia/Manila" },
616 { "Phoenix_Islands Standard Time" , "Pacific/Enderbury" },
617 { "Pierre_Miquelon Standard Time" , "America/Miquelon" },
618 { "Pitcairn Standard Time" , "Pacific/Pitcairn" },
619 { "Ponape Standard Time" , "Pacific/Ponape" },
620 { "Qyzylorda Standard Time" , "Asia/Qyzylorda" },
621 { "Reunion Standard Time" , "Indian/Reunion" },
622 { "Romance Standard Time" , "Europe/Paris" },
623 { "Rothera Standard Time" , "Antarctica/Rothera" },
624 { "Russian Standard Time" , "Europe/Moscow" },
625 { "SA Eastern Standard Time" , "America/Buenos_Aires" },
626 { "SA Pacific Standard Time" , "America/Bogota" },
627 { "SA Western Standard Time" , "America/Caracas" },
628 { "SE Asia Standard Time" , "Asia/Bangkok" },
629 { "Sakhalin Standard Time" , "Asia/Sakhalin" },
630 { "Samara Standard Time" , "Europe/Samara" },
631 { "Samarkand Standard Time" , "Asia/Samarkand" },
632 { "Samoa Standard Time" , "Pacific/Apia" },
633 { "Seychelles Standard Time" , "Indian/Mahe" },
634 { "Shevchenko Standard Time" , "Asia/Aqtau" },
635 { "Singapore Standard Time" , "Asia/Singapore" },
636 { "Solomon Standard Time" , "Pacific/Guadalcanal" },
637 { "South Africa Standard Time" , "Africa/Johannesburg" },
638 { "South_Georgia Standard Time" , "Atlantic/South_Georgia" },
639 { "Sri Lanka Standard Time" , "Asia/Colombo" },
640 { "Suriname Standard Time" , "America/Paramaribo" },
641 { "Sverdlovsk Standard Time" , "Asia/Yekaterinburg" },
642 { "Syowa Standard Time" , "Antarctica/Syowa" },
643 { "Tahiti Standard Time" , "Pacific/Tahiti" },
644 { "Taipei Standard Time" , "Asia/Taipei" },
645 { "Tajikistan Standard Time" , "Asia/Dushanbe" },
646 { "Tashkent Standard Time" , "Asia/Tashkent" },
647 { "Tasmania Standard Time" , "Australia/Hobart" },
648 { "Tbilisi Standard Time" , "Asia/Tbilisi" },
649 { "Tokelau Standard Time" , "Pacific/Fakaofo" },
650 { "Tokyo Standard Time" , "Asia/Tokyo" },
651 { "Tonga Standard Time" , "Pacific/Tongatapu" },
652 { "Truk Standard Time" , "Pacific/Truk" },
653 { "Turkey Standard Time" , "Europe/Istanbul" },
654 { "Turkmenistan Standard Time" , "Asia/Ashgabat" },
655 { "Tuvalu Standard Time" , "Pacific/Funafuti" },
656 { "US Eastern Standard Time" , "America/Indianapolis" },
657 { "US Mountain Standard Time" , "America/Phoenix" },
658 { "Uralsk Standard Time" , "Asia/Oral" },
659 { "Uruguay Standard Time" , "America/Montevideo" },
660 { "Urumqi Standard Time" , "Asia/Urumqi" },
661 { "Uzbekistan Standard Time" , "Asia/Tashkent" },
662 { "Vanuatu Standard Time" , "Pacific/Efate" },
663 { "Venezuela Standard Time" , "America/Caracas" },
664 { "Vladivostok Standard Time" , "Asia/Vladivostok" },
665 { "Volgograd Standard Time" , "Europe/Volgograd" },
666 { "Vostok Standard Time" , "Antarctica/Vostok" },
667 { "W. Australia Standard Time" , "Australia/Perth" },
668 { "W. Central Africa Standard Time" , "Africa/Lagos" },
669 { "W. Europe Standard Time" , "Europe/Berlin" },
670 { "Wake Standard Time" , "Pacific/Wake" },
671 { "Wallis Standard Time" , "Pacific/Wallis" },
672 { "West Asia Standard Time" , "Asia/Karachi" },
673 { "West Pacific Standard Time" , "Pacific/Guam" },
674 { "Yakutsk Standard Time" , "Asia/Yakutsk" },
675 { "Yekaterinburg Standard Time" , "Asia/Yekaterinburg" },
676 { "Yerevan Standard Time" , "Asia/Yerevan" },
677 { "Yukon Standard Time" , "America/Yakutat" },
678 { NULL, NULL }
679 };
680
681 static const char*
get_zoneinfo_timezone(void)682 get_zoneinfo_timezone( void )
683 {
684 if (!android_timezone_init)
685 {
686 char tzname[128];
687 time_t t = time(NULL);
688 struct tm* tm = localtime(&t);
689 const Win32Timezone* win32tz = _win32_timezones;
690
691 android_timezone_init = 1;
692
693 if (!tm) {
694 D("%s: could not determine current date/time\n", __FUNCTION__);
695 return NULL;
696 }
697
698 memset(tzname, 0, sizeof(tzname));
699 strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
700
701 for (win32tz = _win32_timezones; win32tz->win_name != NULL; win32tz++)
702 if ( !strcmp(win32tz->win_name, tzname) ) {
703 android_timezone = win32tz->zoneinfo_name;
704 goto Exit;
705 }
706
707 #if 0 /* TODO */
708 /* we didn't find it, this may come from localized versions of Windows. we're going to explore the registry,
709 * as the code in Postgresql does...
710 */
711 #endif
712 D( "%s: could not determine current timezone\n", __FUNCTION__ );
713 return NULL;
714 }
715 Exit:
716 D( "emulator: found timezone %s\n", android_timezone );
717 return android_timezone;
718 }
719
720 #endif /* _WIN32 */
721
722