diff options
Diffstat (limited to 'android/utils/timezone.c')
-rw-r--r-- | android/utils/timezone.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/android/utils/timezone.c b/android/utils/timezone.c new file mode 100644 index 0000000..b5588a3 --- /dev/null +++ b/android/utils/timezone.c @@ -0,0 +1,721 @@ +/* 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#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. + */ +#ifdef __APPLE__ + +#include <unistd.h> +#include <limits.h> +#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; + } + } + pstrcpy( android_timezone0, sizeof(android_timezone0), 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 + */ +#ifdef __linux__ + +#include <unistd.h> +#include <limits.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#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 (%lld != %lld)\n", st.st_size, 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/<something> where <something> 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; + + pstrcpy( android_timezone0, sizeof(android_timezone0), 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 <time.h> + +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 */ + |