/* Copyright (C) 2007-2008 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ #include "android/utils/debug.h" #include "android/utils/timezone.h" #include "android/utils/bufprint.h" #include "android/android.h" #include #include #include #include "qemu-common.h" #define DEBUG 1 #if 1 # define D_ACTIVE VERBOSE_CHECK(timezone) #else # define D_ACTIVE DEBUG #endif #if DEBUG # define D(...) do { if (D_ACTIVE) fprintf(stderr, __VA_ARGS__); } while (0) #else # define D(...) ((void)0) #endif static const char* get_zoneinfo_timezone( void ); /* forward */ static char android_timezone0[256]; static const char* android_timezone; static int android_timezone_init; static int check_timezone_is_zoneinfo(const char* tz) { const char* slash1 = NULL, *slash2 = NULL; if (tz == NULL) return 0; /* the name must be of the form Area/Location or Area/Location/SubLocation */ slash1 = strchr( tz, '/' ); if (slash1 == NULL || slash1[1] == 0) return 0; slash2 = strchr( slash1+1, '/'); if (slash2 != NULL) { if (slash2[1] == 0 || strchr(slash2+1, '/') != NULL) return 0; } return 1; } int timezone_set( const char* tzname ) { int len; if ( !check_timezone_is_zoneinfo(tzname) ) return -1; len = strlen(tzname); if (len > sizeof(android_timezone0)-1) return -1; strcpy( android_timezone0, tzname ); android_timezone = android_timezone0; android_timezone_init = 1; return 0; } char* bufprint_zoneinfo_timezone( char* p, char* end ) { const char* tz = get_zoneinfo_timezone(); if (tz == NULL || !check_timezone_is_zoneinfo(tz)) return bufprint(p, end, "Unknown/Unknown"); else return bufprint(p, end, "%s", tz); } /* on OS X, the timezone directory is always /usr/share/zoneinfo * this makes things easy. */ #if defined(__APPLE__) #include #include #define LOCALTIME_FILE "/etc/localtime" #define ZONEINFO_DIR "/usr/share/zoneinfo/" static const char* get_zoneinfo_timezone( void ) { if (!android_timezone_init) { const char* tz = getenv("TZ"); char buff[PATH_MAX+1]; android_timezone_init = 1; if (tz == NULL) { int len = readlink(LOCALTIME_FILE, buff, sizeof(buff)); if (len < 0) { dprint( "### WARNING: Could not read %s, something is very wrong on your system", LOCALTIME_FILE); return NULL; } buff[len] = 0; D("%s: %s points to %s\n", __FUNCTION__, LOCALTIME_FILE, buff); if ( memcmp(buff, ZONEINFO_DIR, sizeof(ZONEINFO_DIR)-1) ) { dprint( "### WARNING: %s does not point to %s, can't determine zoneinfo timezone name", LOCALTIME_FILE, ZONEINFO_DIR ); return NULL; } tz = buff + sizeof(ZONEINFO_DIR)-1; if ( !check_timezone_is_zoneinfo(tz) ) { dprint( "### WARNING: %s does not point to zoneinfo-compatible timezone name\n", LOCALTIME_FILE ); return NULL; } } snprintf(android_timezone0, sizeof(android_timezone0), "%s", tz ); android_timezone = android_timezone0; } D( "found timezone %s", android_timezone ); return android_timezone; } #endif /* __APPLE__ */ /* on Linux, with glibc2, the zoneinfo directory can be changed with TZDIR environment variable * but should be /usr/share/zoneinfo by default. /etc/localtime is not guaranteed to exist on * all platforms, so if it doesn't, try $TZDIR/localtime, then /usr/share/zoneinfo/locatime * ugly, isn't it ? * * besides, modern Linux distribution don't make /etc/localtime a symlink but a straight copy of * the original timezone file. the only way to know which zoneinfo name to retrieve is to compare * it with all files in $TZDIR (at least those that match Area/Location or Area/Location/SubLocation */ #if defined(__linux__) || defined (__FreeBSD__) #include #include #include #include #include #include #include #define ZONEINFO_DIR "/usr/share/zoneinfo/" #define LOCALTIME_FILE1 "/etc/localtime" typedef struct { const char* localtime; struct stat localtime_st; char* path_end; char* path_root; char path[ PATH_MAX ]; } ScanDataRec; static int compare_timezone_to_localtime( ScanDataRec* scan, const char* path ) { struct stat st; int fd1, fd2, result = 0; D( "%s: comparing %s:", __FUNCTION__, path ); if ( stat( path, &st ) < 0 ) { D( " can't stat: %s\n", strerror(errno) ); return 0; } if ( st.st_size != scan->localtime_st.st_size ) { D( " size mistmatch (%zd != %zd)\n", (size_t)st.st_size, (size_t)scan->localtime_st.st_size ); return 0; } fd1 = open( scan->localtime, O_RDONLY ); if (fd1 < 0) { D(" can't open %s: %s\n", scan->localtime, strerror(errno) ); return 0; } fd2 = open( path, O_RDONLY ); if (fd2 < 0) { D(" can't open %s: %s\n", path, strerror(errno) ); close(fd1); return 0; } do { off_t nn; for (nn = 0; nn < st.st_size; nn++) { char temp[2]; int ret; do { ret = read(fd1, &temp[0], 1); } while (ret < 0 && errno == EINTR); if (ret < 0) break; do { ret = read(fd2, &temp[1], 1); } while (ret < 0 && errno == EINTR); if (ret < 0) break; if (temp[0] != temp[1]) break; } result = (nn == st.st_size); } while (0); D( result ? " MATCH\n" : "no match\n" ); close(fd2); close(fd1); return result; } static const char* scan_timezone_dir( ScanDataRec* scan, char* top, int depth ) { DIR* d = opendir( scan->path ); const char* result = NULL; D( "%s: entering '%s\n", __FUNCTION__, scan->path ); if (d != NULL) { struct dirent* ent; while ((ent = readdir(d)) != NULL) { struct stat ent_st; char* p = top; if (ent->d_name[0] == '.') /* avoid hidden and special files */ continue; p = bufprint( p, scan->path_end, "/%s", ent->d_name ); if (p >= scan->path_end) continue; //D( "%s: scanning '%s'\n", __FUNCTION__, scan->path ); if ( stat( scan->path, &ent_st ) < 0 ) continue; if ( S_ISDIR(ent_st.st_mode) && depth < 2 ) { //D( "%s: directory '%s'\n", __FUNCTION__, scan->path ); result = scan_timezone_dir( scan, p, depth + 1 ); if (result != NULL) break; } else if ( S_ISREG(ent_st.st_mode) && (depth >= 1 && depth <= 2) ) { char* name = scan->path_root + 1; if ( check_timezone_is_zoneinfo( name ) ) { if (compare_timezone_to_localtime( scan, scan->path )) { result = strdup( name ); D( "%s: found '%s'\n", __FUNCTION__, result ); break; } } else { //D( "%s: ignoring '%s'\n", __FUNCTION__, scan->path ); } } } closedir(d); } return result; } static const char* get_zoneinfo_timezone( void ) { if (!android_timezone_init) { const char* tz = getenv( "TZ" ); android_timezone_init = 1; if ( tz != NULL && !check_timezone_is_zoneinfo(tz) ) { D( "%s: ignoring non zoneinfo formatted TZ environment variable: '%s'\n", __FUNCTION__, tz ); tz = NULL; } if (tz == NULL) { char* tzdir = NULL; int tzdirlen = 0; char* localtime = NULL; int len; char temp[ PATH_MAX ]; /* determine the correct timezone directory */ { const char* env = getenv("TZDIR"); const char* zoneinfo_dir = ZONEINFO_DIR; if (env == NULL) env = zoneinfo_dir; if ( access( env, R_OK ) != 0 ) { if ( env == zoneinfo_dir ) { fprintf( stderr, "### WARNING: could not find %s directory. unable to determine host timezone\n", env ); } else { D( "%s: TZDIR does not point to valid directory, using %s instead\n", __FUNCTION__, zoneinfo_dir ); env = zoneinfo_dir; } return NULL; } tzdir = strdup(env); } /* remove trailing slash, if any */ len = strlen(tzdir); if (len > 0 && tzdir[len-1] == '/') { tzdir[len-1] = 0; len -= 1; } tzdirlen = len; D( "%s: found timezone dir as %s\n", __FUNCTION__, tzdir ); /* try to find the localtime file */ localtime = LOCALTIME_FILE1; if ( access( localtime, R_OK ) != 0 ) { char *p = temp, *end = p + sizeof(temp); p = bufprint( p, end, "%s/%s", tzdir, "localtime" ); if (p >= end || access( temp, R_OK ) != 0 ) { fprintf( stderr, "### WARNING: could not find %s or %s. unable to determine host timezone\n", LOCALTIME_FILE1, temp ); goto Exit; } localtime = temp; } localtime = strdup(localtime); D( "%s: found localtime file as %s\n", __FUNCTION__, localtime ); #if 1 /* if the localtime file is a link, make a quick check */ len = readlink( localtime, temp, sizeof(temp)-1 ); if (len >= 0 && len > tzdirlen + 2) { temp[len] = 0; /* verify that the link points to tzdir/ where is a valid zoneinfo name */ if ( !memcmp( temp, tzdir, tzdirlen ) && temp[tzdirlen] == '/' ) { if ( check_timezone_is_zoneinfo( temp + tzdirlen + 1 ) ) { /* we have it ! */ tz = temp + tzdirlen + 1; D( "%s: found zoneinfo timezone %s from %s symlink\n", __FUNCTION__, tz, localtime ); goto Exit; } D( "%s: %s link points to non-zoneinfo filename %s, comparing contents\n", __FUNCTION__, localtime, temp ); } } #endif /* otherwise, parse all files under tzdir and see if we have something that looks like it */ { ScanDataRec scan[1]; if ( stat( localtime, &scan->localtime_st ) < 0 ) { fprintf( stderr, "### WARNING: can't access '%s', unable to determine host timezone\n", localtime ); goto Exit; } scan->localtime = localtime; scan->path_end = scan->path + sizeof(scan->path); scan->path_root = bufprint( scan->path, scan->path_end, "%s", tzdir ); tz = scan_timezone_dir( scan, scan->path_root, 0 ); } Exit: if (tzdir) free(tzdir); if (localtime) free(localtime); if (tz == NULL) return NULL; snprintf(android_timezone0, sizeof(android_timezone0), "%s", tz); android_timezone = android_timezone0; } D( "found timezone %s\n", android_timezone ); } return android_timezone; } #endif /* __linux__ */ /* on Windows, we need to translate the Windows timezone into a ZoneInfo one */ #ifdef _WIN32 #include typedef struct { const char* win_name; const char* zoneinfo_name; } Win32Timezone; /* table generated from http://unicode.org/cldr/data/diff/supplemental/windows_tzid.html */ static const Win32Timezone _win32_timezones[] = { { "AUS Central Standard Time" , "Australia/Darwin" }, { "AUS Eastern Standard Time" , "Australia/Sydney" }, { "Acre Standard Time" , "America/Rio_Branco" }, { "Afghanistan Standard Time" , "Asia/Kabul" }, { "Africa_Central Standard Time" , "Africa/Kigali" }, { "Africa_Eastern Standard Time" , "Africa/Kampala" }, { "Africa_FarWestern Standard Time" , "Africa/El_Aaiun" }, { "Africa_Southern Standard Time" , "Africa/Johannesburg" }, { "Africa_Western Standard Time" , "Africa/Niamey" }, { "Aktyubinsk Standard Time" , "Asia/Aqtobe" }, { "Alaska Standard Time" , "America/Juneau" }, { "Alaska_Hawaii Standard Time" , "America/Anchorage" }, { "Alaskan Standard Time" , "America/Anchorage" }, { "Almaty Standard Time" , "Asia/Almaty" }, { "Amazon Standard Time" , "America/Manaus" }, { "America_Central Standard Time" , "America/Winnipeg" }, { "America_Eastern Standard Time" , "America/Panama" }, { "America_Mountain Standard Time" , "America/Edmonton" }, { "America_Pacific Standard Time" , "America/Vancouver" }, { "Anadyr Standard Time" , "Asia/Anadyr" }, { "Aqtau Standard Time" , "Asia/Aqtau" }, { "Aqtobe Standard Time" , "Asia/Aqtobe" }, { "Arab Standard Time" , "Asia/Riyadh" }, { "Arabian Standard Time" , "Asia/Bahrain" }, { "Arabic Standard Time" , "Asia/Baghdad" }, { "Argentina Standard Time" , "America/Buenos_Aires" }, { "Argentina_Western Standard Time" , "America/Mendoza" }, { "Armenia Standard Time" , "Asia/Yerevan" }, { "Ashkhabad Standard Time" , "Asia/Ashgabat" }, { "Atlantic Standard Time" , "America/Curacao" }, { "Australia_Central Standard Time" , "Australia/Adelaide" }, { "Australia_CentralWestern Standard Time", "Australia/Eucla" }, { "Australia_Eastern Standard Time" , "Australia/Sydney" }, { "Australia_Western Standard Time" , "Australia/Perth" }, { "Azerbaijan Standard Time" , "Asia/Baku" }, { "Azores Standard Time" , "Atlantic/Azores" }, { "Baku Standard Time" , "Asia/Baku" }, { "Bangladesh Standard Time" , "Asia/Dhaka" }, { "Bering Standard Time" , "America/Adak" }, { "Bhutan Standard Time" , "Asia/Thimphu" }, { "Bolivia Standard Time" , "America/La_Paz" }, { "Borneo Standard Time" , "Asia/Kuching" }, { "Brasilia Standard Time" , "America/Sao_Paulo" }, { "British Standard Time" , "Europe/London" }, { "Brunei Standard Time" , "Asia/Brunei" }, { "Canada Central Standard Time" , "America/Regina" }, { "Cape Verde Standard Time" , "Atlantic/Cape_Verde" }, { "Cape_Verde Standard Time" , "Atlantic/Cape_Verde" }, { "Caucasus Standard Time" , "Asia/Yerevan" }, { "Cen. Australia Standard Time" , "Australia/Adelaide" }, { "Central Standard Time" , "America/Chicago" }, { "Central America Standard Time" , "America/Guatemala" }, { "Central Asia Standard Time" , "Asia/Dhaka" }, { "Central Brazilian Standard Time" , "America/Manaus" }, { "Central Europe Standard Time" , "Europe/Prague" }, { "Central European Standard Time" , "Europe/Warsaw" }, { "Central Pacific Standard Time" , "Pacific/Guadalcanal" }, { "Central Standard Time (Mexico)" , "America/Mexico_City" }, { "Chamorro Standard Time" , "Pacific/Guam" }, { "Changbai Standard Time" , "Asia/Harbin" }, { "Chatham Standard Time" , "Pacific/Chatham" }, { "Chile Standard Time" , "America/Santiago" }, { "China Standard Time" , "Asia/Taipei" }, { "Choibalsan Standard Time" , "Asia/Choibalsan" }, { "Christmas Standard Time" , "Indian/Christmas" }, { "Cocos Standard Time" , "Indian/Cocos" }, { "Colombia Standard Time" , "America/Bogota" }, { "Cook Standard Time" , "Pacific/Rarotonga" }, { "Cuba Standard Time" , "America/Havana" }, { "Dacca Standard Time" , "Asia/Dhaka" }, { "Dateline Standard Time" , "Pacific/Kwajalein" }, { "Davis Standard Time" , "Antarctica/Davis" }, { "Dominican Standard Time" , "America/Santo_Domingo" }, { "DumontDUrville Standard Time" , "Antarctica/DumontDUrville" }, { "Dushanbe Standard Time" , "Asia/Dushanbe" }, { "Dutch_Guiana Standard Time" , "America/Paramaribo" }, { "E. Africa Standard Time" , "Africa/Nairobi" }, { "E. Australia Standard Time" , "Australia/Brisbane" }, { "E. Europe Standard Time" , "Europe/Minsk" }, { "E. South America Standard Time" , "America/Sao_Paulo" }, { "East_Timor Standard Time" , "Asia/Dili" }, { "Easter Standard Time" , "Pacific/Easter" }, { "Eastern Standard Time" , "America/New_York" }, { "Ecuador Standard Time" , "America/Guayaquil" }, { "Egypt Standard Time" , "Africa/Cairo" }, { "Ekaterinburg Standard Time" , "Asia/Yekaterinburg" }, { "Europe_Central Standard Time" , "Europe/Oslo" }, { "Europe_Eastern Standard Time" , "Europe/Vilnius" }, { "Europe_Western Standard Time" , "Atlantic/Canary" }, { "FLE Standard Time" , "Europe/Helsinki" }, { "Falkland Standard Time" , "Atlantic/Stanley" }, { "Fiji Standard Time" , "Pacific/Fiji" }, { "French_Guiana Standard Time" , "America/Cayenne" }, { "French_Southern Standard Time" , "Indian/Kerguelen" }, { "Frunze Standard Time" , "Asia/Bishkek" }, { "GMT Standard Time" , "Europe/Dublin" }, { "GTB Standard Time" , "Europe/Istanbul" }, { "Galapagos Standard Time" , "Pacific/Galapagos" }, { "Gambier Standard Time" , "Pacific/Gambier" }, { "Georgia Standard Time" , "Asia/Tbilisi" }, { "Georgian Standard Time" , "Asia/Tbilisi" }, { "Gilbert_Islands Standard Time" , "Pacific/Tarawa" }, { "Goose_Bay Standard Time" , "America/Goose_Bay" }, { "Greenland Standard Time" , "America/Godthab" }, { "Greenland_Central Standard Time" , "America/Scoresbysund" }, { "Greenland_Eastern Standard Time" , "America/Scoresbysund" }, { "Greenland_Western Standard Time" , "America/Godthab" }, { "Greenwich Standard Time" , "Africa/Casablanca" }, { "Guam Standard Time" , "Pacific/Guam" }, { "Gulf Standard Time" , "Asia/Muscat" }, { "Guyana Standard Time" , "America/Guyana" }, { "Hawaii_Aleutian Standard Time" , "Pacific/Honolulu" }, { "Hawaiian Standard Time" , "Pacific/Honolulu" }, { "Hong_Kong Standard Time" , "Asia/Hong_Kong" }, { "Hovd Standard Time" , "Asia/Hovd" }, { "India Standard Time" , "Asia/Calcutta" }, { "Indian_Ocean Standard Time" , "Indian/Chagos" }, { "Indochina Standard Time" , "Asia/Vientiane" }, { "Indonesia_Central Standard Time" , "Asia/Makassar" }, { "Indonesia_Eastern Standard Time" , "Asia/Jayapura" }, { "Indonesia_Western Standard Time" , "Asia/Jakarta" }, { "Iran Standard Time" , "Asia/Tehran" }, { "Irish Standard Time" , "Europe/Dublin" }, { "Irkutsk Standard Time" , "Asia/Irkutsk" }, { "Israel Standard Time" , "Asia/Jerusalem" }, { "Japan Standard Time" , "Asia/Tokyo" }, { "Jordan Standard Time" , "Asia/Amman" }, { "Kamchatka Standard Time" , "Asia/Kamchatka" }, { "Karachi Standard Time" , "Asia/Karachi" }, { "Kashgar Standard Time" , "Asia/Kashgar" }, { "Kazakhstan_Eastern Standard Time" , "Asia/Almaty" }, { "Kazakhstan_Western Standard Time" , "Asia/Aqtobe" }, { "Kizilorda Standard Time" , "Asia/Qyzylorda" }, { "Korea Standard Time" , "Asia/Seoul" }, { "Kosrae Standard Time" , "Pacific/Kosrae" }, { "Krasnoyarsk Standard Time" , "Asia/Krasnoyarsk" }, { "Kuybyshev Standard Time" , "Europe/Samara" }, { "Kwajalein Standard Time" , "Pacific/Kwajalein" }, { "Kyrgystan Standard Time" , "Asia/Bishkek" }, { "Lanka Standard Time" , "Asia/Colombo" }, { "Liberia Standard Time" , "Africa/Monrovia" }, { "Line_Islands Standard Time" , "Pacific/Kiritimati" }, { "Long_Shu Standard Time" , "Asia/Chongqing" }, { "Lord_Howe Standard Time" , "Australia/Lord_Howe" }, { "Macau Standard Time" , "Asia/Macau" }, { "Magadan Standard Time" , "Asia/Magadan" }, { "Malaya Standard Time" , "Asia/Kuala_Lumpur" }, { "Malaysia Standard Time" , "Asia/Kuching" }, { "Maldives Standard Time" , "Indian/Maldives" }, { "Marquesas Standard Time" , "Pacific/Marquesas" }, { "Marshall_Islands Standard Time" , "Pacific/Majuro" }, { "Mauritius Standard Time" , "Indian/Mauritius" }, { "Mawson Standard Time" , "Antarctica/Mawson" }, { "Mexico Standard Time" , "America/Mexico_City" }, { "Mexico Standard Time 2 Standard Time" , "America/Chihuahua" }, { "Mid-Atlantic Standard Time" , "America/Noronha" }, { "Middle East Standard Time" , "Asia/Beirut" }, { "Mongolia Standard Time" , "Asia/Ulaanbaatar" }, { "Montevideo Standard Time" , "America/Montevideo" }, { "Moscow Standard Time" , "Europe/Moscow" }, { "Mountain Standard Time" , "America/Denver" }, { "Mountain Standard Time (Mexico)" , "America/Chihuahua" }, { "Myanmar Standard Time" , "Asia/Rangoon" }, { "N. Central Asia Standard Time" , "Asia/Novosibirsk" }, { "Namibia Standard Time" , "Africa/Windhoek" }, { "Nauru Standard Time" , "Pacific/Nauru" }, { "Nepal Standard Time" , "Asia/Katmandu" }, { "New Zealand Standard Time" , "Pacific/Auckland" }, { "New_Caledonia Standard Time" , "Pacific/Noumea" }, { "New_Zealand Standard Time" , "Pacific/Auckland" }, { "Newfoundland Standard Time" , "America/St_Johns" }, { "Niue Standard Time" , "Pacific/Niue" }, { "Norfolk Standard Time" , "Pacific/Norfolk" }, { "Noronha Standard Time" , "America/Noronha" }, { "North Asia Standard Time" , "Asia/Krasnoyarsk" }, { "North Asia East Standard Time" , "Asia/Ulaanbaatar" }, { "North_Mariana Standard Time" , "Pacific/Saipan" }, { "Novosibirsk Standard Time" , "Asia/Novosibirsk" }, { "Omsk Standard Time" , "Asia/Omsk" }, { "Oral Standard Time" , "Asia/Oral" }, { "Pacific Standard Time" , "America/Los_Angeles" }, { "Pacific SA Standard Time" , "America/Santiago" }, { "Pacific Standard Time (Mexico)" , "America/Tijuana" }, { "Pakistan Standard Time" , "Asia/Karachi" }, { "Palau Standard Time" , "Pacific/Palau" }, { "Papua_New_Guinea Standard Time" , "Pacific/Port_Moresby" }, { "Paraguay Standard Time" , "America/Asuncion" }, { "Peru Standard Time" , "America/Lima" }, { "Philippines Standard Time" , "Asia/Manila" }, { "Phoenix_Islands Standard Time" , "Pacific/Enderbury" }, { "Pierre_Miquelon Standard Time" , "America/Miquelon" }, { "Pitcairn Standard Time" , "Pacific/Pitcairn" }, { "Ponape Standard Time" , "Pacific/Ponape" }, { "Qyzylorda Standard Time" , "Asia/Qyzylorda" }, { "Reunion Standard Time" , "Indian/Reunion" }, { "Romance Standard Time" , "Europe/Paris" }, { "Rothera Standard Time" , "Antarctica/Rothera" }, { "Russian Standard Time" , "Europe/Moscow" }, { "SA Eastern Standard Time" , "America/Buenos_Aires" }, { "SA Pacific Standard Time" , "America/Bogota" }, { "SA Western Standard Time" , "America/Caracas" }, { "SE Asia Standard Time" , "Asia/Bangkok" }, { "Sakhalin Standard Time" , "Asia/Sakhalin" }, { "Samara Standard Time" , "Europe/Samara" }, { "Samarkand Standard Time" , "Asia/Samarkand" }, { "Samoa Standard Time" , "Pacific/Apia" }, { "Seychelles Standard Time" , "Indian/Mahe" }, { "Shevchenko Standard Time" , "Asia/Aqtau" }, { "Singapore Standard Time" , "Asia/Singapore" }, { "Solomon Standard Time" , "Pacific/Guadalcanal" }, { "South Africa Standard Time" , "Africa/Johannesburg" }, { "South_Georgia Standard Time" , "Atlantic/South_Georgia" }, { "Sri Lanka Standard Time" , "Asia/Colombo" }, { "Suriname Standard Time" , "America/Paramaribo" }, { "Sverdlovsk Standard Time" , "Asia/Yekaterinburg" }, { "Syowa Standard Time" , "Antarctica/Syowa" }, { "Tahiti Standard Time" , "Pacific/Tahiti" }, { "Taipei Standard Time" , "Asia/Taipei" }, { "Tajikistan Standard Time" , "Asia/Dushanbe" }, { "Tashkent Standard Time" , "Asia/Tashkent" }, { "Tasmania Standard Time" , "Australia/Hobart" }, { "Tbilisi Standard Time" , "Asia/Tbilisi" }, { "Tokelau Standard Time" , "Pacific/Fakaofo" }, { "Tokyo Standard Time" , "Asia/Tokyo" }, { "Tonga Standard Time" , "Pacific/Tongatapu" }, { "Truk Standard Time" , "Pacific/Truk" }, { "Turkey Standard Time" , "Europe/Istanbul" }, { "Turkmenistan Standard Time" , "Asia/Ashgabat" }, { "Tuvalu Standard Time" , "Pacific/Funafuti" }, { "US Eastern Standard Time" , "America/Indianapolis" }, { "US Mountain Standard Time" , "America/Phoenix" }, { "Uralsk Standard Time" , "Asia/Oral" }, { "Uruguay Standard Time" , "America/Montevideo" }, { "Urumqi Standard Time" , "Asia/Urumqi" }, { "Uzbekistan Standard Time" , "Asia/Tashkent" }, { "Vanuatu Standard Time" , "Pacific/Efate" }, { "Venezuela Standard Time" , "America/Caracas" }, { "Vladivostok Standard Time" , "Asia/Vladivostok" }, { "Volgograd Standard Time" , "Europe/Volgograd" }, { "Vostok Standard Time" , "Antarctica/Vostok" }, { "W. Australia Standard Time" , "Australia/Perth" }, { "W. Central Africa Standard Time" , "Africa/Lagos" }, { "W. Europe Standard Time" , "Europe/Berlin" }, { "Wake Standard Time" , "Pacific/Wake" }, { "Wallis Standard Time" , "Pacific/Wallis" }, { "West Asia Standard Time" , "Asia/Karachi" }, { "West Pacific Standard Time" , "Pacific/Guam" }, { "Yakutsk Standard Time" , "Asia/Yakutsk" }, { "Yekaterinburg Standard Time" , "Asia/Yekaterinburg" }, { "Yerevan Standard Time" , "Asia/Yerevan" }, { "Yukon Standard Time" , "America/Yakutat" }, { NULL, NULL } }; static const char* get_zoneinfo_timezone( void ) { if (!android_timezone_init) { char tzname[128]; time_t t = time(NULL); struct tm* tm = localtime(&t); const Win32Timezone* win32tz = _win32_timezones; android_timezone_init = 1; if (!tm) { D("%s: could not determine current date/time\n", __FUNCTION__); return NULL; } memset(tzname, 0, sizeof(tzname)); strftime(tzname, sizeof(tzname) - 1, "%Z", tm); for (win32tz = _win32_timezones; win32tz->win_name != NULL; win32tz++) if ( !strcmp(win32tz->win_name, tzname) ) { android_timezone = win32tz->zoneinfo_name; goto Exit; } #if 0 /* TODO */ /* we didn't find it, this may come from localized versions of Windows. we're going to explore the registry, * as the code in Postgresql does... */ #endif D( "%s: could not determine current timezone\n", __FUNCTION__ ); return NULL; } Exit: D( "emulator: found timezone %s\n", android_timezone ); return android_timezone; } #endif /* _WIN32 */