/* * Return true if the given zone name is valid and is an "acceptable" zone. */ static bool validate_zone(const char *tzname) { pg_tz *tz; if (!tzname || !tzname[0]) return false; tz = pg_load_tz(tzname); if (!tz) return false; if (!pg_tz_acceptable(tz)) return false; return true; }
pg_tz * pg_tzenumerate_next(pg_tzenum *dir) { while (dir->depth >= 0) { struct dirent *direntry; char fullname[MAXPGPATH * 2]; struct stat statbuf; direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]); if (!direntry) { /* End of this directory */ FreeDir(dir->dirdesc[dir->depth]); pfree(dir->dirname[dir->depth]); dir->depth--; continue; } if (direntry->d_name[0] == '.') continue; snprintf(fullname, sizeof(fullname), "%s/%s", dir->dirname[dir->depth], direntry->d_name); if (stat(fullname, &statbuf) != 0) ereport(ERROR, (errcode_for_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] = AllocateDir(fullname); if (!dir->dirdesc[dir->depth]) ereport(ERROR, (errcode_for_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 (!pg_tz_acceptable(&dir->tz)) { /* Ignore leap-second zones */ continue; } /* Timezone loaded OK. */ return &dir->tz; } /* Nothing more found */ return NULL; }
/* * 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 definition */ tz = pg_load_tz(tzname); if (!tz) return -1; /* unrecognized zone name */ /* Reject if leap seconds involved */ if (!pg_tz_acceptable(tz)) { #ifdef DEBUG_IDENTIFY_TIMEZONE fprintf(stderr, "Reject TZ \"%s\": uses leap seconds\n", tzname); #endif 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) { #ifdef DEBUG_IDENTIFY_TIMEZONE fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data\n", 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"); #endif return i; } if (!compare_tm(systm, pgtm)) { #ifdef DEBUG_IDENTIFY_TIMEZONE fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s\n", 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"); #endif 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) { #ifdef DEBUG_IDENTIFY_TIMEZONE fprintf(stderr, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"\n", tzname, i, (long) pgtt, pgtm->tm_zone, cbuf); #endif return i; } } } #ifdef DEBUG_IDENTIFY_TIMEZONE fprintf(stderr, "TZ \"%s\" gets max score %d\n", tzname, i); #endif return i; }