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.13 2004/05/23 23:26:53 tgl Exp $
9
+ * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.14 2004/05/24 02:30:29 tgl Exp $
10
10
*
11
11
*-------------------------------------------------------------------------
12
12
*/
@@ -44,24 +44,21 @@ pg_TZDIR(void)
44
44
* Try to determine the system timezone (as opposed to the timezone
45
45
* set in our own library).
46
46
*/
47
- #define T_YEAR ( 60*60*24*365 )
48
- #define T_MONTH (60*60*24*30 )
47
+ #define T_DAY ((time_t) ( 60*60*24) )
48
+ #define T_MONTH ((time_t) ( 60*60*24*31) )
49
49
50
50
struct tztry
51
51
{
52
- time_t std_t ,
53
- dst_t ;
54
- char std_time [TZ_STRLEN_MAX + 1 ],
55
- dst_time [TZ_STRLEN_MAX + 1 ];
56
- int std_ofs ,
57
- dst_ofs ;
58
- struct tm std_tm ,
59
- dst_tm ;
52
+ char std_zone_name [TZ_STRLEN_MAX + 1 ],
53
+ dst_zone_name [TZ_STRLEN_MAX + 1 ];
54
+ #define MAX_TEST_TIMES 5
55
+ int n_test_times ;
56
+ time_t test_times [MAX_TEST_TIMES ];
60
57
};
61
58
62
59
63
60
static bool
64
- compare_tm (struct tm * s , struct pg_tm * p )
61
+ compare_tm (struct tm * s , struct pg_tm * p )
65
62
{
66
63
if (s -> tm_sec != p -> tm_sec ||
67
64
s -> tm_min != p -> tm_min ||
@@ -77,36 +74,31 @@ compare_tm(struct tm * s, struct pg_tm * p)
77
74
}
78
75
79
76
static bool
80
- try_timezone (char * tzname , struct tztry * tt , bool checkdst )
77
+ try_timezone (char * tzname , struct tztry * tt )
81
78
{
82
- struct pg_tm * pgtm ;
79
+ int i ;
80
+ struct tm * systm ;
81
+ struct pg_tm * pgtm ;
83
82
84
83
if (!pg_tzset (tzname ))
85
- return false; /* If this timezone couldn't be picked at
86
- * all */
84
+ return false; /* can't handle the TZ name at all */
87
85
88
- /* Verify standard time */
89
- pgtm = pg_localtime (& (tt -> std_t ));
90
- if (!pgtm )
91
- return false;
92
- if (!compare_tm (& (tt -> std_tm ), pgtm ))
93
- return false;
94
-
95
- if (!checkdst )
96
- return true;
97
-
98
- /* Now check daylight time */
99
- pgtm = pg_localtime (& (tt -> dst_t ));
100
- if (!pgtm )
101
- return false;
102
- if (!compare_tm (& (tt -> dst_tm ), pgtm ))
103
- return false;
86
+ /* Check for match at all the test times */
87
+ for (i = 0 ; i < tt -> n_test_times ; i ++ )
88
+ {
89
+ pgtm = pg_localtime (& (tt -> test_times [i ]));
90
+ if (!pgtm )
91
+ return false; /* probably shouldn't happen */
92
+ systm = localtime (& (tt -> test_times [i ]));
93
+ if (!compare_tm (systm , pgtm ))
94
+ return false;
95
+ }
104
96
105
97
return true;
106
98
}
107
99
108
100
static int
109
- get_timezone_offset (struct tm * tm )
101
+ get_timezone_offset (struct tm * tm )
110
102
{
111
103
#if defined(HAVE_STRUCT_TM_TM_ZONE )
112
104
return tm -> tm_gmtoff ;
@@ -150,88 +142,132 @@ win32_get_timezone_abbrev(char *tz)
150
142
* Try to identify a timezone name (in our terminology) that matches the
151
143
* observed behavior of the system timezone library. We cannot assume that
152
144
* the system TZ environment setting (if indeed there is one) matches our
153
- * terminology, so ignore it and just look at what localtime() returns.
145
+ * terminology, so we ignore it and just look at what localtime() returns.
154
146
*/
155
147
static char *
156
148
identify_system_timezone (void )
157
149
{
158
- static char __tzbuf [TZ_STRLEN_MAX + 1 ];
159
- bool std_found = false,
160
- dst_found = false;
161
- time_t tnow = time (NULL );
150
+ static char resultbuf [TZ_STRLEN_MAX + 1 ];
151
+ time_t tnow ;
162
152
time_t t ;
153
+ int nowisdst ,
154
+ curisdst ;
155
+ int std_ofs = 0 ;
163
156
struct tztry tt ;
157
+ struct tm * tm ;
164
158
char cbuf [TZ_STRLEN_MAX + 1 ];
165
159
166
160
/* Initialize OS timezone library */
167
161
tzset ();
168
162
163
+ /* No info yet */
169
164
memset (& tt , 0 , sizeof (tt ));
170
165
171
- for (t = tnow ; t < tnow + T_YEAR ; t += T_MONTH )
166
+ /*
167
+ * The idea here is to scan forward from today and try to locate the
168
+ * next two daylight-savings transition boundaries. We will test for
169
+ * correct results on the day before and after each boundary; this
170
+ * gives at least some confidence that we've selected the right DST
171
+ * rule set.
172
+ */
173
+ tnow = time (NULL );
174
+
175
+ /*
176
+ * Round back to a GMT midnight so results don't depend on local time
177
+ * of day
178
+ */
179
+ tnow -= (tnow % T_DAY );
180
+
181
+ /* Always test today, so we have at least one test point */
182
+ tt .test_times [tt .n_test_times ++ ] = tnow ;
183
+
184
+ tm = localtime (& tnow );
185
+ nowisdst = tm -> tm_isdst ;
186
+ curisdst = nowisdst ;
187
+
188
+ if (curisdst == 0 )
172
189
{
173
- struct tm * tm = localtime (& t );
190
+ /* Set up STD zone name, in case we are in a non-DST zone */
191
+ memset (cbuf , 0 , sizeof (cbuf ));
192
+ strftime (cbuf , sizeof (cbuf ) - 1 , "%Z" , tm ); /* zone abbr */
193
+ strcpy (tt .std_zone_name , TZABBREV (cbuf ));
194
+ /* Also preset std_ofs */
195
+ std_ofs = get_timezone_offset (tm );
196
+ }
174
197
175
- if (tm -> tm_isdst == 0 && !std_found )
176
- {
177
- /* Standard time */
178
- memcpy (& tt .std_tm , tm , sizeof (struct tm ));
179
- memset (cbuf , 0 , sizeof (cbuf ));
180
- strftime (cbuf , sizeof (cbuf ) - 1 , "%Z" , tm ); /* zone abbr */
181
- strcpy (tt .std_time , TZABBREV (cbuf ));
182
- tt .std_ofs = get_timezone_offset (tm );
183
- tt .std_t = t ;
184
- std_found = true;
185
- }
186
- else if (tm -> tm_isdst == 1 && !dst_found )
198
+ /*
199
+ * We have to look a little further ahead than one year, in case today
200
+ * is just past a DST boundary that falls earlier in the year than the
201
+ * next similar boundary. Arbitrarily scan up to 14 months.
202
+ */
203
+ for (t = tnow + T_DAY ; t < tnow + T_MONTH * 14 ; t += T_DAY )
204
+ {
205
+ tm = localtime (& t );
206
+ if (tm -> tm_isdst >= 0 && tm -> tm_isdst != curisdst )
187
207
{
188
- /* Daylight time */
189
- memcpy (& tt .dst_tm , tm , sizeof (struct tm ));
208
+ /* Found a boundary */
209
+ tt .test_times [tt .n_test_times ++ ] = t - T_DAY ;
210
+ tt .test_times [tt .n_test_times ++ ] = t ;
211
+ curisdst = tm -> tm_isdst ;
212
+ /* Save STD or DST zone name, also std_ofs */
190
213
memset (cbuf , 0 , sizeof (cbuf ));
191
214
strftime (cbuf , sizeof (cbuf ) - 1 , "%Z" , tm ); /* zone abbr */
192
- strcpy (tt .dst_time , TZABBREV (cbuf ));
193
- tt .dst_ofs = get_timezone_offset (tm );
194
- tt .dst_t = t ;
195
- dst_found = true;
215
+ if (curisdst == 0 )
216
+ {
217
+ strcpy (tt .std_zone_name , TZABBREV (cbuf ));
218
+ std_ofs = get_timezone_offset (tm );
219
+ }
220
+ else
221
+ strcpy (tt .dst_zone_name , TZABBREV (cbuf ));
222
+ /* Have we found two boundaries? */
223
+ if (tt .n_test_times >= 5 )
224
+ break ;
196
225
}
197
- if (std_found && dst_found )
198
- break ; /* Got both standard and daylight */
199
226
}
200
227
201
- if (!std_found )
228
+ /* We should have found a STD zone name by now... */
229
+ if (tt .std_zone_name [0 ] == '\0' )
202
230
{
203
- /* Failed to determine TZ! */
204
231
ereport (LOG ,
205
232
(errmsg ("unable to determine system timezone, defaulting to \"%s\"" , "GMT" ),
206
233
errhint ("You can specify the correct timezone in postgresql.conf." )));
207
234
return NULL ; /* go to GMT */
208
235
}
209
236
210
- if (dst_found )
237
+ /* If we found DST too then try STD<ofs>DST */
238
+ if (tt .dst_zone_name [0 ] != '\0' )
211
239
{
212
- /* Try STD<ofs>DST */
213
- sprintf ( __tzbuf , "%s%d%s" , tt .std_time , - tt . std_ofs / 3600 , tt .dst_time );
214
- if (try_timezone (__tzbuf , & tt , dst_found ))
215
- return __tzbuf ;
240
+ snprintf ( resultbuf , sizeof ( resultbuf ), "%s%d%s" ,
241
+ tt .std_zone_name , - std_ofs / 3600 , tt .dst_zone_name );
242
+ if (try_timezone (resultbuf , & tt ))
243
+ return resultbuf ;
216
244
}
217
- /* Try just the STD timezone */
218
- strcpy (__tzbuf , tt .std_time );
219
- if (try_timezone (__tzbuf , & tt , dst_found ))
220
- return __tzbuf ;
245
+
246
+ /* Try just the STD timezone (works for GMT at least) */
247
+ strcpy (resultbuf , tt .std_zone_name );
248
+ if (try_timezone (resultbuf , & tt ))
249
+ return resultbuf ;
250
+
251
+ /* Try STD<ofs> */
252
+ snprintf (resultbuf , sizeof (resultbuf ), "%s%d" ,
253
+ tt .std_zone_name , - std_ofs / 3600 );
254
+ if (try_timezone (resultbuf , & tt ))
255
+ return resultbuf ;
221
256
222
257
/*
223
- * Did not find the timezone. Fallback to try a GMT zone. Note that the
258
+ * Did not find the timezone. Fallback to use a GMT zone. Note that the
224
259
* zic timezone database names the GMT-offset zones in POSIX style: plus
225
260
* is west of Greenwich. It's unfortunate that this is opposite of SQL
226
261
* conventions. Should we therefore change the names? Probably not...
227
262
*/
228
- sprintf (__tzbuf , "Etc/GMT%s%d" ,
229
- (- tt .std_ofs > 0 ) ? "+" : "" , - tt .std_ofs / 3600 );
263
+ snprintf (resultbuf , sizeof (resultbuf ), "Etc/GMT%s%d" ,
264
+ (- std_ofs > 0 ) ? "+" : "" , - std_ofs / 3600 );
265
+
230
266
ereport (LOG ,
231
- (errmsg ("could not recognize system timezone, defaulting to \"%s\"" ,
232
- __tzbuf ),
233
- errhint ("You can specify the correct timezone in postgresql.conf." )));
234
- return __tzbuf ;
267
+ (errmsg ("could not recognize system timezone, defaulting to \"%s\"" ,
268
+ resultbuf ),
269
+ errhint ("You can specify the correct timezone in postgresql.conf." )));
270
+ return resultbuf ;
235
271
}
236
272
237
273
/*
0 commit comments