Home | History | Annotate | Download | only in utils
      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