• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
check_timezone_is_zoneinfo(const char * tz)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
timezone_set(const char * tzname)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*
bufprint_zoneinfo_timezone(char * p,char * end)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*
get_zoneinfo_timezone(void)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
compare_timezone_to_localtime(ScanDataRec * scan,const char * path)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*
scan_timezone_dir(ScanDataRec * scan,char * top,int depth)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*
get_zoneinfo_timezone(void)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*
get_zoneinfo_timezone(void)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