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