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
38static const char* get_zoneinfo_timezone( void );  /* forward */
39
40static char         android_timezone0[256];
41static const char*  android_timezone;
42static int          android_timezone_init;
43
44static int
45check_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
66int
67timezone_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
86char*
87bufprint_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/"
106static const char*
107get_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
166typedef 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
174static int
175compare_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
233static const char*
234scan_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
291static const char*
292get_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
421typedef 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 */
427static 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
682static const char*
683get_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    }
716Exit:
717    D( "emulator: found timezone %s\n", android_timezone );
718    return android_timezone;
719}
720
721#endif /* _WIN32 */
722
723