/* * Get a pg_tz struct for the given timezone name. Returns NULL if name * is invalid or not an "acceptable" zone. */ static pg_tz* get_pg_tz_for_zone(const char *tzname) { pg_tz* tz; if (!tzname || !tzname[0]) return NULL; tz = pg_tzset(tzname); if (!tz) return NULL; if (!tz_acceptable(tz)) return NULL; return tz; }
/* * Set the global timezone. Verify that it's acceptable first. */ static bool set_global_timezone(const char *tzname) { pg_tz *tznew; if (!tzname || !tzname[0]) return false; tznew = pg_tzset(tzname); if (!tznew) return false; if (!tz_acceptable(tznew)) return false; global_timezone = tznew; return true; }
/* * See how well a specific timezone setting matches the system behavior * * We score a timezone setting according to the number of test times it * matches. (The test times are ordered later-to-earlier, but this routine * doesn't actually know that; it just scans until the first non-match.) * * We return -1 for a completely unusable setting; this is worse than the * score of zero for a setting that works but matches not even the first * test time. */ static int score_timezone(const char *tzname, struct tztry * tt) { int i; pg_time_t pgtt; struct tm* systm; struct pg_tm* pgtm; char cbuf[TZ_STRLEN_MAX + 1]; pg_tz tz; /* * Load timezone directly. Don't use pg_tzset, because we don't want all * timezones loaded in the cache at startup. */ if (tzload(tzname, NULL, &tz.state, TRUE) != 0) { if (tzname[0] == ':' || tzparse(tzname, &tz.state, FALSE) != 0) { return -1; /* can't handle the TZ name at all */ } } /* Reject if leap seconds involved */ if (!tz_acceptable(&tz)) { elog(DEBUG4, "Reject TZ \"%s\": uses leap seconds", tzname); return -1; } /* Check for match at all the test times */ for (i = 0; i < tt->n_test_times; i++) { pgtt = (pg_time_t) (tt->test_times[i]); pgtm = pg_localtime(&pgtt, &tz); if (!pgtm) return -1; /* probably shouldn't happen */ systm = localtime(&(tt->test_times[i])); if (!systm) { elog(DEBUG4, "TZ \"%s\" scores %d:" " at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data", tzname, i, (long) pgtt, pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday, pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec, pgtm->tm_isdst ? "dst" : "std"); return i; } if (!compare_tm(systm, pgtm)) { elog(DEBUG4, "TZ \"%s\" scores %d:" " at %ld %04d-%02d-%02d %02d:%02d:%02d %s" " versus %04d-%02d-%02d %02d:%02d:%02d %s", tzname, i, (long) pgtt, pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday, pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec, pgtm->tm_isdst ? "dst" : "std", systm->tm_year + 1900, systm->tm_mon + 1, systm->tm_mday, systm->tm_hour, systm->tm_min, systm->tm_sec, systm->tm_isdst ? "dst" : "std"); return i; } if (systm->tm_isdst >= 0) { /* Check match of zone names, too */ if (pgtm->tm_zone == NULL) return -1; /* probably shouldn't happen */ memset(cbuf, 0, sizeof(cbuf)); strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm); /* zone abbr */ if (strcmp(cbuf, pgtm->tm_zone) != 0) { elog(DEBUG4, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"", tzname, i, (long) pgtt, pgtm->tm_zone, cbuf); return i; } } } elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i); return i; }
pg_tz* pg_tzenumerate_next(pg_tzenum *dir) { while (dir->depth >= 0) { struct dirent *direntry; char fullname[MAX_PG_PATH]; struct stat statbuf; direntry = read_dir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]); if (!direntry) { /* End of this directory */ free_dir(dir->dirdesc[dir->depth]); pfree(dir->dirname[dir->depth]); dir->depth--; continue; } if (direntry->d_name[0] == '.') continue; snprintf(fullname, MAX_PG_PATH, "%s/%s", dir->dirname[dir->depth], direntry->d_name); if (stat(fullname, &statbuf) != 0) ereport(ERROR, ( errcode_file_access(), errmsg("could not stat \"%s\": %m", fullname))); if (S_ISDIR(statbuf.st_mode)) { /* Step into the subdirectory */ if (dir->depth >= MAX_TZDIR_DEPTH - 1) ereport(ERROR, ( errmsg_internal("timezone directory stack overflow"))); dir->depth++; dir->dirname[dir->depth] = pstrdup(fullname); dir->dirdesc[dir->depth] = alloc_dir(fullname); if (!dir->dirdesc[dir->depth]) ereport(ERROR, ( errcode_file_access(), errmsg("could not open directory \"%s\": %m", fullname))); /* Start over reading in the new directory */ continue; } /* * Load this timezone using tzload() not pg_tzset(), so we don't fill * the cache */ if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state, TRUE) != 0) { /* Zone could not be loaded, ignore it */ continue; } if (!tz_acceptable(&dir->tz)) { /* Ignore leap-second zones */ continue; } /* Timezone loaded OK. */ return &dir->tz; } /* Nothing more found */ return NULL; }