6
6
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7
7
*
8
8
* IDENTIFICATION
9
- * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.17 2004/06/03 02:08:07 tgl Exp $
9
+ * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.18 2004/07/10 23:06:50 tgl Exp $
10
10
*
11
11
*-------------------------------------------------------------------------
12
12
*/
29
29
30
30
31
31
#define T_DAY ((time_t) (60*60*24))
32
+ #define T_WEEK ((time_t) (60*60*24*7))
32
33
#define T_MONTH ((time_t) (60*60*24*31))
33
34
35
+ #define MAX_TEST_TIMES (52*35) /* 35 years, or 1970..2004 */
36
+
34
37
struct tztry
35
38
{
36
- char std_zone_name [TZ_STRLEN_MAX + 1 ],
37
- dst_zone_name [TZ_STRLEN_MAX + 1 ];
38
- #define MAX_TEST_TIMES 10
39
39
int n_test_times ;
40
40
time_t test_times [MAX_TEST_TIMES ];
41
41
};
@@ -219,27 +219,61 @@ identify_system_timezone(void)
219
219
static char resultbuf [TZ_STRLEN_MAX + 1 ];
220
220
time_t tnow ;
221
221
time_t t ;
222
- int nowisdst ,
223
- curisdst ;
224
- int std_ofs = 0 ;
225
222
struct tztry tt ;
226
223
struct tm * tm ;
227
224
char tmptzdir [MAXPGPATH ];
225
+ int std_ofs ;
226
+ char std_zone_name [TZ_STRLEN_MAX + 1 ],
227
+ dst_zone_name [TZ_STRLEN_MAX + 1 ];
228
228
char cbuf [TZ_STRLEN_MAX + 1 ];
229
229
230
230
/* Initialize OS timezone library */
231
231
tzset ();
232
232
233
- /* No info yet */
234
- memset (& tt , 0 , sizeof (tt ));
233
+ /*
234
+ * Set up the list of dates to be probed to verify that our timezone
235
+ * matches the system zone. We first probe January and July of 1970;
236
+ * this serves to quickly eliminate the vast majority of the TZ database
237
+ * entries. If those dates match, we probe every week from 1970 to
238
+ * late 2004. This exhaustive test is intended to ensure that we have
239
+ * the same DST transition rules as the system timezone. (Note: we
240
+ * probe Thursdays, not Sundays, to avoid triggering DST-transition
241
+ * bugs in localtime itself.)
242
+ *
243
+ * Ideally we'd probe some dates before 1970 too, but that is guaranteed
244
+ * to fail if the system TZ library doesn't cope with DST before 1970.
245
+ */
246
+ tt .n_test_times = 0 ;
247
+ tt .test_times [tt .n_test_times ++ ] = t = build_time_t (1970 , 1 , 15 );
248
+ tt .test_times [tt .n_test_times ++ ] = build_time_t (1970 , 7 , 15 );
249
+ while (tt .n_test_times < MAX_TEST_TIMES )
250
+ {
251
+ t += T_WEEK ;
252
+ tt .test_times [tt .n_test_times ++ ] = t ;
253
+ }
254
+
255
+ /* Search for a matching timezone file */
256
+ strcpy (tmptzdir , pg_TZDIR ());
257
+ if (scan_available_timezones (tmptzdir ,
258
+ tmptzdir + strlen (tmptzdir ) + 1 ,
259
+ & tt ))
260
+ {
261
+ StrNCpy (resultbuf , pg_get_current_timezone (), sizeof (resultbuf ));
262
+ return resultbuf ;
263
+ }
235
264
236
265
/*
237
- * The idea here is to scan forward from today and try to locate the
238
- * next two daylight-savings transition boundaries. We will test for
239
- * correct results on the day before and after each boundary; this
240
- * gives at least some confidence that we've selected the right DST
241
- * rule set.
266
+ * Couldn't find a match in the database, so next we try constructed zone
267
+ * names (like "PST8PDT").
268
+ *
269
+ * First we need to determine the names of the local standard and daylight
270
+ * zones. The idea here is to scan forward from today until we have
271
+ * seen both zones, if both are in use.
242
272
*/
273
+ memset (std_zone_name , 0 , sizeof (std_zone_name ));
274
+ memset (dst_zone_name , 0 , sizeof (dst_zone_name ));
275
+ std_ofs = 0 ;
276
+
243
277
tnow = time (NULL );
244
278
245
279
/*
@@ -248,103 +282,62 @@ identify_system_timezone(void)
248
282
*/
249
283
tnow -= (tnow % T_DAY );
250
284
251
- /* Always test today, so we have at least one test point */
252
- tt .test_times [tt .n_test_times ++ ] = tnow ;
253
-
254
- tm = localtime (& tnow );
255
- nowisdst = tm -> tm_isdst ;
256
- curisdst = nowisdst ;
257
-
258
- if (curisdst == 0 )
259
- {
260
- /* Set up STD zone name, in case we are in a non-DST zone */
261
- memset (cbuf , 0 , sizeof (cbuf ));
262
- strftime (cbuf , sizeof (cbuf ) - 1 , "%Z" , tm ); /* zone abbr */
263
- strcpy (tt .std_zone_name , TZABBREV (cbuf ));
264
- /* Also preset std_ofs */
265
- std_ofs = get_timezone_offset (tm );
266
- }
267
-
268
285
/*
269
286
* We have to look a little further ahead than one year, in case today
270
287
* is just past a DST boundary that falls earlier in the year than the
271
288
* next similar boundary. Arbitrarily scan up to 14 months.
272
289
*/
273
- for (t = tnow + T_DAY ; t < tnow + T_MONTH * 14 ; t += T_DAY )
290
+ for (t = tnow ; t <= tnow + T_MONTH * 14 ; t += T_MONTH )
274
291
{
275
292
tm = localtime (& t );
276
- if (tm -> tm_isdst >= 0 && tm -> tm_isdst != curisdst )
293
+ if (tm -> tm_isdst < 0 )
294
+ continue ;
295
+ if (tm -> tm_isdst == 0 && std_zone_name [0 ] == '\0' )
277
296
{
278
- /* Found a boundary */
279
- tt .test_times [tt .n_test_times ++ ] = t - T_DAY ;
280
- tt .test_times [tt .n_test_times ++ ] = t ;
281
- curisdst = tm -> tm_isdst ;
282
- /* Save STD or DST zone name, also std_ofs */
297
+ /* found STD zone */
283
298
memset (cbuf , 0 , sizeof (cbuf ));
284
299
strftime (cbuf , sizeof (cbuf ) - 1 , "%Z" , tm ); /* zone abbr */
285
- if (curisdst == 0 )
286
- {
287
- strcpy (tt .std_zone_name , TZABBREV (cbuf ));
288
- std_ofs = get_timezone_offset (tm );
289
- }
290
- else
291
- strcpy (tt .dst_zone_name , TZABBREV (cbuf ));
292
- /* Have we found two boundaries? */
293
- if (tt .n_test_times >= 5 )
294
- break ;
300
+ strcpy (std_zone_name , TZABBREV (cbuf ));
301
+ std_ofs = get_timezone_offset (tm );
295
302
}
303
+ if (tm -> tm_isdst > 0 && dst_zone_name [0 ] == '\0' )
304
+ {
305
+ /* found DST zone */
306
+ memset (cbuf , 0 , sizeof (cbuf ));
307
+ strftime (cbuf , sizeof (cbuf ) - 1 , "%Z" , tm ); /* zone abbr */
308
+ strcpy (dst_zone_name , TZABBREV (cbuf ));
309
+ }
310
+ /* Done if found both */
311
+ if (std_zone_name [0 ] && dst_zone_name [0 ])
312
+ break ;
296
313
}
297
314
298
- /*
299
- * Add a couple of historical dates as well; without this we are likely
300
- * to choose an accidental match, such as Antartica/Palmer when we
301
- * really want America/Santiago. Ideally we'd probe some dates before
302
- * 1970 too, but that is guaranteed to fail if the system TZ library
303
- * doesn't cope with DST before 1970.
304
- */
305
- tt .test_times [tt .n_test_times ++ ] = build_time_t (1970 , 1 , 15 );
306
- tt .test_times [tt .n_test_times ++ ] = build_time_t (1970 , 7 , 15 );
307
- tt .test_times [tt .n_test_times ++ ] = build_time_t (1990 , 4 , 1 );
308
- tt .test_times [tt .n_test_times ++ ] = build_time_t (1990 , 10 , 1 );
309
-
310
- Assert (tt .n_test_times <= MAX_TEST_TIMES );
311
-
312
315
/* We should have found a STD zone name by now... */
313
- if (tt . std_zone_name [0 ] == '\0' )
316
+ if (std_zone_name [0 ] == '\0' )
314
317
{
315
318
ereport (LOG ,
316
319
(errmsg ("unable to determine system timezone, defaulting to \"%s\"" , "GMT" ),
317
320
errhint ("You can specify the correct timezone in postgresql.conf." )));
318
321
return NULL ; /* go to GMT */
319
322
}
320
323
321
- /* Search for a matching timezone file */
322
- strcpy (tmptzdir , pg_TZDIR ());
323
- if (scan_available_timezones (tmptzdir ,
324
- tmptzdir + strlen (tmptzdir ) + 1 ,
325
- & tt ))
326
- {
327
- StrNCpy (resultbuf , pg_get_current_timezone (), sizeof (resultbuf ));
328
- return resultbuf ;
329
- }
330
-
331
324
/* If we found DST then try STD<ofs>DST */
332
- if (tt . dst_zone_name [0 ] != '\0' )
325
+ if (dst_zone_name [0 ] != '\0' )
333
326
{
334
327
snprintf (resultbuf , sizeof (resultbuf ), "%s%d%s" ,
335
- tt . std_zone_name , - std_ofs / 3600 , tt . dst_zone_name );
328
+ std_zone_name , - std_ofs / 3600 , dst_zone_name );
336
329
if (try_timezone (resultbuf , & tt ))
337
330
return resultbuf ;
338
331
}
339
332
340
333
/* Try just the STD timezone (works for GMT at least) */
341
- strcpy (resultbuf , tt . std_zone_name );
334
+ strcpy (resultbuf , std_zone_name );
342
335
if (try_timezone (resultbuf , & tt ))
343
336
return resultbuf ;
344
337
345
338
/* Try STD<ofs> */
346
339
snprintf (resultbuf , sizeof (resultbuf ), "%s%d" ,
347
- tt . std_zone_name , - std_ofs / 3600 );
340
+ std_zone_name , - std_ofs / 3600 );
348
341
if (try_timezone (resultbuf , & tt ))
349
342
return resultbuf ;
350
343
0 commit comments