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