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