]> granicus.if.org Git - postgresql/commitdiff
Fix identify_system_timezone() so that it tests the behavior of the system
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 Jul 2008 03:41:18 +0000 (03:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 Jul 2008 03:41:18 +0000 (03:41 +0000)
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.

src/timezone/pgtz.c

index b3381c28e65196480d034536e65f72529409f1f2..9afac9d320a0218c3d3e2d1aa59e74ae88243fca 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.38.2.1 2005/11/22 18:23:31 momjian Exp $
+ *       $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.38.2.2 2008/07/01 03:41:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,7 +80,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
 {
@@ -249,6 +249,7 @@ identify_system_timezone(void)
        time_t          t;
        struct tztry tt;
        struct tm  *tm;
+       int                     thisyear;
        int                     bestscore;
        char            tmptzdir[MAXPGPATH];
        int                     std_ofs;
@@ -261,11 +262,14 @@ 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
+        * 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
@@ -275,9 +279,29 @@ identify_system_timezone(void)
         * (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;
@@ -292,7 +316,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 zone