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 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 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* 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* 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 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* 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* 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* 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