Remove croak and Perl_croak from gettext triggers. While we could
[PostgreSQL.git] / src / timezone / pgtz.c
blobc92cd93aa872f02af4c605f930facec8e6cfbec4
1 /*-------------------------------------------------------------------------
3 * pgtz.c
4 * Timezone Library Integration Functions
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * IDENTIFICATION
9 * $PostgreSQL$
11 *-------------------------------------------------------------------------
13 #define NO_REDEFINE_TIMEFUNCS
15 #include "postgres.h"
17 #include <ctype.h>
18 #include <fcntl.h>
19 #include <sys/stat.h>
20 #include <time.h>
22 #include "miscadmin.h"
23 #include "pgtz.h"
24 #include "storage/fd.h"
25 #include "tzfile.h"
26 #include "utils/datetime.h"
27 #include "utils/guc.h"
28 #include "utils/hsearch.h"
30 /* Current session timezone (controlled by TimeZone GUC) */
31 pg_tz *session_timezone = NULL;
33 /* Current log timezone (controlled by log_timezone GUC) */
34 pg_tz *log_timezone = NULL;
36 /* Fallback GMT timezone for last-ditch error message formatting */
37 pg_tz *gmt_timezone = NULL;
38 static pg_tz gmt_timezone_data;
41 static bool scan_directory_ci(const char *dirname,
42 const char *fname, int fnamelen,
43 char *canonname, int canonnamelen);
44 static const char *identify_system_timezone(void);
45 static pg_tz *get_pg_tz_for_zone(const char *tzname);
46 static pg_tz *select_default_timezone(void);
50 * Return full pathname of timezone data directory
52 static const char *
53 pg_TZDIR(void)
55 #ifndef SYSTEMTZDIR
56 /* normal case: timezone stuff is under our share dir */
57 static bool done_tzdir = false;
58 static char tzdir[MAXPGPATH];
60 if (done_tzdir)
61 return tzdir;
63 get_share_path(my_exec_path, tzdir);
64 strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));
66 done_tzdir = true;
67 return tzdir;
68 #else
69 /* we're configured to use system's timezone database */
70 return SYSTEMTZDIR;
71 #endif
76 * Given a timezone name, open() the timezone data file. Return the
77 * file descriptor if successful, -1 if not.
79 * The input name is searched for case-insensitively (we assume that the
80 * timezone database does not contain case-equivalent names).
82 * If "canonname" is not NULL, then on success the canonical spelling of the
83 * given name is stored there (it is assumed to be > TZ_STRLEN_MAX bytes!).
85 int
86 pg_open_tzfile(const char *name, char *canonname)
88 const char *fname;
89 char fullname[MAXPGPATH];
90 int fullnamelen;
91 int orignamelen;
94 * Loop to split the given name into directory levels; for each level,
95 * search using scan_directory_ci().
97 strcpy(fullname, pg_TZDIR());
98 orignamelen = fullnamelen = strlen(fullname);
99 fname = name;
100 for (;;)
102 const char *slashptr;
103 int fnamelen;
105 slashptr = strchr(fname, '/');
106 if (slashptr)
107 fnamelen = slashptr - fname;
108 else
109 fnamelen = strlen(fname);
110 if (fullnamelen + 1 + fnamelen >= MAXPGPATH)
111 return -1; /* not gonna fit */
112 if (!scan_directory_ci(fullname, fname, fnamelen,
113 fullname + fullnamelen + 1,
114 MAXPGPATH - fullnamelen - 1))
115 return -1;
116 fullname[fullnamelen++] = '/';
117 fullnamelen += strlen(fullname + fullnamelen);
118 if (slashptr)
119 fname = slashptr + 1;
120 else
121 break;
124 if (canonname)
125 strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);
127 return open(fullname, O_RDONLY | PG_BINARY, 0);
132 * Scan specified directory for a case-insensitive match to fname
133 * (of length fnamelen --- fname may not be null terminated!). If found,
134 * copy the actual filename into canonname and return true.
136 static bool
137 scan_directory_ci(const char *dirname, const char *fname, int fnamelen,
138 char *canonname, int canonnamelen)
140 bool found = false;
141 DIR *dirdesc;
142 struct dirent *direntry;
144 dirdesc = AllocateDir(dirname);
145 if (!dirdesc)
147 ereport(LOG,
148 (errcode_for_file_access(),
149 errmsg("could not open directory \"%s\": %m", dirname)));
150 return false;
153 while ((direntry = ReadDir(dirdesc, dirname)) != NULL)
156 * Ignore . and .., plus any other "hidden" files. This is a security
157 * measure to prevent access to files outside the timezone directory.
159 if (direntry->d_name[0] == '.')
160 continue;
162 if (strlen(direntry->d_name) == fnamelen &&
163 pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
165 /* Found our match */
166 strlcpy(canonname, direntry->d_name, canonnamelen);
167 found = true;
168 break;
172 FreeDir(dirdesc);
174 return found;
179 * The following block of code attempts to determine which timezone in our
180 * timezone database is the best match for the active system timezone.
182 * On most systems, we rely on trying to match the observable behavior of
183 * the C library's localtime() function. The database zone that matches
184 * furthest into the past is the one to use. Often there will be several
185 * zones with identical rankings (since the zic database assigns multiple
186 * names to many zones). We break ties arbitrarily by preferring shorter,
187 * then alphabetically earlier zone names.
189 * Win32's native knowledge about timezones appears to be too incomplete
190 * and too different from the zic database for the above matching strategy
191 * to be of any use. But there is just a limited number of timezones
192 * available, so we can rely on a handmade mapping table instead.
195 #ifndef WIN32
197 #define T_DAY ((time_t) (60*60*24))
198 #define T_WEEK ((time_t) (60*60*24*7))
199 #define T_MONTH ((time_t) (60*60*24*31))
201 #define MAX_TEST_TIMES (52*100) /* 100 years */
203 struct tztry
205 int n_test_times;
206 time_t test_times[MAX_TEST_TIMES];
209 static void scan_available_timezones(char *tzdir, char *tzdirsub,
210 struct tztry * tt,
211 int *bestscore, char *bestzonename);
215 * Get GMT offset from a system struct tm
217 static int
218 get_timezone_offset(struct tm * tm)
220 #if defined(HAVE_STRUCT_TM_TM_ZONE)
221 return tm->tm_gmtoff;
222 #elif defined(HAVE_INT_TIMEZONE)
223 return -TIMEZONE_GLOBAL;
224 #else
225 #error No way to determine TZ? Can this happen?
226 #endif
230 * Convenience subroutine to convert y/m/d to time_t (NOT pg_time_t)
232 static time_t
233 build_time_t(int year, int month, int day)
235 struct tm tm;
237 memset(&tm, 0, sizeof(tm));
238 tm.tm_mday = day;
239 tm.tm_mon = month - 1;
240 tm.tm_year = year - 1900;
242 return mktime(&tm);
246 * Does a system tm value match one we computed ourselves?
248 static bool
249 compare_tm(struct tm * s, struct pg_tm * p)
251 if (s->tm_sec != p->tm_sec ||
252 s->tm_min != p->tm_min ||
253 s->tm_hour != p->tm_hour ||
254 s->tm_mday != p->tm_mday ||
255 s->tm_mon != p->tm_mon ||
256 s->tm_year != p->tm_year ||
257 s->tm_wday != p->tm_wday ||
258 s->tm_yday != p->tm_yday ||
259 s->tm_isdst != p->tm_isdst)
260 return false;
261 return true;
265 * See how well a specific timezone setting matches the system behavior
267 * We score a timezone setting according to the number of test times it
268 * matches. (The test times are ordered later-to-earlier, but this routine
269 * doesn't actually know that; it just scans until the first non-match.)
271 * We return -1 for a completely unusable setting; this is worse than the
272 * score of zero for a setting that works but matches not even the first
273 * test time.
275 static int
276 score_timezone(const char *tzname, struct tztry * tt)
278 int i;
279 pg_time_t pgtt;
280 struct tm *systm;
281 struct pg_tm *pgtm;
282 char cbuf[TZ_STRLEN_MAX + 1];
283 pg_tz tz;
287 * Load timezone directly. Don't use pg_tzset, because we don't want all
288 * timezones loaded in the cache at startup.
290 if (tzload(tzname, NULL, &tz.state, TRUE) != 0)
292 if (tzname[0] == ':' || tzparse(tzname, &tz.state, FALSE) != 0)
294 return -1; /* can't handle the TZ name at all */
298 /* Reject if leap seconds involved */
299 if (!tz_acceptable(&tz))
301 elog(DEBUG4, "Reject TZ \"%s\": uses leap seconds", tzname);
302 return -1;
305 /* Check for match at all the test times */
306 for (i = 0; i < tt->n_test_times; i++)
308 pgtt = (pg_time_t) (tt->test_times[i]);
309 pgtm = pg_localtime(&pgtt, &tz);
310 if (!pgtm)
311 return -1; /* probably shouldn't happen */
312 systm = localtime(&(tt->test_times[i]));
313 if (!systm)
315 elog(DEBUG4, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data",
316 tzname, i, (long) pgtt,
317 pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
318 pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
319 pgtm->tm_isdst ? "dst" : "std");
320 return i;
322 if (!compare_tm(systm, pgtm))
324 elog(DEBUG4, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s",
325 tzname, i, (long) pgtt,
326 pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
327 pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
328 pgtm->tm_isdst ? "dst" : "std",
329 systm->tm_year + 1900, systm->tm_mon + 1, systm->tm_mday,
330 systm->tm_hour, systm->tm_min, systm->tm_sec,
331 systm->tm_isdst ? "dst" : "std");
332 return i;
334 if (systm->tm_isdst >= 0)
336 /* Check match of zone names, too */
337 if (pgtm->tm_zone == NULL)
338 return -1; /* probably shouldn't happen */
339 memset(cbuf, 0, sizeof(cbuf));
340 strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm); /* zone abbr */
341 if (strcmp(cbuf, pgtm->tm_zone) != 0)
343 elog(DEBUG4, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"",
344 tzname, i, (long) pgtt,
345 pgtm->tm_zone, cbuf);
346 return i;
351 elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);
352 return i;
357 * Try to identify a timezone name (in our terminology) that best matches the
358 * observed behavior of the system timezone library. We cannot assume that
359 * the system TZ environment setting (if indeed there is one) matches our
360 * terminology, so we ignore it and just look at what localtime() returns.
362 static const char *
363 identify_system_timezone(void)
365 static char resultbuf[TZ_STRLEN_MAX + 1];
366 time_t tnow;
367 time_t t;
368 struct tztry tt;
369 struct tm *tm;
370 int thisyear;
371 int bestscore;
372 char tmptzdir[MAXPGPATH];
373 int std_ofs;
374 char std_zone_name[TZ_STRLEN_MAX + 1],
375 dst_zone_name[TZ_STRLEN_MAX + 1];
376 char cbuf[TZ_STRLEN_MAX + 1];
378 /* Initialize OS timezone library */
379 tzset();
382 * Set up the list of dates to be probed to see how well our timezone
383 * matches the system zone. We first probe January and July of the
384 * current year; this serves to quickly eliminate the vast majority of the
385 * TZ database entries. If those dates match, we probe every week for 100
386 * years backwards from the current July. (Weekly resolution is good
387 * enough to identify DST transition rules, since everybody switches on
388 * Sundays.) This is sufficient to cover most of the Unix time_t range,
389 * and we don't want to look further than that since many systems won't
390 * have sane TZ behavior further back anyway. The further
391 * back the zone matches, the better we score it. This may seem like a
392 * rather random way of doing things, but experience has shown that
393 * system-supplied timezone definitions are likely to have DST behavior
394 * that is right for the recent past and not so accurate further back.
395 * Scoring in this way allows us to recognize zones that have some
396 * commonality with the zic database, without insisting on exact match.
397 * (Note: we probe Thursdays, not Sundays, to avoid triggering
398 * DST-transition bugs in localtime itself.)
400 tnow = time(NULL);
401 tm = localtime(&tnow);
402 if (!tm)
403 return NULL; /* give up if localtime is broken... */
404 thisyear = tm->tm_year + 1900;
406 t = build_time_t(thisyear, 1, 15);
408 * Round back to GMT midnight Thursday. This depends on the knowledge
409 * that the time_t origin is Thu Jan 01 1970. (With a different origin
410 * we'd be probing some other day of the week, but it wouldn't matter
411 * anyway unless localtime() had DST-transition bugs.)
413 t -= (t % T_WEEK);
415 tt.n_test_times = 0;
416 tt.test_times[tt.n_test_times++] = t;
418 t = build_time_t(thisyear, 7, 15);
419 t -= (t % T_WEEK);
421 tt.test_times[tt.n_test_times++] = t;
423 while (tt.n_test_times < MAX_TEST_TIMES)
425 t -= T_WEEK;
426 tt.test_times[tt.n_test_times++] = t;
429 /* Search for the best-matching timezone file */
430 strcpy(tmptzdir, pg_TZDIR());
431 bestscore = -1;
432 resultbuf[0] = '\0';
433 scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1,
434 &tt,
435 &bestscore, resultbuf);
436 if (bestscore > 0)
438 /* Ignore zic's rather silly "Factory" time zone; use GMT instead */
439 if (strcmp(resultbuf, "Factory") == 0)
440 return NULL;
441 return resultbuf;
445 * Couldn't find a match in the database, so next we try constructed zone
446 * names (like "PST8PDT").
448 * First we need to determine the names of the local standard and daylight
449 * zones. The idea here is to scan forward from today until we have seen
450 * both zones, if both are in use.
452 memset(std_zone_name, 0, sizeof(std_zone_name));
453 memset(dst_zone_name, 0, sizeof(dst_zone_name));
454 std_ofs = 0;
456 tnow = time(NULL);
459 * Round back to a GMT midnight so results don't depend on local time of
460 * day
462 tnow -= (tnow % T_DAY);
465 * We have to look a little further ahead than one year, in case today is
466 * just past a DST boundary that falls earlier in the year than the next
467 * similar boundary. Arbitrarily scan up to 14 months.
469 for (t = tnow; t <= tnow + T_MONTH * 14; t += T_MONTH)
471 tm = localtime(&t);
472 if (!tm)
473 continue;
474 if (tm->tm_isdst < 0)
475 continue;
476 if (tm->tm_isdst == 0 && std_zone_name[0] == '\0')
478 /* found STD zone */
479 memset(cbuf, 0, sizeof(cbuf));
480 strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
481 strcpy(std_zone_name, cbuf);
482 std_ofs = get_timezone_offset(tm);
484 if (tm->tm_isdst > 0 && dst_zone_name[0] == '\0')
486 /* found DST zone */
487 memset(cbuf, 0, sizeof(cbuf));
488 strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
489 strcpy(dst_zone_name, cbuf);
491 /* Done if found both */
492 if (std_zone_name[0] && dst_zone_name[0])
493 break;
496 /* We should have found a STD zone name by now... */
497 if (std_zone_name[0] == '\0')
499 ereport(LOG,
500 (errmsg("could not determine system time zone, defaulting to \"%s\"", "GMT"),
501 errhint("You can specify the correct timezone in postgresql.conf.")));
502 return NULL; /* go to GMT */
505 /* If we found DST then try STD<ofs>DST */
506 if (dst_zone_name[0] != '\0')
508 snprintf(resultbuf, sizeof(resultbuf), "%s%d%s",
509 std_zone_name, -std_ofs / 3600, dst_zone_name);
510 if (score_timezone(resultbuf, &tt) > 0)
511 return resultbuf;
514 /* Try just the STD timezone (works for GMT at least) */
515 strcpy(resultbuf, std_zone_name);
516 if (score_timezone(resultbuf, &tt) > 0)
517 return resultbuf;
519 /* Try STD<ofs> */
520 snprintf(resultbuf, sizeof(resultbuf), "%s%d",
521 std_zone_name, -std_ofs / 3600);
522 if (score_timezone(resultbuf, &tt) > 0)
523 return resultbuf;
526 * Did not find the timezone. Fallback to use a GMT zone. Note that the
527 * zic timezone database names the GMT-offset zones in POSIX style: plus
528 * is west of Greenwich. It's unfortunate that this is opposite of SQL
529 * conventions. Should we therefore change the names? Probably not...
531 snprintf(resultbuf, sizeof(resultbuf), "Etc/GMT%s%d",
532 (-std_ofs > 0) ? "+" : "", -std_ofs / 3600);
534 ereport(LOG,
535 (errmsg("could not recognize system timezone, defaulting to \"%s\"",
536 resultbuf),
537 errhint("You can specify the correct timezone in postgresql.conf.")));
538 return resultbuf;
542 * Recursively scan the timezone database looking for the best match to
543 * the system timezone behavior.
545 * tzdir points to a buffer of size MAXPGPATH. On entry, it holds the
546 * pathname of a directory containing TZ files. We internally modify it
547 * to hold pathnames of sub-directories and files, but must restore it
548 * to its original contents before exit.
550 * tzdirsub points to the part of tzdir that represents the subfile name
551 * (ie, tzdir + the original directory name length, plus one for the
552 * first added '/').
554 * tt tells about the system timezone behavior we need to match.
556 * *bestscore and *bestzonename on entry hold the best score found so far
557 * and the name of the best zone. We overwrite them if we find a better
558 * score. bestzonename must be a buffer of length TZ_STRLEN_MAX + 1.
560 static void
561 scan_available_timezones(char *tzdir, char *tzdirsub, struct tztry * tt,
562 int *bestscore, char *bestzonename)
564 int tzdir_orig_len = strlen(tzdir);
565 DIR *dirdesc;
566 struct dirent *direntry;
568 dirdesc = AllocateDir(tzdir);
569 if (!dirdesc)
571 ereport(LOG,
572 (errcode_for_file_access(),
573 errmsg("could not open directory \"%s\": %m", tzdir)));
574 return;
577 while ((direntry = ReadDir(dirdesc, tzdir)) != NULL)
579 struct stat statbuf;
581 /* Ignore . and .., plus any other "hidden" files */
582 if (direntry->d_name[0] == '.')
583 continue;
585 snprintf(tzdir + tzdir_orig_len, MAXPGPATH - tzdir_orig_len,
586 "/%s", direntry->d_name);
588 if (stat(tzdir, &statbuf) != 0)
590 ereport(LOG,
591 (errcode_for_file_access(),
592 errmsg("could not stat \"%s\": %m", tzdir)));
593 tzdir[tzdir_orig_len] = '\0';
594 continue;
597 if (S_ISDIR(statbuf.st_mode))
599 /* Recurse into subdirectory */
600 scan_available_timezones(tzdir, tzdirsub, tt,
601 bestscore, bestzonename);
603 else
605 /* Load and test this file */
606 int score = score_timezone(tzdirsub, tt);
608 if (score > *bestscore)
610 *bestscore = score;
611 strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
613 else if (score == *bestscore)
615 /* Consider how to break a tie */
616 if (strlen(tzdirsub) < strlen(bestzonename) ||
617 (strlen(tzdirsub) == strlen(bestzonename) &&
618 strcmp(tzdirsub, bestzonename) < 0))
619 strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
623 /* Restore tzdir */
624 tzdir[tzdir_orig_len] = '\0';
627 FreeDir(dirdesc);
629 #else /* WIN32 */
631 static const struct
633 const char *stdname; /* Windows name of standard timezone */
634 const char *dstname; /* Windows name of daylight timezone */
635 const char *pgtzname; /* Name of pgsql timezone to map to */
636 } win32_tzmap[] =
640 * This list was built from the contents of the registry at
641 * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time
642 * Zones on Windows XP Professional SP2
644 * The zones have been matched to zic timezones by looking at the cities
645 * listed in the win32 display name (in the comment here) in most cases.
648 "Afghanistan Standard Time", "Afghanistan Daylight Time",
649 "Asia/Kabul"
650 }, /* (GMT+04:30) Kabul */
652 "Alaskan Standard Time", "Alaskan Daylight Time",
653 "US/Alaska"
654 }, /* (GMT-09:00) Alaska */
656 "Arab Standard Time", "Arab Daylight Time",
657 "Asia/Kuwait"
658 }, /* (GMT+03:00) Kuwait, Riyadh */
660 "Arabian Standard Time", "Arabian Daylight Time",
661 "Asia/Muscat"
662 }, /* (GMT+04:00) Abu Dhabi, Muscat */
664 "Arabic Standard Time", "Arabic Daylight Time",
665 "Asia/Baghdad"
666 }, /* (GMT+03:00) Baghdad */
668 "Armenian Standard Time", "Armenian Daylight Time",
669 "Asia/Yerevan"
670 }, /* (GMT+04:00) Yerevan */
672 "Atlantic Standard Time", "Atlantic Daylight Time",
673 "Canada/Atlantic"
674 }, /* (GMT-04:00) Atlantic Time (Canada) */
676 "AUS Central Standard Time", "AUS Central Daylight Time",
677 "Australia/Darwin"
678 }, /* (GMT+09:30) Darwin */
680 "AUS Eastern Standard Time", "AUS Eastern Daylight Time",
681 "Australia/Canberra"
682 }, /* (GMT+10:00) Canberra, Melbourne, Sydney */
684 "Azores Standard Time", "Azores Daylight Time",
685 "Atlantic/Azores"
686 }, /* (GMT-01:00) Azores */
688 "Canada Central Standard Time", "Canada Central Daylight Time",
689 "Canada/Saskatchewan"
690 }, /* (GMT-06:00) Saskatchewan */
692 "Cape Verde Standard Time", "Cape Verde Daylight Time",
693 "Atlantic/Cape_Verde"
694 }, /* (GMT-01:00) Cape Verde Is. */
696 "Caucasus Standard Time", "Caucasus Daylight Time",
697 "Asia/Baku"
698 }, /* (GMT+04:00) Baku, Tbilisi, Yerevan */
700 "Cen. Australia Standard Time", "Cen. Australia Daylight Time",
701 "Australia/Adelaide"
702 }, /* (GMT+09:30) Adelaide */
704 "Central America Standard Time", "Central America Daylight Time",
705 "CST6CDT"
706 }, /* (GMT-06:00) Central America */
708 "Central Asia Standard Time", "Central Asia Daylight Time",
709 "Asia/Dhaka"
710 }, /* (GMT+06:00) Astana, Dhaka */
712 "Central Europe Standard Time", "Central Europe Daylight Time",
713 "Europe/Belgrade"
714 }, /* (GMT+01:00) Belgrade, Bratislava, Budapest,
715 * Ljubljana, Prague */
717 "Central European Standard Time", "Central European Daylight Time",
718 "Europe/Sarajevo"
719 }, /* (GMT+01:00) Sarajevo, Skopje, Warsaw,
720 * Zagreb */
722 "Central Pacific Standard Time", "Central Pacific Daylight Time",
723 "Pacific/Noumea"
724 }, /* (GMT+11:00) Magadan, Solomon Is., New
725 * Caledonia */
727 "Central Standard Time", "Central Daylight Time",
728 "US/Central"
729 }, /* (GMT-06:00) Central Time (US & Canada) */
731 "Central Standard Time (Mexico)", "Central Daylight Time (Mexico)",
732 "America/Mexico_City"
733 }, /* (GMT-06:00) Guadalajara, Mexico City,
734 Monterrey - New */
736 "China Standard Time", "China Daylight Time",
737 "Asia/Hong_Kong"
738 }, /* (GMT+08:00) Beijing, Chongqing, Hong Kong,
739 * Urumqi */
741 "Dateline Standard Time", "Dateline Daylight Time",
742 "Etc/GMT+12"
743 }, /* (GMT-12:00) International Date Line West */
745 "E. Africa Standard Time", "E. Africa Daylight Time",
746 "Africa/Nairobi"
747 }, /* (GMT+03:00) Nairobi */
749 "E. Australia Standard Time", "E. Australia Daylight Time",
750 "Australia/Brisbane"
751 }, /* (GMT+10:00) Brisbane */
753 "E. Europe Standard Time", "E. Europe Daylight Time",
754 "Europe/Bucharest"
755 }, /* (GMT+02:00) Bucharest */
757 "E. South America Standard Time", "E. South America Daylight Time",
758 "America/Araguaina"
759 }, /* (GMT-03:00) Brasilia */
761 "Eastern Standard Time", "Eastern Daylight Time",
762 "US/Eastern"
763 }, /* (GMT-05:00) Eastern Time (US & Canada) */
765 "Egypt Standard Time", "Egypt Daylight Time",
766 "Africa/Cairo"
767 }, /* (GMT+02:00) Cairo */
769 "Ekaterinburg Standard Time", "Ekaterinburg Daylight Time",
770 "Asia/Yekaterinburg"
771 }, /* (GMT+05:00) Ekaterinburg */
773 "Fiji Standard Time", "Fiji Daylight Time",
774 "Pacific/Fiji"
775 }, /* (GMT+12:00) Fiji, Kamchatka, Marshall Is. */
777 "FLE Standard Time", "FLE Daylight Time",
778 "Europe/Helsinki"
779 }, /* (GMT+02:00) Helsinki, Kyiv, Riga, Sofia,
780 * Tallinn, Vilnius */
782 "Georgian Standard Time", "Georgian Daylight Time",
783 "Asia/Tbilisi"
784 }, /* (GMT+03:00) Tbilisi */
786 "GMT Standard Time", "GMT Daylight Time",
787 "Europe/London"
788 }, /* (GMT) Greenwich Mean Time : Dublin,
789 * Edinburgh, Lisbon, London */
791 "Greenland Standard Time", "Greenland Daylight Time",
792 "America/Godthab"
793 }, /* (GMT-03:00) Greenland */
795 "Greenwich Standard Time", "Greenwich Daylight Time",
796 "Africa/Casablanca"
797 }, /* (GMT) Casablanca, Monrovia */
799 "GTB Standard Time", "GTB Daylight Time",
800 "Europe/Athens"
801 }, /* (GMT+02:00) Athens, Istanbul, Minsk */
803 "Hawaiian Standard Time", "Hawaiian Daylight Time",
804 "US/Hawaii"
805 }, /* (GMT-10:00) Hawaii */
807 "India Standard Time", "India Daylight Time",
808 "Asia/Calcutta"
809 }, /* (GMT+05:30) Chennai, Kolkata, Mumbai, New
810 * Delhi */
812 "Iran Standard Time", "Iran Daylight Time",
813 "Asia/Tehran"
814 }, /* (GMT+03:30) Tehran */
816 "Jerusalem Standard Time", "Jerusalem Daylight Time",
817 "Asia/Jerusalem"
818 }, /* (GMT+02:00) Jerusalem */
820 "Jordan Standard Time", "Jordan Daylight Time",
821 "Asia/Amman"
822 }, /* (GMT+02:00) Amman */
824 "Korea Standard Time", "Korea Daylight Time",
825 "Asia/Seoul"
826 }, /* (GMT+09:00) Seoul */
828 "Mexico Standard Time", "Mexico Daylight Time",
829 "America/Mexico_City"
830 }, /* (GMT-06:00) Guadalajara, Mexico City,
831 * Monterrey */
833 "Mexico Standard Time 2", "Mexico Daylight Time 2",
834 "America/Chihuahua"
835 }, /* (GMT-07:00) Chihuahua, La Paz, Mazatlan */
837 "Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
838 "Atlantic/South_Georgia"
839 }, /* (GMT-02:00) Mid-Atlantic */
841 "Middle East Standard Time", "Middle East Daylight Time",
842 "Asia/Beirut"
843 }, /* (GMT+02:00) Beirut */
845 "Montevideo Standard Time", "Montevideo Daylight Time",
846 "America/Montevideo"
847 }, /* (GMT-03:00) Montevideo */
849 "Mountain Standard Time", "Mountain Daylight Time",
850 "US/Mountain"
851 }, /* (GMT-07:00) Mountain Time (US & Canada) */
853 "Mountain Standard Time (Mexico)", "Mountain Daylight Time (Mexico)",
854 "America/Chihuahua"
855 }, /* (GMT-07:00) Chihuahua, La Paz,
856 Mazatlan - New */
858 "Myanmar Standard Time", "Myanmar Daylight Time",
859 "Asia/Rangoon"
860 }, /* (GMT+06:30) Rangoon */
862 "N. Central Asia Standard Time", "N. Central Asia Daylight Time",
863 "Asia/Almaty"
864 }, /* (GMT+06:00) Almaty, Novosibirsk */
866 "Namibia Standard Time", "Namibia Daylight Time",
867 "Africa/Windhoek"
868 }, /* (GMT+02:00) Windhoek */
870 "Nepal Standard Time", "Nepal Daylight Time",
871 "Asia/Katmandu"
872 }, /* (GMT+05:45) Kathmandu */
874 "New Zealand Standard Time", "New Zealand Daylight Time",
875 "Pacific/Auckland"
876 }, /* (GMT+12:00) Auckland, Wellington */
878 "Newfoundland Standard Time", "Newfoundland Daylight Time",
879 "Canada/Newfoundland"
880 }, /* (GMT-03:30) Newfoundland */
882 "North Asia East Standard Time", "North Asia East Daylight Time",
883 "Asia/Irkutsk"
884 }, /* (GMT+08:00) Irkutsk, Ulaan Bataar */
886 "North Asia Standard Time", "North Asia Daylight Time",
887 "Asia/Krasnoyarsk"
888 }, /* (GMT+07:00) Krasnoyarsk */
890 "Pacific SA Standard Time", "Pacific SA Daylight Time",
891 "America/Santiago"
892 }, /* (GMT-04:00) Santiago */
894 "Pacific Standard Time", "Pacific Daylight Time",
895 "US/Pacific"
896 }, /* (GMT-08:00) Pacific Time (US & Canada);
897 * Tijuana */
899 "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)",
900 "America/Tijuana"
901 }, /* (GMT-08:00) Tijuana, Baja California */
903 "Romance Standard Time", "Romance Daylight Time",
904 "Europe/Brussels"
905 }, /* (GMT+01:00) Brussels, Copenhagen, Madrid,
906 * Paris */
908 "Russian Standard Time", "Russian Daylight Time",
909 "Europe/Moscow"
910 }, /* (GMT+03:00) Moscow, St. Petersburg,
911 * Volgograd */
913 "SA Eastern Standard Time", "SA Eastern Daylight Time",
914 "America/Buenos_Aires"
915 }, /* (GMT-03:00) Buenos Aires, Georgetown */
917 "SA Pacific Standard Time", "SA Pacific Daylight Time",
918 "America/Bogota"
919 }, /* (GMT-05:00) Bogota, Lima, Quito */
921 "SA Western Standard Time", "SA Western Daylight Time",
922 "America/Caracas"
923 }, /* (GMT-04:00) Caracas, La Paz */
925 "Samoa Standard Time", "Samoa Daylight Time",
926 "Pacific/Midway"
927 }, /* (GMT-11:00) Midway Island, Samoa */
929 "SE Asia Standard Time", "SE Asia Daylight Time",
930 "Asia/Bangkok"
931 }, /* (GMT+07:00) Bangkok, Hanoi, Jakarta */
933 "Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
934 "Asia/Kuala_Lumpur"
935 }, /* (GMT+08:00) Kuala Lumpur, Singapore */
937 "South Africa Standard Time", "South Africa Daylight Time",
938 "Africa/Harare"
939 }, /* (GMT+02:00) Harare, Pretoria */
941 "Sri Lanka Standard Time", "Sri Lanka Daylight Time",
942 "Asia/Colombo"
943 }, /* (GMT+06:00) Sri Jayawardenepura */
945 "Taipei Standard Time", "Taipei Daylight Time",
946 "Asia/Taipei"
947 }, /* (GMT+08:00) Taipei */
949 "Tasmania Standard Time", "Tasmania Daylight Time",
950 "Australia/Hobart"
951 }, /* (GMT+10:00) Hobart */
953 "Tokyo Standard Time", "Tokyo Daylight Time",
954 "Asia/Tokyo"
955 }, /* (GMT+09:00) Osaka, Sapporo, Tokyo */
957 "Tonga Standard Time", "Tonga Daylight Time",
958 "Pacific/Tongatapu"
959 }, /* (GMT+13:00) Nuku'alofa */
961 "US Eastern Standard Time", "US Eastern Daylight Time",
962 "US/Eastern"
963 }, /* (GMT-05:00) Indiana (East) */
965 "US Mountain Standard Time", "US Mountain Daylight Time",
966 "US/Arizona"
967 }, /* (GMT-07:00) Arizona */
969 "Vladivostok Standard Time", "Vladivostok Daylight Time",
970 "Asia/Vladivostok"
971 }, /* (GMT+10:00) Vladivostok */
973 "W. Australia Standard Time", "W. Australia Daylight Time",
974 "Australia/Perth"
975 }, /* (GMT+08:00) Perth */
976 /* {"W. Central Africa Standard Time", "W. Central Africa Daylight Time",
977 * * * * * * * ""}, Could not find a match for this one. Excluded for now. *//* (
978 * G MT+01:00) West Central Africa */
980 "W. Europe Standard Time", "W. Europe Daylight Time",
981 "CET"
982 }, /* (GMT+01:00) Amsterdam, Berlin, Bern, Rome,
983 * Stockholm, Vienna */
985 "West Asia Standard Time", "West Asia Daylight Time",
986 "Asia/Karachi"
987 }, /* (GMT+05:00) Islamabad, Karachi, Tashkent */
989 "West Pacific Standard Time", "West Pacific Daylight Time",
990 "Pacific/Guam"
991 }, /* (GMT+10:00) Guam, Port Moresby */
993 "Yakutsk Standard Time", "Yakutsk Daylight Time",
994 "Asia/Yakutsk"
995 }, /* (GMT+09:00) Yakutsk */
997 NULL, NULL, NULL
1001 static const char *
1002 identify_system_timezone(void)
1004 int i;
1005 char tzname[128];
1006 char localtzname[256];
1007 time_t t = time(NULL);
1008 struct tm *tm = localtime(&t);
1009 HKEY rootKey;
1010 int idx;
1012 if (!tm)
1014 ereport(WARNING,
1015 (errmsg_internal("could not determine current date/time: localtime failed")));
1016 return NULL;
1019 memset(tzname, 0, sizeof(tzname));
1020 strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
1022 for (i = 0; win32_tzmap[i].stdname != NULL; i++)
1024 if (strcmp(tzname, win32_tzmap[i].stdname) == 0 ||
1025 strcmp(tzname, win32_tzmap[i].dstname) == 0)
1027 elog(DEBUG4, "TZ \"%s\" matches Windows timezone \"%s\"",
1028 win32_tzmap[i].pgtzname, tzname);
1029 return win32_tzmap[i].pgtzname;
1034 * Localized Windows versions return localized names for the timezone.
1035 * Scan the registry to find the English name, and then try matching
1036 * against our table again.
1038 memset(localtzname, 0, sizeof(localtzname));
1039 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1040 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1042 KEY_READ,
1043 &rootKey) != ERROR_SUCCESS)
1045 ereport(WARNING,
1046 (errmsg_internal("could not open registry key to identify Windows timezone: %i", (int) GetLastError())));
1047 return NULL;
1050 for (idx = 0;; idx++)
1052 char keyname[256];
1053 char zonename[256];
1054 DWORD namesize;
1055 FILETIME lastwrite;
1056 HKEY key;
1057 LONG r;
1059 memset(keyname, 0, sizeof(keyname));
1060 namesize = sizeof(keyname);
1061 if ((r = RegEnumKeyEx(rootKey,
1062 idx,
1063 keyname,
1064 &namesize,
1065 NULL,
1066 NULL,
1067 NULL,
1068 &lastwrite)) != ERROR_SUCCESS)
1070 if (r == ERROR_NO_MORE_ITEMS)
1071 break;
1072 ereport(WARNING,
1073 (errmsg_internal("could not enumerate registry subkeys to identify Windows timezone: %i", (int) r)));
1074 break;
1077 if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
1079 ereport(WARNING,
1080 (errmsg_internal("could not open registry subkey to identify Windows timezone: %i", (int) r)));
1081 break;
1084 memset(zonename, 0, sizeof(zonename));
1085 namesize = sizeof(zonename);
1086 if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
1088 ereport(WARNING,
1089 (errmsg_internal("could not query value for 'std' to identify Windows timezone: %i", (int) r)));
1090 RegCloseKey(key);
1091 break;
1093 if (strcmp(tzname, zonename) == 0)
1095 /* Matched zone */
1096 strcpy(localtzname, keyname);
1097 RegCloseKey(key);
1098 break;
1100 memset(zonename, 0, sizeof(zonename));
1101 namesize = sizeof(zonename);
1102 if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
1104 ereport(WARNING,
1105 (errmsg_internal("could not query value for 'dlt' to identify Windows timezone: %i", (int) r)));
1106 RegCloseKey(key);
1107 break;
1109 if (strcmp(tzname, zonename) == 0)
1111 /* Matched DST zone */
1112 strcpy(localtzname, keyname);
1113 RegCloseKey(key);
1114 break;
1117 RegCloseKey(key);
1120 RegCloseKey(rootKey);
1122 if (localtzname[0])
1124 /* Found a localized name, so scan for that one too */
1125 for (i = 0; win32_tzmap[i].stdname != NULL; i++)
1127 if (strcmp(localtzname, win32_tzmap[i].stdname) == 0 ||
1128 strcmp(localtzname, win32_tzmap[i].dstname) == 0)
1130 elog(DEBUG4, "TZ \"%s\" matches localized Windows timezone \"%s\" (\"%s\")",
1131 win32_tzmap[i].pgtzname, tzname, localtzname);
1132 return win32_tzmap[i].pgtzname;
1137 ereport(WARNING,
1138 (errmsg("could not find a match for Windows timezone \"%s\"",
1139 tzname)));
1140 return NULL;
1142 #endif /* WIN32 */
1147 * We keep loaded timezones in a hashtable so we don't have to
1148 * load and parse the TZ definition file every time one is selected.
1149 * Because we want timezone names to be found case-insensitively,
1150 * the hash key is the uppercased name of the zone.
1152 typedef struct
1154 /* tznameupper contains the all-upper-case name of the timezone */
1155 char tznameupper[TZ_STRLEN_MAX + 1];
1156 pg_tz tz;
1157 } pg_tz_cache;
1159 static HTAB *timezone_cache = NULL;
1162 static bool
1163 init_timezone_hashtable(void)
1165 HASHCTL hash_ctl;
1167 MemSet(&hash_ctl, 0, sizeof(hash_ctl));
1169 hash_ctl.keysize = TZ_STRLEN_MAX + 1;
1170 hash_ctl.entrysize = sizeof(pg_tz_cache);
1172 timezone_cache = hash_create("Timezones",
1174 &hash_ctl,
1175 HASH_ELEM);
1176 if (!timezone_cache)
1177 return false;
1179 return true;
1183 * Load a timezone from file or from cache.
1184 * Does not verify that the timezone is acceptable!
1186 struct pg_tz *
1187 pg_tzset(const char *name)
1189 pg_tz_cache *tzp;
1190 struct state tzstate;
1191 char uppername[TZ_STRLEN_MAX + 1];
1192 char canonname[TZ_STRLEN_MAX + 1];
1193 char *p;
1195 if (strlen(name) > TZ_STRLEN_MAX)
1196 return NULL; /* not going to fit */
1198 if (!timezone_cache)
1199 if (!init_timezone_hashtable())
1200 return NULL;
1203 * Upcase the given name to perform a case-insensitive hashtable search.
1204 * (We could alternatively downcase it, but we prefer upcase so that we
1205 * can get consistently upcased results from tzparse() in case the name is
1206 * a POSIX-style timezone spec.)
1208 p = uppername;
1209 while (*name)
1210 *p++ = pg_toupper((unsigned char) *name++);
1211 *p = '\0';
1213 tzp = (pg_tz_cache *) hash_search(timezone_cache,
1214 uppername,
1215 HASH_FIND,
1216 NULL);
1217 if (tzp)
1219 /* Timezone found in cache, nothing more to do */
1220 return &tzp->tz;
1223 if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
1225 if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0)
1227 /* Unknown timezone. Fail our call instead of loading GMT! */
1228 return NULL;
1230 /* For POSIX timezone specs, use uppercase name as canonical */
1231 strcpy(canonname, uppername);
1234 /* Save timezone in the cache */
1235 tzp = (pg_tz_cache *) hash_search(timezone_cache,
1236 uppername,
1237 HASH_ENTER,
1238 NULL);
1240 /* hash_search already copied uppername into the hash key */
1241 strcpy(tzp->tz.TZname, canonname);
1242 memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
1244 return &tzp->tz;
1249 * Check whether timezone is acceptable.
1251 * What we are doing here is checking for leap-second-aware timekeeping.
1252 * We need to reject such TZ settings because they'll wreak havoc with our
1253 * date/time arithmetic.
1255 * NB: this must NOT ereport(ERROR). The caller must get control back so that
1256 * it can restore the old value of TZ if we don't like the new one.
1258 bool
1259 tz_acceptable(pg_tz *tz)
1261 struct pg_tm *tt;
1262 pg_time_t time2000;
1265 * To detect leap-second timekeeping, run pg_localtime for what should be
1266 * GMT midnight, 2000-01-01. Insist that the tm_sec value be zero; any
1267 * other result has to be due to leap seconds.
1269 time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1270 tt = pg_localtime(&time2000, tz);
1271 if (!tt || tt->tm_sec != 0)
1272 return false;
1274 return true;
1279 * Get a pg_tz struct for the given timezone name. Returns NULL if name
1280 * is invalid or not an "acceptable" zone.
1282 static pg_tz *
1283 get_pg_tz_for_zone(const char *tzname)
1285 pg_tz *tz;
1287 if (!tzname || !tzname[0])
1288 return NULL;
1290 tz = pg_tzset(tzname);
1291 if (!tz)
1292 return NULL;
1294 if (!tz_acceptable(tz))
1295 return NULL;
1297 return tz;
1301 * Identify a suitable default timezone setting based on the environment.
1303 * We first look to the TZ environment variable. If not found or not
1304 * recognized by our own code, we see if we can identify the timezone
1305 * from the behavior of the system timezone library. When all else fails,
1306 * fall back to GMT.
1308 static pg_tz *
1309 select_default_timezone(void)
1311 pg_tz *def_tz;
1313 def_tz = get_pg_tz_for_zone(getenv("TZ"));
1314 if (def_tz)
1315 return def_tz;
1317 def_tz = get_pg_tz_for_zone(identify_system_timezone());
1318 if (def_tz)
1319 return def_tz;
1321 def_tz = get_pg_tz_for_zone("GMT");
1322 if (def_tz)
1323 return def_tz;
1325 ereport(FATAL,
1326 (errmsg("could not select a suitable default timezone"),
1327 errdetail("It appears that your GMT time zone uses leap seconds. PostgreSQL does not support leap seconds.")));
1328 return NULL; /* keep compiler quiet */
1333 * Pre-initialize timezone library
1335 * This is called before GUC variable initialization begins. Its purpose
1336 * is to ensure that elog.c has a pgtz variable available to format timestamps
1337 * with, in case log_line_prefix is set to a value requiring that. We cannot
1338 * set log_timezone yet.
1340 void
1341 pg_timezone_pre_initialize(void)
1344 * We can't use tzload() because we may not know where PGSHAREDIR is (in
1345 * particular this is true in an EXEC_BACKEND subprocess). Since this
1346 * timezone variable will only be used for emergency fallback purposes, it
1347 * seems OK to just use the "lastditch" case provided by tzparse().
1349 if (tzparse("GMT", &gmt_timezone_data.state, TRUE) != 0)
1350 elog(FATAL, "could not initialize GMT timezone");
1351 strcpy(gmt_timezone_data.TZname, "GMT");
1352 gmt_timezone = &gmt_timezone_data;
1356 * Initialize timezone library
1358 * This is called after initial loading of postgresql.conf. If no TimeZone
1359 * setting was found therein, we try to derive one from the environment.
1360 * Likewise for log_timezone.
1362 void
1363 pg_timezone_initialize(void)
1365 pg_tz *def_tz = NULL;
1367 /* Do we need to try to figure the session timezone? */
1368 if (pg_strcasecmp(GetConfigOption("timezone"), "UNKNOWN") == 0)
1370 /* Select setting */
1371 def_tz = select_default_timezone();
1372 session_timezone = def_tz;
1373 /* Tell GUC about the value. Will redundantly call pg_tzset() */
1374 SetConfigOption("timezone", pg_get_timezone_name(def_tz),
1375 PGC_POSTMASTER, PGC_S_ARGV);
1378 /* What about the log timezone? */
1379 if (pg_strcasecmp(GetConfigOption("log_timezone"), "UNKNOWN") == 0)
1381 /* Select setting, but don't duplicate work */
1382 if (!def_tz)
1383 def_tz = select_default_timezone();
1384 log_timezone = def_tz;
1385 /* Tell GUC about the value. Will redundantly call pg_tzset() */
1386 SetConfigOption("log_timezone", pg_get_timezone_name(def_tz),
1387 PGC_POSTMASTER, PGC_S_ARGV);
1393 * Functions to enumerate available timezones
1395 * Note that pg_tzenumerate_next() will return a pointer into the pg_tzenum
1396 * structure, so the data is only valid up to the next call.
1398 * All data is allocated using palloc in the current context.
1400 #define MAX_TZDIR_DEPTH 10
1402 struct pg_tzenum
1404 int baselen;
1405 int depth;
1406 DIR *dirdesc[MAX_TZDIR_DEPTH];
1407 char *dirname[MAX_TZDIR_DEPTH];
1408 struct pg_tz tz;
1411 /* typedef pg_tzenum is declared in pgtime.h */
1413 pg_tzenum *
1414 pg_tzenumerate_start(void)
1416 pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
1417 char *startdir = pstrdup(pg_TZDIR());
1419 ret->baselen = strlen(startdir) + 1;
1420 ret->depth = 0;
1421 ret->dirname[0] = startdir;
1422 ret->dirdesc[0] = AllocateDir(startdir);
1423 if (!ret->dirdesc[0])
1424 ereport(ERROR,
1425 (errcode_for_file_access(),
1426 errmsg("could not open directory \"%s\": %m", startdir)));
1427 return ret;
1430 void
1431 pg_tzenumerate_end(pg_tzenum *dir)
1433 while (dir->depth >= 0)
1435 FreeDir(dir->dirdesc[dir->depth]);
1436 pfree(dir->dirname[dir->depth]);
1437 dir->depth--;
1439 pfree(dir);
1442 pg_tz *
1443 pg_tzenumerate_next(pg_tzenum *dir)
1445 while (dir->depth >= 0)
1447 struct dirent *direntry;
1448 char fullname[MAXPGPATH];
1449 struct stat statbuf;
1451 direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
1453 if (!direntry)
1455 /* End of this directory */
1456 FreeDir(dir->dirdesc[dir->depth]);
1457 pfree(dir->dirname[dir->depth]);
1458 dir->depth--;
1459 continue;
1462 if (direntry->d_name[0] == '.')
1463 continue;
1465 snprintf(fullname, MAXPGPATH, "%s/%s",
1466 dir->dirname[dir->depth], direntry->d_name);
1467 if (stat(fullname, &statbuf) != 0)
1468 ereport(ERROR,
1469 (errcode_for_file_access(),
1470 errmsg("could not stat \"%s\": %m", fullname)));
1472 if (S_ISDIR(statbuf.st_mode))
1474 /* Step into the subdirectory */
1475 if (dir->depth >= MAX_TZDIR_DEPTH - 1)
1476 ereport(ERROR,
1477 (errmsg("timezone directory stack overflow")));
1478 dir->depth++;
1479 dir->dirname[dir->depth] = pstrdup(fullname);
1480 dir->dirdesc[dir->depth] = AllocateDir(fullname);
1481 if (!dir->dirdesc[dir->depth])
1482 ereport(ERROR,
1483 (errcode_for_file_access(),
1484 errmsg("could not open directory \"%s\": %m",
1485 fullname)));
1487 /* Start over reading in the new directory */
1488 continue;
1492 * Load this timezone using tzload() not pg_tzset(), so we don't fill
1493 * the cache
1495 if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
1496 TRUE) != 0)
1498 /* Zone could not be loaded, ignore it */
1499 continue;
1502 if (!tz_acceptable(&dir->tz))
1504 /* Ignore leap-second zones */
1505 continue;
1508 /* Timezone loaded OK. */
1509 return &dir->tz;
1512 /* Nothing more found */
1513 return NULL;