Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * findtimezone.c
4 : * Functions for determining the default timezone to use.
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/bin/initdb/findtimezone.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres_fe.h"
14 :
15 : #include <fcntl.h>
16 : #include <sys/stat.h>
17 : #include <time.h>
18 :
19 : #include "pgtz.h"
20 :
21 : /* Ideally this would be in a .h file, but it hardly seems worth the trouble */
22 : extern const char *select_default_timezone(const char *share_path);
23 :
24 :
25 : #ifndef SYSTEMTZDIR
26 : static char tzdirpath[MAXPGPATH];
27 : #endif
28 :
29 :
30 : /*
31 : * Return full pathname of timezone data directory
32 : *
33 : * In this file, tzdirpath is assumed to be set up by select_default_timezone.
34 : */
35 : static const char *
36 597 : pg_TZDIR(void)
37 : {
38 : #ifndef SYSTEMTZDIR
39 : /* normal case: timezone stuff is under our share dir */
40 597 : return tzdirpath;
41 : #else
42 : /* we're configured to use system's timezone database */
43 : return SYSTEMTZDIR;
44 : #endif
45 : }
46 :
47 :
48 : /*
49 : * Given a timezone name, open() the timezone data file. Return the
50 : * file descriptor if successful, -1 if not.
51 : *
52 : * This is simpler than the backend function of the same name because
53 : * we assume that the input string has the correct case already, so there
54 : * is no need for case-folding. (This is obviously true if we got the file
55 : * name from the filesystem to start with. The only other place it can come
56 : * from is the environment variable TZ, and there seems no need to allow
57 : * case variation in that; other programs aren't likely to.)
58 : *
59 : * If "canonname" is not NULL, then on success the canonical spelling of the
60 : * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
61 : * This is redundant but kept for compatibility with the backend code.
62 : */
63 : int
64 596 : pg_open_tzfile(const char *name, char *canonname)
65 : {
66 : char fullname[MAXPGPATH];
67 :
68 596 : if (canonname)
69 0 : strlcpy(canonname, name, TZ_STRLEN_MAX + 1);
70 :
71 596 : strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
72 596 : if (strlen(fullname) + 1 + strlen(name) >= MAXPGPATH)
73 0 : return -1; /* not gonna fit */
74 596 : strcat(fullname, "/");
75 596 : strcat(fullname, name);
76 :
77 596 : return open(fullname, O_RDONLY | PG_BINARY, 0);
78 : }
79 :
80 :
81 :
82 : /*
83 : * Load a timezone definition.
84 : * Does not verify that the timezone is acceptable!
85 : *
86 : * This corresponds to the backend's pg_tzset(), except that we only support
87 : * one loaded timezone at a time.
88 : */
89 : static pg_tz *
90 596 : pg_load_tz(const char *name)
91 : {
92 : static pg_tz tz;
93 :
94 596 : if (strlen(name) > TZ_STRLEN_MAX)
95 0 : return NULL; /* not going to fit */
96 :
97 : /*
98 : * "GMT" is always sent to tzparse(); see comments for pg_tzset().
99 : */
100 596 : if (strcmp(name, "GMT") == 0)
101 : {
102 1 : if (!tzparse(name, &tz.state, true))
103 : {
104 : /* This really, really should not happen ... */
105 0 : return NULL;
106 : }
107 : }
108 595 : else if (tzload(name, NULL, &tz.state, true) != 0)
109 : {
110 0 : if (name[0] == ':' || !tzparse(name, &tz.state, false))
111 : {
112 0 : return NULL; /* unknown timezone */
113 : }
114 : }
115 :
116 596 : strcpy(tz.TZname, name);
117 :
118 596 : return &tz;
119 : }
120 :
121 :
122 : /*
123 : * The following block of code attempts to determine which timezone in our
124 : * timezone database is the best match for the active system timezone.
125 : *
126 : * On most systems, we rely on trying to match the observable behavior of
127 : * the C library's localtime() function. The database zone that matches
128 : * furthest into the past is the one to use. Often there will be several
129 : * zones with identical rankings (since the Olson database assigns multiple
130 : * names to many zones). We break ties arbitrarily by preferring shorter,
131 : * then alphabetically earlier zone names.
132 : *
133 : * Win32's native knowledge about timezones appears to be too incomplete
134 : * and too different from the Olson database for the above matching strategy
135 : * to be of any use. But there is just a limited number of timezones
136 : * available, so we can rely on a handmade mapping table instead.
137 : */
138 :
139 : #ifndef WIN32
140 :
141 : #define T_DAY ((time_t) (60*60*24))
142 : #define T_WEEK ((time_t) (60*60*24*7))
143 : #define T_MONTH ((time_t) (60*60*24*31))
144 :
145 : #define MAX_TEST_TIMES (52*100) /* 100 years */
146 :
147 : struct tztry
148 : {
149 : int n_test_times;
150 : time_t test_times[MAX_TEST_TIMES];
151 : };
152 :
153 : static void scan_available_timezones(char *tzdir, char *tzdirsub,
154 : struct tztry *tt,
155 : int *bestscore, char *bestzonename);
156 :
157 :
158 : /*
159 : * Get GMT offset from a system struct tm
160 : */
161 : static int
162 0 : get_timezone_offset(struct tm *tm)
163 : {
164 : #if defined(HAVE_STRUCT_TM_TM_ZONE)
165 0 : return tm->tm_gmtoff;
166 : #elif defined(HAVE_INT_TIMEZONE)
167 : return -TIMEZONE_GLOBAL;
168 : #else
169 : #error No way to determine TZ? Can this happen?
170 : #endif
171 : }
172 :
173 : /*
174 : * Convenience subroutine to convert y/m/d to time_t (NOT pg_time_t)
175 : */
176 : static time_t
177 2 : build_time_t(int year, int month, int day)
178 : {
179 : struct tm tm;
180 :
181 2 : memset(&tm, 0, sizeof(tm));
182 2 : tm.tm_mday = day;
183 2 : tm.tm_mon = month - 1;
184 2 : tm.tm_year = year - 1900;
185 :
186 2 : return mktime(&tm);
187 : }
188 :
189 : /*
190 : * Does a system tm value match one we computed ourselves?
191 : */
192 : static bool
193 10776 : compare_tm(struct tm *s, struct pg_tm *p)
194 : {
195 21552 : if (s->tm_sec != p->tm_sec ||
196 21528 : s->tm_min != p->tm_min ||
197 20963 : s->tm_hour != p->tm_hour ||
198 20422 : s->tm_mday != p->tm_mday ||
199 20422 : s->tm_mon != p->tm_mon ||
200 20422 : s->tm_year != p->tm_year ||
201 20422 : s->tm_wday != p->tm_wday ||
202 20422 : s->tm_yday != p->tm_yday ||
203 10211 : s->tm_isdst != p->tm_isdst)
204 565 : return false;
205 10211 : return true;
206 : }
207 :
208 : /*
209 : * See how well a specific timezone setting matches the system behavior
210 : *
211 : * We score a timezone setting according to the number of test times it
212 : * matches. (The test times are ordered later-to-earlier, but this routine
213 : * doesn't actually know that; it just scans until the first non-match.)
214 : *
215 : * We return -1 for a completely unusable setting; this is worse than the
216 : * score of zero for a setting that works but matches not even the first
217 : * test time.
218 : */
219 : static int
220 595 : score_timezone(const char *tzname, struct tztry *tt)
221 : {
222 : int i;
223 : pg_time_t pgtt;
224 : struct tm *systm;
225 : struct pg_tm *pgtm;
226 : char cbuf[TZ_STRLEN_MAX + 1];
227 : pg_tz *tz;
228 :
229 : /* Load timezone definition */
230 595 : tz = pg_load_tz(tzname);
231 595 : if (!tz)
232 0 : return -1; /* unrecognized zone name */
233 :
234 : /* Reject if leap seconds involved */
235 595 : if (!pg_tz_acceptable(tz))
236 : {
237 : #ifdef DEBUG_IDENTIFY_TIMEZONE
238 : fprintf(stderr, "Reject TZ \"%s\": uses leap seconds\n", tzname);
239 : #endif
240 0 : return -1;
241 : }
242 :
243 : /* Check for match at all the test times */
244 10776 : for (i = 0; i < tt->n_test_times; i++)
245 : {
246 10776 : pgtt = (pg_time_t) (tt->test_times[i]);
247 10776 : pgtm = pg_localtime(&pgtt, tz);
248 10776 : if (!pgtm)
249 0 : return -1; /* probably shouldn't happen */
250 10776 : systm = localtime(&(tt->test_times[i]));
251 10776 : if (!systm)
252 : {
253 : #ifdef DEBUG_IDENTIFY_TIMEZONE
254 : fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data\n",
255 : tzname, i, (long) pgtt,
256 : pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
257 : pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
258 : pgtm->tm_isdst ? "dst" : "std");
259 : #endif
260 0 : return i;
261 : }
262 10776 : if (!compare_tm(systm, pgtm))
263 : {
264 : #ifdef DEBUG_IDENTIFY_TIMEZONE
265 : fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s\n",
266 : tzname, i, (long) pgtt,
267 : pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
268 : pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
269 : pgtm->tm_isdst ? "dst" : "std",
270 : systm->tm_year + 1900, systm->tm_mon + 1, systm->tm_mday,
271 : systm->tm_hour, systm->tm_min, systm->tm_sec,
272 : systm->tm_isdst ? "dst" : "std");
273 : #endif
274 565 : return i;
275 : }
276 10211 : if (systm->tm_isdst >= 0)
277 : {
278 : /* Check match of zone names, too */
279 10211 : if (pgtm->tm_zone == NULL)
280 0 : return -1; /* probably shouldn't happen */
281 10211 : memset(cbuf, 0, sizeof(cbuf));
282 10211 : strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm); /* zone abbr */
283 10211 : if (strcmp(cbuf, pgtm->tm_zone) != 0)
284 : {
285 : #ifdef DEBUG_IDENTIFY_TIMEZONE
286 : fprintf(stderr, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"\n",
287 : tzname, i, (long) pgtt,
288 : pgtm->tm_zone, cbuf);
289 : #endif
290 30 : return i;
291 : }
292 : }
293 : }
294 :
295 : #ifdef DEBUG_IDENTIFY_TIMEZONE
296 : fprintf(stderr, "TZ \"%s\" gets max score %d\n", tzname, i);
297 : #endif
298 :
299 0 : return i;
300 : }
301 :
302 :
303 : /*
304 : * Try to identify a timezone name (in our terminology) that best matches the
305 : * observed behavior of the system timezone library. We cannot assume that
306 : * the system TZ environment setting (if indeed there is one) matches our
307 : * terminology, so we ignore it and just look at what localtime() returns.
308 : */
309 : static const char *
310 1 : identify_system_timezone(void)
311 : {
312 : static char resultbuf[TZ_STRLEN_MAX + 1];
313 : time_t tnow;
314 : time_t t;
315 : struct tztry tt;
316 : struct tm *tm;
317 : int thisyear;
318 : int bestscore;
319 : char tmptzdir[MAXPGPATH];
320 : int std_ofs;
321 : char std_zone_name[TZ_STRLEN_MAX + 1],
322 : dst_zone_name[TZ_STRLEN_MAX + 1];
323 : char cbuf[TZ_STRLEN_MAX + 1];
324 :
325 : /* Initialize OS timezone library */
326 1 : tzset();
327 :
328 : /*
329 : * Set up the list of dates to be probed to see how well our timezone
330 : * matches the system zone. We first probe January and July of the
331 : * current year; this serves to quickly eliminate the vast majority of the
332 : * TZ database entries. If those dates match, we probe every week for 100
333 : * years backwards from the current July. (Weekly resolution is good
334 : * enough to identify DST transition rules, since everybody switches on
335 : * Sundays.) This is sufficient to cover most of the Unix time_t range,
336 : * and we don't want to look further than that since many systems won't
337 : * have sane TZ behavior further back anyway. The further back the zone
338 : * matches, the better we score it. This may seem like a rather random
339 : * way of doing things, but experience has shown that system-supplied
340 : * timezone definitions are likely to have DST behavior that is right for
341 : * the recent past and not so accurate further back. Scoring in this way
342 : * allows us to recognize zones that have some commonality with the Olson
343 : * database, without insisting on exact match. (Note: we probe Thursdays,
344 : * not Sundays, to avoid triggering DST-transition bugs in localtime
345 : * itself.)
346 : */
347 1 : tnow = time(NULL);
348 1 : tm = localtime(&tnow);
349 1 : if (!tm)
350 0 : return NULL; /* give up if localtime is broken... */
351 1 : thisyear = tm->tm_year + 1900;
352 :
353 1 : t = build_time_t(thisyear, 1, 15);
354 :
355 : /*
356 : * Round back to GMT midnight Thursday. This depends on the knowledge
357 : * that the time_t origin is Thu Jan 01 1970. (With a different origin
358 : * we'd be probing some other day of the week, but it wouldn't matter
359 : * anyway unless localtime() had DST-transition bugs.)
360 : */
361 1 : t -= (t % T_WEEK);
362 :
363 1 : tt.n_test_times = 0;
364 1 : tt.test_times[tt.n_test_times++] = t;
365 :
366 1 : t = build_time_t(thisyear, 7, 15);
367 1 : t -= (t % T_WEEK);
368 :
369 1 : tt.test_times[tt.n_test_times++] = t;
370 :
371 5200 : while (tt.n_test_times < MAX_TEST_TIMES)
372 : {
373 5198 : t -= T_WEEK;
374 5198 : tt.test_times[tt.n_test_times++] = t;
375 : }
376 :
377 : /* Search for the best-matching timezone file */
378 1 : strlcpy(tmptzdir, pg_TZDIR(), sizeof(tmptzdir));
379 1 : bestscore = -1;
380 1 : resultbuf[0] = '\0';
381 1 : scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1,
382 : &tt,
383 : &bestscore, resultbuf);
384 1 : if (bestscore > 0)
385 : {
386 : /* Ignore Olson's rather silly "Factory" zone; use GMT instead */
387 1 : if (strcmp(resultbuf, "Factory") == 0)
388 0 : return NULL;
389 1 : return resultbuf;
390 : }
391 :
392 : /*
393 : * Couldn't find a match in the database, so next we try constructed zone
394 : * names (like "PST8PDT").
395 : *
396 : * First we need to determine the names of the local standard and daylight
397 : * zones. The idea here is to scan forward from today until we have seen
398 : * both zones, if both are in use.
399 : */
400 0 : memset(std_zone_name, 0, sizeof(std_zone_name));
401 0 : memset(dst_zone_name, 0, sizeof(dst_zone_name));
402 0 : std_ofs = 0;
403 :
404 0 : tnow = time(NULL);
405 :
406 : /*
407 : * Round back to a GMT midnight so results don't depend on local time of
408 : * day
409 : */
410 0 : tnow -= (tnow % T_DAY);
411 :
412 : /*
413 : * We have to look a little further ahead than one year, in case today is
414 : * just past a DST boundary that falls earlier in the year than the next
415 : * similar boundary. Arbitrarily scan up to 14 months.
416 : */
417 0 : for (t = tnow; t <= tnow + T_MONTH * 14; t += T_MONTH)
418 : {
419 0 : tm = localtime(&t);
420 0 : if (!tm)
421 0 : continue;
422 0 : if (tm->tm_isdst < 0)
423 0 : continue;
424 0 : if (tm->tm_isdst == 0 && std_zone_name[0] == '\0')
425 : {
426 : /* found STD zone */
427 0 : memset(cbuf, 0, sizeof(cbuf));
428 0 : strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
429 0 : strcpy(std_zone_name, cbuf);
430 0 : std_ofs = get_timezone_offset(tm);
431 : }
432 0 : if (tm->tm_isdst > 0 && dst_zone_name[0] == '\0')
433 : {
434 : /* found DST zone */
435 0 : memset(cbuf, 0, sizeof(cbuf));
436 0 : strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
437 0 : strcpy(dst_zone_name, cbuf);
438 : }
439 : /* Done if found both */
440 0 : if (std_zone_name[0] && dst_zone_name[0])
441 0 : break;
442 : }
443 :
444 : /* We should have found a STD zone name by now... */
445 0 : if (std_zone_name[0] == '\0')
446 : {
447 : #ifdef DEBUG_IDENTIFY_TIMEZONE
448 : fprintf(stderr, "could not determine system time zone\n");
449 : #endif
450 0 : return NULL; /* go to GMT */
451 : }
452 :
453 : /* If we found DST then try STD<ofs>DST */
454 0 : if (dst_zone_name[0] != '\0')
455 : {
456 0 : snprintf(resultbuf, sizeof(resultbuf), "%s%d%s",
457 0 : std_zone_name, -std_ofs / 3600, dst_zone_name);
458 0 : if (score_timezone(resultbuf, &tt) > 0)
459 0 : return resultbuf;
460 : }
461 :
462 : /* Try just the STD timezone (works for GMT at least) */
463 0 : strcpy(resultbuf, std_zone_name);
464 0 : if (score_timezone(resultbuf, &tt) > 0)
465 0 : return resultbuf;
466 :
467 : /* Try STD<ofs> */
468 0 : snprintf(resultbuf, sizeof(resultbuf), "%s%d",
469 0 : std_zone_name, -std_ofs / 3600);
470 0 : if (score_timezone(resultbuf, &tt) > 0)
471 0 : return resultbuf;
472 :
473 : /*
474 : * Did not find the timezone. Fallback to use a GMT zone. Note that the
475 : * Olson timezone database names the GMT-offset zones in POSIX style: plus
476 : * is west of Greenwich. It's unfortunate that this is opposite of SQL
477 : * conventions. Should we therefore change the names? Probably not...
478 : */
479 0 : snprintf(resultbuf, sizeof(resultbuf), "Etc/GMT%s%d",
480 0 : (-std_ofs > 0) ? "+" : "", -std_ofs / 3600);
481 :
482 : #ifdef DEBUG_IDENTIFY_TIMEZONE
483 : fprintf(stderr, "could not recognize system time zone, using \"%s\"\n",
484 : resultbuf);
485 : #endif
486 0 : return resultbuf;
487 : }
488 :
489 : /*
490 : * Recursively scan the timezone database looking for the best match to
491 : * the system timezone behavior.
492 : *
493 : * tzdir points to a buffer of size MAXPGPATH. On entry, it holds the
494 : * pathname of a directory containing TZ files. We internally modify it
495 : * to hold pathnames of sub-directories and files, but must restore it
496 : * to its original contents before exit.
497 : *
498 : * tzdirsub points to the part of tzdir that represents the subfile name
499 : * (ie, tzdir + the original directory name length, plus one for the
500 : * first added '/').
501 : *
502 : * tt tells about the system timezone behavior we need to match.
503 : *
504 : * *bestscore and *bestzonename on entry hold the best score found so far
505 : * and the name of the best zone. We overwrite them if we find a better
506 : * score. bestzonename must be a buffer of length TZ_STRLEN_MAX + 1.
507 : */
508 : static void
509 21 : scan_available_timezones(char *tzdir, char *tzdirsub, struct tztry *tt,
510 : int *bestscore, char *bestzonename)
511 : {
512 21 : int tzdir_orig_len = strlen(tzdir);
513 : char **names;
514 : char **namep;
515 :
516 21 : names = pgfnames(tzdir);
517 21 : if (!names)
518 21 : return;
519 :
520 636 : for (namep = names; *namep; namep++)
521 : {
522 615 : char *name = *namep;
523 : struct stat statbuf;
524 :
525 : /* Ignore . and .., plus any other "hidden" files */
526 615 : if (name[0] == '.')
527 0 : continue;
528 :
529 615 : snprintf(tzdir + tzdir_orig_len, MAXPGPATH - tzdir_orig_len,
530 : "/%s", name);
531 :
532 615 : if (stat(tzdir, &statbuf) != 0)
533 : {
534 : #ifdef DEBUG_IDENTIFY_TIMEZONE
535 : fprintf(stderr, "could not stat \"%s\": %s\n",
536 : tzdir, strerror(errno));
537 : #endif
538 0 : tzdir[tzdir_orig_len] = '\0';
539 0 : continue;
540 : }
541 :
542 615 : if (S_ISDIR(statbuf.st_mode))
543 : {
544 : /* Recurse into subdirectory */
545 20 : scan_available_timezones(tzdir, tzdirsub, tt,
546 : bestscore, bestzonename);
547 : }
548 : else
549 : {
550 : /* Load and test this file */
551 595 : int score = score_timezone(tzdirsub, tt);
552 :
553 595 : if (score > *bestscore)
554 : {
555 3 : *bestscore = score;
556 3 : strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
557 : }
558 592 : else if (score == *bestscore)
559 : {
560 : /* Consider how to break a tie */
561 367 : if (strlen(tzdirsub) < strlen(bestzonename) ||
562 184 : (strlen(tzdirsub) == strlen(bestzonename) &&
563 2 : strcmp(tzdirsub, bestzonename) < 0))
564 4 : strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
565 : }
566 : }
567 :
568 : /* Restore tzdir */
569 615 : tzdir[tzdir_orig_len] = '\0';
570 : }
571 :
572 21 : pgfnames_cleanup(names);
573 : }
574 : #else /* WIN32 */
575 :
576 : static const struct
577 : {
578 : const char *stdname; /* Windows name of standard timezone */
579 : const char *dstname; /* Windows name of daylight timezone */
580 : const char *pgtzname; /* Name of pgsql timezone to map to */
581 : } win32_tzmap[] =
582 :
583 : {
584 : /*
585 : * This list was built from the contents of the registry at
586 : * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time
587 : * Zones on Windows 10 and Windows 7.
588 : *
589 : * The zones have been matched to Olson timezones by looking at the cities
590 : * listed in the win32 display name (in the comment here) in most cases.
591 : */
592 : {
593 : "Afghanistan Standard Time", "Afghanistan Daylight Time",
594 : "Asia/Kabul"
595 : }, /* (UTC+04:30) Kabul */
596 : {
597 : "Alaskan Standard Time", "Alaskan Daylight Time",
598 : "US/Alaska"
599 : }, /* (UTC-09:00) Alaska */
600 : {
601 : "Aleutian Standard Time", "Aleutian Daylight Time",
602 : "US/Aleutan"
603 : }, /* (UTC-10:00) Aleutian Islands */
604 : {
605 : "Altai Standard Time", "Altai Daylight Time",
606 : "Asia/Barnaul"
607 : }, /* (UTC+07:00) Barnaul, Gorno-Altaysk */
608 : {
609 : "Arab Standard Time", "Arab Daylight Time",
610 : "Asia/Kuwait"
611 : }, /* (UTC+03:00) Kuwait, Riyadh */
612 : {
613 : "Arabian Standard Time", "Arabian Daylight Time",
614 : "Asia/Muscat"
615 : }, /* (UTC+04:00) Abu Dhabi, Muscat */
616 : {
617 : "Arabic Standard Time", "Arabic Daylight Time",
618 : "Asia/Baghdad"
619 : }, /* (UTC+03:00) Baghdad */
620 : {
621 : "Argentina Standard Time", "Argentina Daylight Time",
622 : "America/Buenos_Aires"
623 : }, /* (UTC-03:00) City of Buenos Aires */
624 : {
625 : "Armenian Standard Time", "Armenian Daylight Time",
626 : "Asia/Yerevan"
627 : }, /* (UTC+04:00) Baku, Tbilisi, Yerevan */
628 : {
629 : "Astrakhan Standard Time", "Astrakhan Daylight Time",
630 : "Europe/Astrakhan"
631 : }, /* (UTC+04:00) Astrakhan, Ulyanovsk */
632 : {
633 : "Atlantic Standard Time", "Atlantic Daylight Time",
634 : "Canada/Atlantic"
635 : }, /* (UTC-04:00) Atlantic Time (Canada) */
636 : {
637 : "AUS Central Standard Time", "AUS Central Daylight Time",
638 : "Australia/Darwin"
639 : }, /* (UTC+09:30) Darwin */
640 : {
641 : "Aus Central W. Standard Time", "Aus Central W. Daylight Time",
642 : "Australia/Eucla"
643 : }, /* (UTC+08:45) Eucla */
644 : {
645 : "AUS Eastern Standard Time", "AUS Eastern Daylight Time",
646 : "Australia/Canberra"
647 : }, /* (UTC+10:00) Canberra, Melbourne, Sydney */
648 : {
649 : "Azerbaijan Standard Time", "Azerbaijan Daylight Time",
650 : "Asia/Baku"
651 : }, /* (UTC+04:00) Baku */
652 : {
653 : "Azores Standard Time", "Azores Daylight Time",
654 : "Atlantic/Azores"
655 : }, /* (UTC-01:00) Azores */
656 : {
657 : "Bahia Standard Time", "Bahia Daylight Time",
658 : "America/Salvador"
659 : }, /* (UTC-03:00) Salvador */
660 : {
661 : "Bangladesh Standard Time", "Bangladesh Daylight Time",
662 : "Asia/Dhaka"
663 : }, /* (UTC+06:00) Dhaka */
664 : {
665 : "Bougainville Standard Time", "Bougainville Daylight Time",
666 : "Pacific/Bougainville"
667 : }, /* (UTC+11:00) Bougainville Island */
668 : {
669 : "Belarus Standard Time", "Belarus Daylight Time",
670 : "Europe/Minsk"
671 : }, /* (UTC+03:00) Minsk */
672 : {
673 : "Cabo Verde Standard Time", "Cabo Verde Daylight Time",
674 : "Atlantic/Cape_Verde"
675 : }, /* (UTC-01:00) Cabo Verde Is. */
676 : {
677 : "Chatham Islands Standard Time", "Chatham Islands Daylight Time",
678 : "Pacific/Chatham"
679 : }, /* (UTC+12:45) Chatham Islands */
680 : {
681 : "Canada Central Standard Time", "Canada Central Daylight Time",
682 : "Canada/Saskatchewan"
683 : }, /* (UTC-06:00) Saskatchewan */
684 : {
685 : "Cape Verde Standard Time", "Cape Verde Daylight Time",
686 : "Atlantic/Cape_Verde"
687 : }, /* (UTC-01:00) Cape Verde Is. */
688 : {
689 : "Caucasus Standard Time", "Caucasus Daylight Time",
690 : "Asia/Baku"
691 : }, /* (UTC+04:00) Yerevan */
692 : {
693 : "Cen. Australia Standard Time", "Cen. Australia Daylight Time",
694 : "Australia/Adelaide"
695 : }, /* (UTC+09:30) Adelaide */
696 : /* Central America (other than Mexico) generally does not observe DST */
697 : {
698 : "Central America Standard Time", "Central America Daylight Time",
699 : "CST6"
700 : }, /* (UTC-06:00) Central America */
701 : {
702 : "Central Asia Standard Time", "Central Asia Daylight Time",
703 : "Asia/Dhaka"
704 : }, /* (UTC+06:00) Astana */
705 : {
706 : "Central Brazilian Standard Time", "Central Brazilian Daylight Time",
707 : "America/Cuiaba"
708 : }, /* (UTC-04:00) Cuiaba */
709 : {
710 : "Central Europe Standard Time", "Central Europe Daylight Time",
711 : "Europe/Belgrade"
712 : }, /* (UTC+01:00) Belgrade, Bratislava, Budapest,
713 : * Ljubljana, Prague */
714 : {
715 : "Central European Standard Time", "Central European Daylight Time",
716 : "Europe/Sarajevo"
717 : }, /* (UTC+01:00) Sarajevo, Skopje, Warsaw,
718 : * Zagreb */
719 : {
720 : "Central Pacific Standard Time", "Central Pacific Daylight Time",
721 : "Pacific/Noumea"
722 : }, /* (UTC+11:00) Solomon Is., New Caledonia */
723 : {
724 : "Central Standard Time", "Central Daylight Time",
725 : "US/Central"
726 : }, /* (UTC-06:00) Central Time (US & Canada) */
727 : {
728 : "Central Standard Time (Mexico)", "Central Daylight Time (Mexico)",
729 : "America/Mexico_City"
730 : }, /* (UTC-06:00) Guadalajara, Mexico City,
731 : * Monterrey */
732 : {
733 : "China Standard Time", "China Daylight Time",
734 : "Asia/Hong_Kong"
735 : }, /* (UTC+08:00) Beijing, Chongqing, Hong Kong,
736 : * Urumqi */
737 : {
738 : "Cuba Standard Time", "Cuba Daylight Time",
739 : "America/Havana"
740 : }, /* (UTC-05:00) Havana */
741 : {
742 : "Dateline Standard Time", "Dateline Daylight Time",
743 : "Etc/UTC+12"
744 : }, /* (UTC-12:00) International Date Line West */
745 : {
746 : "E. Africa Standard Time", "E. Africa Daylight Time",
747 : "Africa/Nairobi"
748 : }, /* (UTC+03:00) Nairobi */
749 : {
750 : "E. Australia Standard Time", "E. Australia Daylight Time",
751 : "Australia/Brisbane"
752 : }, /* (UTC+10:00) Brisbane */
753 : {
754 : "E. Europe Standard Time", "E. Europe Daylight Time",
755 : "Europe/Bucharest"
756 : }, /* (UTC+02:00) E. Europe */
757 : {
758 : "E. South America Standard Time", "E. South America Daylight Time",
759 : "America/Araguaina"
760 : }, /* (UTC-03:00) Brasilia */
761 : {
762 : "Eastern Standard Time", "Eastern Daylight Time",
763 : "US/Eastern"
764 : }, /* (UTC-05:00) Eastern Time (US & Canada) */
765 : {
766 : "Eastern Standard Time (Mexico)", "Eastern Daylight Time (Mexico)",
767 : "America/Mexico_City"
768 : }, /* (UTC-05:00) Chetumal */
769 : {
770 : "Easter Island Standard Time", "Easter Island Daylight Time",
771 : "Pacific/Easter"
772 : }, /* (UTC-06:00) Easter Island */
773 : {
774 : "Egypt Standard Time", "Egypt Daylight Time",
775 : "Africa/Cairo"
776 : }, /* (UTC+02:00) Cairo */
777 : {
778 : "Ekaterinburg Standard Time (RTZ 4)", "Ekaterinburg Daylight Time",
779 : "Asia/Yekaterinburg"
780 : }, /* (UTC+05:00) Ekaterinburg */
781 : {
782 : "Fiji Standard Time", "Fiji Daylight Time",
783 : "Pacific/Fiji"
784 : }, /* (UTC+12:00) Fiji */
785 : {
786 : "FLE Standard Time", "FLE Daylight Time",
787 : "Europe/Helsinki"
788 : }, /* (UTC+02:00) Helsinki, Kyiv, Riga, Sofia,
789 : * Tallinn, Vilnius */
790 : {
791 : "Georgian Standard Time", "Georgian Daylight Time",
792 : "Asia/Tbilisi"
793 : }, /* (UTC+04:00) Tbilisi */
794 : {
795 : "GMT Standard Time", "GMT Daylight Time",
796 : "Europe/London"
797 : }, /* (UTC) Dublin, Edinburgh, Lisbon, London */
798 : {
799 : "Greenland Standard Time", "Greenland Daylight Time",
800 : "America/Godthab"
801 : }, /* (UTC-03:00) Greenland */
802 : {
803 : "Greenwich Standard Time", "Greenwich Daylight Time",
804 : "Africa/Casablanca"
805 : }, /* (UTC) Monrovia, Reykjavik */
806 : {
807 : "GTB Standard Time", "GTB Daylight Time",
808 : "Europe/Athens"
809 : }, /* (UTC+02:00) Athens, Bucharest */
810 : {
811 : "Haiti Standard Time", "Haiti Daylight Time",
812 : "US/Eastern"
813 : }, /* (UTC-05:00) Haiti */
814 : {
815 : "Hawaiian Standard Time", "Hawaiian Daylight Time",
816 : "US/Hawaii"
817 : }, /* (UTC-10:00) Hawaii */
818 : {
819 : "India Standard Time", "India Daylight Time",
820 : "Asia/Calcutta"
821 : }, /* (UTC+05:30) Chennai, Kolkata, Mumbai, New
822 : * Delhi */
823 : {
824 : "Iran Standard Time", "Iran Daylight Time",
825 : "Asia/Tehran"
826 : }, /* (UTC+03:30) Tehran */
827 : {
828 : "Jerusalem Standard Time", "Jerusalem Daylight Time",
829 : "Asia/Jerusalem"
830 : }, /* (UTC+02:00) Jerusalem */
831 : {
832 : "Jordan Standard Time", "Jordan Daylight Time",
833 : "Asia/Amman"
834 : }, /* (UTC+02:00) Amman */
835 : {
836 : "Kamchatka Standard Time", "Kamchatka Daylight Time",
837 : "Asia/Kamchatka"
838 : }, /* (UTC+12:00) Petropavlovsk-Kamchatsky - Old */
839 : {
840 : "Korea Standard Time", "Korea Daylight Time",
841 : "Asia/Seoul"
842 : }, /* (UTC+09:00) Seoul */
843 : {
844 : "Libya Standard Time", "Libya Daylight Time",
845 : "Africa/Tripoli"
846 : }, /* (UTC+02:00) Tripoli */
847 : {
848 : "Line Islands Standard Time", "Line Islands Daylight Time",
849 : "Pacific/Kiritimati"
850 : }, /* (UTC+14:00) Kiritimati Island */
851 : {
852 : "Lord Howe Standard Time", "Lord Howe Daylight Time",
853 : "Australia/Lord_Howe"
854 : }, /* (UTC+10:30) Lord Howe Island */
855 : {
856 : "Magadan Standard Time", "Magadan Daylight Time",
857 : "Asia/Magadan"
858 : }, /* (UTC+10:00) Magadan */
859 : {
860 : "Marquesas Standard Time", "Marquesas Daylight Time",
861 : "Pacific/Marquesas"
862 : }, /* (UTC-09:30) Marquesas Islands */
863 : {
864 : "Mauritius Standard Time", "Mauritius Daylight Time",
865 : "Indian/Mauritius"
866 : }, /* (UTC+04:00) Port Louis */
867 : {
868 : "Mexico Standard Time", "Mexico Daylight Time",
869 : "America/Mexico_City"
870 : }, /* (UTC-06:00) Guadalajara, Mexico City,
871 : * Monterrey */
872 : {
873 : "Mexico Standard Time 2", "Mexico Daylight Time 2",
874 : "America/Chihuahua"
875 : }, /* (UTC-07:00) Chihuahua, La Paz, Mazatlan */
876 : {
877 : "Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
878 : "Atlantic/South_Georgia"
879 : }, /* (UTC-02:00) Mid-Atlantic - Old */
880 : {
881 : "Middle East Standard Time", "Middle East Daylight Time",
882 : "Asia/Beirut"
883 : }, /* (UTC+02:00) Beirut */
884 : {
885 : "Montevideo Standard Time", "Montevideo Daylight Time",
886 : "America/Montevideo"
887 : }, /* (UTC-03:00) Montevideo */
888 : {
889 : "Morocco Standard Time", "Morocco Daylight Time",
890 : "Africa/Casablanca"
891 : }, /* (UTC) Casablanca */
892 : {
893 : "Mountain Standard Time", "Mountain Daylight Time",
894 : "US/Mountain"
895 : }, /* (UTC-07:00) Mountain Time (US & Canada) */
896 : {
897 : "Mountain Standard Time (Mexico)", "Mountain Daylight Time (Mexico)",
898 : "America/Chihuahua"
899 : }, /* (UTC-07:00) Chihuahua, La Paz, Mazatlan */
900 : {
901 : "Myanmar Standard Time", "Myanmar Daylight Time",
902 : "Asia/Rangoon"
903 : }, /* (UTC+06:30) Yangon (Rangoon) */
904 : {
905 : "N. Central Asia Standard Time", "N. Central Asia Daylight Time",
906 : "Asia/Novosibirsk"
907 : }, /* (UTC+06:00) Novosibirsk (RTZ 5) */
908 : {
909 : "Namibia Standard Time", "Namibia Daylight Time",
910 : "Africa/Windhoek"
911 : }, /* (UTC+01:00) Windhoek */
912 : {
913 : "Nepal Standard Time", "Nepal Daylight Time",
914 : "Asia/Katmandu"
915 : }, /* (UTC+05:45) Kathmandu */
916 : {
917 : "New Zealand Standard Time", "New Zealand Daylight Time",
918 : "Pacific/Auckland"
919 : }, /* (UTC+12:00) Auckland, Wellington */
920 : {
921 : "Newfoundland Standard Time", "Newfoundland Daylight Time",
922 : "Canada/Newfoundland"
923 : }, /* (UTC-03:30) Newfoundland */
924 : {
925 : "Norfolk Standard Time", "Norfolk Daylight Time",
926 : "Pacific/Norfolk"
927 : }, /* (UTC+11:00) Norfolk Island */
928 : {
929 : "North Asia East Standard Time", "North Asia East Daylight Time",
930 : "Asia/Irkutsk"
931 : }, /* (UTC+08:00) Irkutsk, Ulaan Bataar */
932 : {
933 : "North Asia Standard Time", "North Asia Daylight Time",
934 : "Asia/Krasnoyarsk"
935 : }, /* (UTC+07:00) Krasnoyarsk */
936 : {
937 : "North Korea Standard Time", "North Korea Daylight Time",
938 : "Asia/Pyongyang"
939 : }, /* (UTC+08:30) Pyongyang */
940 : {
941 : "Pacific SA Standard Time", "Pacific SA Daylight Time",
942 : "America/Santiago"
943 : }, /* (UTC-03:00) Santiago */
944 : {
945 : "Pacific Standard Time", "Pacific Daylight Time",
946 : "US/Pacific"
947 : }, /* (UTC-08:00) Pacific Time (US & Canada) */
948 : {
949 : "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)",
950 : "America/Tijuana"
951 : }, /* (UTC-08:00) Baja California */
952 : {
953 : "Pakistan Standard Time", "Pakistan Daylight Time",
954 : "Asia/Karachi"
955 : }, /* (UTC+05:00) Islamabad, Karachi */
956 : {
957 : "Paraguay Standard Time", "Paraguay Daylight Time",
958 : "America/Asuncion"
959 : }, /* (UTC-04:00) Asuncion */
960 : {
961 : "Romance Standard Time", "Romance Daylight Time",
962 : "Europe/Brussels"
963 : }, /* (UTC+01:00) Brussels, Copenhagen, Madrid,
964 : * Paris */
965 : {
966 : "Russia TZ 1 Standard Time", "Russia TZ 1 Daylight Time",
967 : "Europe/Kaliningrad"
968 : }, /* (UTC+02:00) Kaliningrad (RTZ 1) */
969 : {
970 : "Russia TZ 2 Standard Time", "Russia TZ 2 Daylight Time",
971 : "Europe/Moscow"
972 : }, /* (UTC+03:00) Moscow, St. Petersburg,
973 : * Volgograd (RTZ 2) */
974 : {
975 : "Russia TZ 3 Standard Time", "Russia TZ 3 Daylight Time",
976 : "Europe/Samara"
977 : }, /* (UTC+04:00) Izhevsk, Samara (RTZ 3) */
978 : {
979 : "Russia TZ 4 Standard Time", "Russia TZ 4 Daylight Time",
980 : "Asia/Yekaterinburg"
981 : }, /* (UTC+05:00) Ekaterinburg (RTZ 4) */
982 : {
983 : "Russia TZ 5 Standard Time", "Russia TZ 5 Daylight Time",
984 : "Asia/Novosibirsk"
985 : }, /* (UTC+06:00) Novosibirsk (RTZ 5) */
986 : {
987 : "Russia TZ 6 Standard Time", "Russia TZ 6 Daylight Time",
988 : "Asia/Krasnoyarsk"
989 : }, /* (UTC+07:00) Krasnoyarsk (RTZ 6) */
990 : {
991 : "Russia TZ 7 Standard Time", "Russia TZ 7 Daylight Time",
992 : "Asia/Irkutsk"
993 : }, /* (UTC+08:00) Irkutsk (RTZ 7) */
994 : {
995 : "Russia TZ 8 Standard Time", "Russia TZ 8 Daylight Time",
996 : "Asia/Yakutsk"
997 : }, /* (UTC+09:00) Yakutsk (RTZ 8) */
998 : {
999 : "Russia TZ 9 Standard Time", "Russia TZ 9 Daylight Time",
1000 : "Asia/Vladivostok"
1001 : }, /* (UTC+10:00) Vladivostok, Magadan (RTZ 9) */
1002 : {
1003 : "Russia TZ 10 Standard Time", "Russia TZ 10 Daylight Time",
1004 : "Asia/Magadan"
1005 : }, /* (UTC+11:00) Chokurdakh (RTZ 10) */
1006 : {
1007 : "Russia TZ 11 Standard Time", "Russia TZ 11 Daylight Time",
1008 : "Asia/Anadyr"
1009 : }, /* (UTC+12:00) Anadyr,
1010 : * Petropavlovsk-Kamchatsky (RTZ 11) */
1011 : {
1012 : "Russian Standard Time", "Russian Daylight Time",
1013 : "Europe/Moscow"
1014 : }, /* (UTC+03:00) Moscow, St. Petersburg,
1015 : * Volgograd */
1016 : {
1017 : "SA Eastern Standard Time", "SA Eastern Daylight Time",
1018 : "America/Buenos_Aires"
1019 : }, /* (UTC-03:00) Cayenne, Fortaleza */
1020 : {
1021 : "SA Pacific Standard Time", "SA Pacific Daylight Time",
1022 : "America/Bogota"
1023 : }, /* (UTC-05:00) Bogota, Lima, Quito, Rio Branco */
1024 : {
1025 : "SA Western Standard Time", "SA Western Daylight Time",
1026 : "America/Caracas"
1027 : }, /* (UTC-04:00) Georgetown, La Paz, Manaus, San
1028 : * Juan */
1029 : {
1030 : "Saint Pierre Standard Time", "Saint Pierre Daylight Time",
1031 : "America/Miquelon"
1032 : }, /* (UTC-03:00) Saint Pierre and Miquelon */
1033 : {
1034 : "Samoa Standard Time", "Samoa Daylight Time",
1035 : "Pacific/Samoa"
1036 : }, /* (UTC+13:00) Samoa */
1037 : {
1038 : "SE Asia Standard Time", "SE Asia Daylight Time",
1039 : "Asia/Bangkok"
1040 : }, /* (UTC+07:00) Bangkok, Hanoi, Jakarta */
1041 : {
1042 : "Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
1043 : "Asia/Kuala_Lumpur"
1044 : }, /* (UTC+08:00) Kuala Lumpur, Singapore */
1045 : {
1046 : "Sakhalin Standard Time", "Sakhalin Daylight Time",
1047 : "Asia/Sakhalin"
1048 : }, /* (UTC+11:00) Sakhalin */
1049 : {
1050 : "South Africa Standard Time", "South Africa Daylight Time",
1051 : "Africa/Harare"
1052 : }, /* (UTC+02:00) Harare, Pretoria */
1053 : {
1054 : "Sri Lanka Standard Time", "Sri Lanka Daylight Time",
1055 : "Asia/Colombo"
1056 : }, /* (UTC+05:30) Sri Jayawardenepura */
1057 : {
1058 : "Syria Standard Time", "Syria Daylight Time",
1059 : "Asia/Damascus"
1060 : }, /* (UTC+02:00) Damascus */
1061 : {
1062 : "Taipei Standard Time", "Taipei Daylight Time",
1063 : "Asia/Taipei"
1064 : }, /* (UTC+08:00) Taipei */
1065 : {
1066 : "Tasmania Standard Time", "Tasmania Daylight Time",
1067 : "Australia/Hobart"
1068 : }, /* (UTC+10:00) Hobart */
1069 : {
1070 : "Tocantins Standard Time", "Tocantins Daylight Time",
1071 : "America/Araguaina"
1072 : }, /* (UTC-03:00) Araguaina */
1073 : {
1074 : "Tokyo Standard Time", "Tokyo Daylight Time",
1075 : "Asia/Tokyo"
1076 : }, /* (UTC+09:00) Osaka, Sapporo, Tokyo */
1077 : {
1078 : "Tonga Standard Time", "Tonga Daylight Time",
1079 : "Pacific/Tongatapu"
1080 : }, /* (UTC+13:00) Nuku'alofa */
1081 : {
1082 : "Tomsk Standard Time", "Tomsk Daylight Time",
1083 : "Asia/Tomsk"
1084 : }, /* (UTC+07:00) Tomsk */
1085 : {
1086 : "Transbaikal Standard Time", "Transbaikal Daylight Time",
1087 : "Asia/Chita"
1088 : }, /* (UTC+09:00) Chita */
1089 : {
1090 : "Turkey Standard Time", "Turkey Daylight Time",
1091 : "Europe/Istanbul"
1092 : }, /* (UTC+02:00) Istanbul */
1093 : {
1094 : "Turks and Caicos Standard Time", "Turks and Caicos Daylight Time",
1095 : "America/Grand_Turk"
1096 : }, /* (UTC-04:00) Turks and Caicos */
1097 : {
1098 : "Ulaanbaatar Standard Time", "Ulaanbaatar Daylight Time",
1099 : "Asia/Ulaanbaatar",
1100 : }, /* (UTC+08:00) Ulaanbaatar */
1101 : {
1102 : "US Eastern Standard Time", "US Eastern Daylight Time",
1103 : "US/Eastern"
1104 : }, /* (UTC-05:00) Indiana (East) */
1105 : {
1106 : "US Mountain Standard Time", "US Mountain Daylight Time",
1107 : "US/Arizona"
1108 : }, /* (UTC-07:00) Arizona */
1109 : {
1110 : "Coordinated Universal Time", "Coordinated Universal Time",
1111 : "UTC"
1112 : }, /* (UTC) Coordinated Universal Time */
1113 : {
1114 : "UTC+12", "UTC+12",
1115 : "Etc/GMT+12"
1116 : }, /* (UTC+12:00) Coordinated Universal Time+12 */
1117 : {
1118 : "UTC-02", "UTC-02",
1119 : "Etc/GMT-02"
1120 : }, /* (UTC-02:00) Coordinated Universal Time-02 */
1121 : {
1122 : "UTC-08", "UTC-08",
1123 : "Etc/GMT-08"
1124 : }, /* (UTC-08:00) Coordinated Universal Time-08 */
1125 : {
1126 : "UTC-09", "UTC-09",
1127 : "Etc/GMT-09"
1128 : }, /* (UTC-09:00) Coordinated Universal Time-09 */
1129 : {
1130 : "UTC-11", "UTC-11",
1131 : "Etc/GMT-11"
1132 : }, /* (UTC-11:00) Coordinated Universal Time-11 */
1133 : {
1134 : "Venezuela Standard Time", "Venezuela Daylight Time",
1135 : "America/Caracas",
1136 : }, /* (UTC-04:30) Caracas */
1137 : {
1138 : "Vladivostok Standard Time", "Vladivostok Daylight Time",
1139 : "Asia/Vladivostok"
1140 : }, /* (UTC+10:00) Vladivostok (RTZ 9) */
1141 : {
1142 : "W. Australia Standard Time", "W. Australia Daylight Time",
1143 : "Australia/Perth"
1144 : }, /* (UTC+08:00) Perth */
1145 : #ifdef NOT_USED
1146 : /* Could not find a match for this one (just a guess). Excluded for now. */
1147 : {
1148 : "W. Central Africa Standard Time", "W. Central Africa Daylight Time",
1149 : "WAT"
1150 : }, /* (UTC+01:00) West Central Africa */
1151 : #endif
1152 : {
1153 : "W. Europe Standard Time", "W. Europe Daylight Time",
1154 : "CET"
1155 : }, /* (UTC+01:00) Amsterdam, Berlin, Bern, Rome,
1156 : * Stockholm, Vienna */
1157 : {
1158 : "W. Mongolia Standard Time", "W. Mongolia Daylight Time",
1159 : "Asia/Hovd"
1160 : }, /* (UTC+07:00) Hovd */
1161 : {
1162 : "West Asia Standard Time", "West Asia Daylight Time",
1163 : "Asia/Karachi"
1164 : }, /* (UTC+05:00) Ashgabat, Tashkent */
1165 : {
1166 : "West Bank Gaza Standard Time", "West Bank Gaza Daylight Time",
1167 : "Asia/Gaza"
1168 : }, /* (UTC+02:00) Gaza, Hebron */
1169 : {
1170 : "West Pacific Standard Time", "West Pacific Daylight Time",
1171 : "Pacific/Guam"
1172 : }, /* (UTC+10:00) Guam, Port Moresby */
1173 : {
1174 : "Yakutsk Standard Time", "Yakutsk Daylight Time",
1175 : "Asia/Yakutsk"
1176 : }, /* (UTC+09:00) Yakutsk */
1177 : {
1178 : NULL, NULL, NULL
1179 : }
1180 : };
1181 :
1182 : static const char *
1183 : identify_system_timezone(void)
1184 : {
1185 : int i;
1186 : char tzname[128];
1187 : char localtzname[256];
1188 : time_t t = time(NULL);
1189 : struct tm *tm = localtime(&t);
1190 : HKEY rootKey;
1191 : int idx;
1192 :
1193 : if (!tm)
1194 : {
1195 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1196 : fprintf(stderr, "could not identify system time zone: localtime() failed\n");
1197 : #endif
1198 : return NULL; /* go to GMT */
1199 : }
1200 :
1201 : memset(tzname, 0, sizeof(tzname));
1202 : strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
1203 :
1204 : for (i = 0; win32_tzmap[i].stdname != NULL; i++)
1205 : {
1206 : if (strcmp(tzname, win32_tzmap[i].stdname) == 0 ||
1207 : strcmp(tzname, win32_tzmap[i].dstname) == 0)
1208 : {
1209 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1210 : fprintf(stderr, "TZ \"%s\" matches system time zone \"%s\"\n",
1211 : win32_tzmap[i].pgtzname, tzname);
1212 : #endif
1213 : return win32_tzmap[i].pgtzname;
1214 : }
1215 : }
1216 :
1217 : /*
1218 : * Localized Windows versions return localized names for the timezone.
1219 : * Scan the registry to find the English name, and then try matching
1220 : * against our table again.
1221 : */
1222 : memset(localtzname, 0, sizeof(localtzname));
1223 : if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1224 : "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1225 : 0,
1226 : KEY_READ,
1227 : &rootKey) != ERROR_SUCCESS)
1228 : {
1229 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1230 : fprintf(stderr, "could not open registry key to identify system time zone: error code %lu\n",
1231 : GetLastError());
1232 : #endif
1233 : return NULL; /* go to GMT */
1234 : }
1235 :
1236 : for (idx = 0;; idx++)
1237 : {
1238 : char keyname[256];
1239 : char zonename[256];
1240 : DWORD namesize;
1241 : FILETIME lastwrite;
1242 : HKEY key;
1243 : LONG r;
1244 :
1245 : memset(keyname, 0, sizeof(keyname));
1246 : namesize = sizeof(keyname);
1247 : if ((r = RegEnumKeyEx(rootKey,
1248 : idx,
1249 : keyname,
1250 : &namesize,
1251 : NULL,
1252 : NULL,
1253 : NULL,
1254 : &lastwrite)) != ERROR_SUCCESS)
1255 : {
1256 : if (r == ERROR_NO_MORE_ITEMS)
1257 : break;
1258 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1259 : fprintf(stderr, "could not enumerate registry subkeys to identify system time zone: %d\n",
1260 : (int) r);
1261 : #endif
1262 : break;
1263 : }
1264 :
1265 : if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
1266 : {
1267 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1268 : fprintf(stderr, "could not open registry subkey to identify system time zone: %d\n",
1269 : (int) r);
1270 : #endif
1271 : break;
1272 : }
1273 :
1274 : memset(zonename, 0, sizeof(zonename));
1275 : namesize = sizeof(zonename);
1276 : if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
1277 : {
1278 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1279 : fprintf(stderr, "could not query value for key \"std\" to identify system time zone \"%s\": %d\n",
1280 : keyname, (int) r);
1281 : #endif
1282 : RegCloseKey(key);
1283 : continue; /* Proceed to look at the next timezone */
1284 : }
1285 : if (strcmp(tzname, zonename) == 0)
1286 : {
1287 : /* Matched zone */
1288 : strcpy(localtzname, keyname);
1289 : RegCloseKey(key);
1290 : break;
1291 : }
1292 : memset(zonename, 0, sizeof(zonename));
1293 : namesize = sizeof(zonename);
1294 : if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
1295 : {
1296 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1297 : fprintf(stderr, "could not query value for key \"dlt\" to identify system time zone \"%s\": %d\n",
1298 : keyname, (int) r);
1299 : #endif
1300 : RegCloseKey(key);
1301 : continue; /* Proceed to look at the next timezone */
1302 : }
1303 : if (strcmp(tzname, zonename) == 0)
1304 : {
1305 : /* Matched DST zone */
1306 : strcpy(localtzname, keyname);
1307 : RegCloseKey(key);
1308 : break;
1309 : }
1310 :
1311 : RegCloseKey(key);
1312 : }
1313 :
1314 : RegCloseKey(rootKey);
1315 :
1316 : if (localtzname[0])
1317 : {
1318 : /* Found a localized name, so scan for that one too */
1319 : for (i = 0; win32_tzmap[i].stdname != NULL; i++)
1320 : {
1321 : if (strcmp(localtzname, win32_tzmap[i].stdname) == 0 ||
1322 : strcmp(localtzname, win32_tzmap[i].dstname) == 0)
1323 : {
1324 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1325 : fprintf(stderr, "TZ \"%s\" matches localized system time zone \"%s\" (\"%s\")\n",
1326 : win32_tzmap[i].pgtzname, tzname, localtzname);
1327 : #endif
1328 : return win32_tzmap[i].pgtzname;
1329 : }
1330 : }
1331 : }
1332 :
1333 : #ifdef DEBUG_IDENTIFY_TIMEZONE
1334 : fprintf(stderr, "could not find a match for system time zone \"%s\"\n",
1335 : tzname);
1336 : #endif
1337 : return NULL; /* go to GMT */
1338 : }
1339 : #endif /* WIN32 */
1340 :
1341 :
1342 : /*
1343 : * Return true if the given zone name is valid and is an "acceptable" zone.
1344 : */
1345 : static bool
1346 2 : validate_zone(const char *tzname)
1347 : {
1348 : pg_tz *tz;
1349 :
1350 2 : if (!tzname || !tzname[0])
1351 1 : return false;
1352 :
1353 1 : tz = pg_load_tz(tzname);
1354 1 : if (!tz)
1355 0 : return false;
1356 :
1357 1 : if (!pg_tz_acceptable(tz))
1358 0 : return false;
1359 :
1360 1 : return true;
1361 : }
1362 :
1363 : /*
1364 : * Identify a suitable default timezone setting based on the environment.
1365 : *
1366 : * The installation share_path must be passed in, as that is the default
1367 : * location for the timezone database directory.
1368 : *
1369 : * We first look to the TZ environment variable. If not found or not
1370 : * recognized by our own code, we see if we can identify the timezone
1371 : * from the behavior of the system timezone library. When all else fails,
1372 : * return NULL, indicating that we should default to GMT.
1373 : */
1374 : const char *
1375 1 : select_default_timezone(const char *share_path)
1376 : {
1377 : const char *tzname;
1378 :
1379 : /* Initialize timezone directory path, if needed */
1380 : #ifndef SYSTEMTZDIR
1381 1 : snprintf(tzdirpath, sizeof(tzdirpath), "%s/timezone", share_path);
1382 : #endif
1383 :
1384 : /* Check TZ environment variable */
1385 1 : tzname = getenv("TZ");
1386 1 : if (validate_zone(tzname))
1387 0 : return tzname;
1388 :
1389 : /* Nope, so try to identify the system timezone */
1390 1 : tzname = identify_system_timezone();
1391 1 : if (validate_zone(tzname))
1392 1 : return tzname;
1393 :
1394 0 : return NULL;
1395 : }
|