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