Fix identify_system_timezone() so that it tests the behavior of the system

timezone setting in the current year and for 100 years back, rather than
always examining years 1904-2004.  The original coding would have problems
distinguishing zones whose behavior diverged only after 2004; which is a
situation we will surely face sometime, if it's not out there already.

In passing, also prevent selection of the dummy "Factory" timezone, even
if that's exactly what the system is using.  Reporting time as GMT seems
better than that.
This commit is contained in:
Tom Lane 2008-07-01 03:41:25 +00:00
parent 0e69ff3399
commit 2d94cf6bfe

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.29 2004/12/31 22:03:59 pgsql Exp $
* $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.29.4.1 2008/07/01 03:41:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -74,7 +74,7 @@ pg_TZDIR(void)
#define T_WEEK ((time_t) (60*60*24*7))
#define T_MONTH ((time_t) (60*60*24*31))
#define MAX_TEST_TIMES (52*100) /* 100 years, or 1904..2004 */
#define MAX_TEST_TIMES (52*100) /* 100 years */
struct tztry
{
@ -232,6 +232,7 @@ identify_system_timezone(void)
time_t t;
struct tztry tt;
struct tm *tm;
int thisyear;
int bestscore;
char tmptzdir[MAXPGPATH];
int std_ofs;
@ -244,24 +245,46 @@ identify_system_timezone(void)
/*
* Set up the list of dates to be probed to see how well our timezone
* matches the system zone. We first probe January and July of 2004;
* this serves to quickly eliminate the vast majority of the TZ
* database entries. If those dates match, we probe every week from
* 2004 backwards to late 1904. (Weekly resolution is good enough to
* identify DST transition rules, since everybody switches on
* Sundays.) The further back the zone matches, the better we score
* it. This may seem like a rather random way of doing things, but
* experience has shown that system-supplied timezone definitions are
* likely to have DST behavior that is right for the recent past and
* not so accurate further back. Scoring in this way allows us to
* recognize zones that have some commonality with the zic database,
* without insisting on exact match. (Note: we probe Thursdays, not
* Sundays, to avoid triggering DST-transition bugs in localtime
* itself.)
* matches the system zone. We first probe January and July of the
* current year; this serves to quickly eliminate the vast majority of the
* TZ database entries. If those dates match, we probe every week for 100
* years backwards from the current July. (Weekly resolution is good
* enough to identify DST transition rules, since everybody switches on
* Sundays.) This is sufficient to cover most of the Unix time_t range,
* and we don't want to look further than that since many systems won't
* have sane TZ behavior further back anyway. The further
* back the zone matches, the better we score it. This may seem like a
* rather random way of doing things, but experience has shown that
* system-supplied timezone definitions are likely to have DST behavior
* that is right for the recent past and not so accurate further back.
* Scoring in this way allows us to recognize zones that have some
* commonality with the zic database, without insisting on exact match.
* (Note: we probe Thursdays, not Sundays, to avoid triggering
* DST-transition bugs in localtime itself.)
*/
tnow = time(NULL);
tm = localtime(&tnow);
if (!tm)
return NULL; /* give up if localtime is broken... */
thisyear = tm->tm_year + 1900;
t = build_time_t(thisyear, 1, 15);
/*
* Round back to GMT midnight Thursday. This depends on the knowledge
* that the time_t origin is Thu Jan 01 1970. (With a different origin
* we'd be probing some other day of the week, but it wouldn't matter
* anyway unless localtime() had DST-transition bugs.)
*/
t -= (t % T_WEEK);
tt.n_test_times = 0;
tt.test_times[tt.n_test_times++] = build_time_t(2004, 1, 15);
tt.test_times[tt.n_test_times++] = t = build_time_t(2004, 7, 15);
tt.test_times[tt.n_test_times++] = t;
t = build_time_t(thisyear, 7, 15);
t -= (t % T_WEEK);
tt.test_times[tt.n_test_times++] = t;
while (tt.n_test_times < MAX_TEST_TIMES)
{
t -= T_WEEK;
@ -276,7 +299,12 @@ identify_system_timezone(void)
&tt,
&bestscore, resultbuf);
if (bestscore > 0)
{
/* Ignore zic's rather silly "Factory" time zone; use GMT instead */
if (strcmp(resultbuf, "Factory") == 0)
return NULL;
return resultbuf;
}
/*
* Couldn't find a match in the database, so next we try constructed