]>
Commit | Line | Data |
---|---|---|
df4ef2ab | 1 | /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc. |
f65fd747 | 2 | This file is part of the GNU C Library. |
28f540f4 | 3 | |
f65fd747 UD |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Library General Public License as | |
6 | published by the Free Software Foundation; either version 2 of the | |
7 | License, or (at your option) any later version. | |
28f540f4 | 8 | |
f65fd747 UD |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Library General Public License for more details. | |
28f540f4 | 13 | |
f65fd747 UD |
14 | You should have received a copy of the GNU Library General Public |
15 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
16 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
17 | Boston, MA 02111-1307, USA. */ | |
28f540f4 | 18 | |
28f540f4 RM |
19 | #include <ctype.h> |
20 | #include <stddef.h> | |
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | #include <time.h> | |
25 | ||
26 | /* Defined in mktime.c. */ | |
f65fd747 | 27 | extern const unsigned short int __mon_yday[2][13]; |
28f540f4 RM |
28 | |
29 | #define NOID | |
30 | #include "tzfile.h" | |
31 | ||
32 | extern int __use_tzfile; | |
f65fd747 UD |
33 | extern void __tzfile_read __P ((const char *file)); |
34 | extern void __tzfile_default __P ((char *std, char *dst, | |
35 | long int stdoff, long int dstoff)); | |
36 | extern int __tz_compute __P ((time_t timer, const struct tm *tm)); | |
28f540f4 | 37 | |
28f540f4 RM |
38 | char *__tzname[2] = { (char *) "GMT", (char *) "GMT" }; |
39 | int __daylight = 0; | |
40 | long int __timezone = 0L; | |
41 | ||
0ac2e7d8 BK |
42 | weak_alias (__tzname, tzname) |
43 | weak_alias (__daylight, daylight) | |
44 | weak_alias (__timezone, timezone) | |
45 | ||
28f540f4 RM |
46 | |
47 | #define min(a, b) ((a) < (b) ? (a) : (b)) | |
48 | #define max(a, b) ((a) > (b) ? (a) : (b)) | |
49 | #define sign(x) ((x) < 0 ? -1 : 1) | |
50 | ||
51 | ||
52 | /* This structure contains all the information about a | |
53 | timezone given in the POSIX standard TZ envariable. */ | |
54 | typedef struct | |
55 | { | |
56 | char *name; | |
57 | ||
58 | /* When to change. */ | |
59 | enum { J0, J1, M } type; /* Interpretation of: */ | |
60 | unsigned short int m, n, d; /* Month, week, day. */ | |
61 | unsigned int secs; /* Time of day. */ | |
62 | ||
63 | long int offset; /* Seconds east of GMT (west if < 0). */ | |
64 | ||
65 | /* We cache the computed time of change for a | |
66 | given year so we don't have to recompute it. */ | |
67 | time_t change; /* When to change to this zone. */ | |
68 | int computed_for; /* Year above is computed for. */ | |
69 | } tz_rule; | |
70 | ||
71 | /* tz_rules[0] is standard, tz_rules[1] is daylight. */ | |
72 | static tz_rule tz_rules[2]; | |
f65fd747 UD |
73 | |
74 | ||
75 | static int compute_change __P ((tz_rule *rule, int year)); | |
28f540f4 | 76 | \f |
68dbb3a6 | 77 | static char *old_tz = NULL; |
28f540f4 RM |
78 | |
79 | /* Interpret the TZ envariable. */ | |
df4ef2ab | 80 | void __tzset_internal __P ((int always)); |
28f540f4 | 81 | void |
df4ef2ab UD |
82 | __tzset_internal (always) |
83 | int always; | |
28f540f4 | 84 | { |
df4ef2ab | 85 | static int is_initialized = 0; |
f65fd747 | 86 | register const char *tz; |
28f540f4 RM |
87 | register size_t l; |
88 | unsigned short int hh, mm, ss; | |
89 | unsigned short int whichrule; | |
90 | ||
df4ef2ab UD |
91 | if (is_initialized && !always) |
92 | return; | |
93 | is_initialized = 1; | |
94 | ||
68dbb3a6 UD |
95 | /* Examine the TZ environment variable. */ |
96 | tz = getenv ("TZ"); | |
97 | ||
98 | /* A leading colon means "implementation defined syntax". | |
99 | We ignore the colon and always use the same algorithm: | |
100 | try a data file, and if none exists parse the 1003.1 syntax. */ | |
101 | if (tz && *tz == ':') | |
102 | ++tz; | |
103 | ||
104 | /* Check whether the value changes since the last run. */ | |
105 | if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0) | |
106 | /* No change, simply return. */ | |
107 | return; | |
108 | ||
28f540f4 RM |
109 | /* Free old storage. */ |
110 | if (tz_rules[0].name != NULL && *tz_rules[0].name != '\0') | |
5290baf0 | 111 | free((void *) tz_rules[0].name); |
28f540f4 RM |
112 | if (tz_rules[1].name != NULL && *tz_rules[1].name != '\0' && |
113 | tz_rules[1].name != tz_rules[0].name) | |
5290baf0 UD |
114 | free((void *) tz_rules[1].name); |
115 | tz_rules[0].name = NULL; | |
116 | tz_rules[1].name = NULL; | |
28f540f4 | 117 | |
68dbb3a6 UD |
118 | /* Save the value of `tz'. */ |
119 | if (old_tz != NULL) | |
120 | free (old_tz); | |
121 | old_tz = tz ? __strdup (tz) : NULL; | |
a3b58440 RM |
122 | |
123 | /* Try to read a data file. */ | |
124 | __tzfile_read (tz); | |
125 | if (__use_tzfile) | |
68dbb3a6 | 126 | return; |
28f540f4 | 127 | |
a3b58440 RM |
128 | /* No data file found. Default to UTC if nothing specified. */ |
129 | ||
28f540f4 RM |
130 | if (tz == NULL || *tz == '\0') |
131 | { | |
a3b58440 RM |
132 | static const char UTC[] = "UTC"; |
133 | size_t len = sizeof UTC; | |
68dbb3a6 | 134 | tz_rules[0].name = (char *) malloc (len); |
a3b58440 RM |
135 | if (tz_rules[0].name == NULL) |
136 | return; | |
68dbb3a6 | 137 | tz_rules[1].name = (char *) malloc (len); |
a3b58440 RM |
138 | if (tz_rules[1].name == NULL) |
139 | return; | |
f65fd747 UD |
140 | memcpy ((void *) tz_rules[0].name, UTC, len); |
141 | memcpy ((void *) tz_rules[1].name, UTC, len); | |
a3b58440 RM |
142 | tz_rules[0].type = tz_rules[1].type = J0; |
143 | tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0; | |
144 | tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0; | |
145 | tz_rules[0].secs = tz_rules[1].secs = 0; | |
146 | tz_rules[0].offset = tz_rules[1].offset = 0L; | |
147 | tz_rules[0].change = tz_rules[1].change = (time_t) -1; | |
148 | tz_rules[0].computed_for = tz_rules[1].computed_for = 0; | |
28f540f4 RM |
149 | return; |
150 | } | |
151 | ||
933e73fa | 152 | /* Clear out old state and reset to unnamed UTC. */ |
28f540f4 RM |
153 | memset (tz_rules, 0, sizeof tz_rules); |
154 | tz_rules[0].name = tz_rules[1].name = (char *) ""; | |
155 | ||
156 | /* Get the standard timezone name. */ | |
157 | tz_rules[0].name = (char *) malloc (strlen (tz) + 1); | |
158 | if (tz_rules[0].name == NULL) | |
68dbb3a6 UD |
159 | { |
160 | /* Clear the old tz name so we will try again. */ | |
161 | free (old_tz); | |
162 | old_tz = NULL; | |
163 | return; | |
164 | } | |
28f540f4 RM |
165 | |
166 | if (sscanf(tz, "%[^0-9,+-]", tz_rules[0].name) != 1 || | |
167 | (l = strlen(tz_rules[0].name)) < 3) | |
168 | { | |
169 | free (tz_rules[0].name); | |
170 | tz_rules[0].name = (char *) ""; | |
171 | return; | |
172 | } | |
173 | ||
174 | { | |
f65fd747 | 175 | char *n = realloc ((void *) tz_rules[0].name, l + 1); |
28f540f4 RM |
176 | if (n != NULL) |
177 | tz_rules[0].name = n; | |
178 | } | |
179 | ||
180 | tz += l; | |
181 | ||
933e73fa | 182 | /* Figure out the standard offset from UTC. */ |
28f540f4 RM |
183 | if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit(*tz))) |
184 | return; | |
185 | ||
186 | if (*tz == '-' || *tz == '+') | |
187 | tz_rules[0].offset = *tz++ == '-' ? 1L : -1L; | |
188 | else | |
189 | tz_rules[0].offset = -1L; | |
190 | switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) | |
191 | { | |
192 | default: | |
193 | return; | |
194 | case 1: | |
195 | mm = 0; | |
196 | case 2: | |
197 | ss = 0; | |
198 | case 3: | |
199 | break; | |
200 | } | |
f65fd747 UD |
201 | tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) + |
202 | (min (hh, 23) * 60 * 60)); | |
28f540f4 RM |
203 | |
204 | for (l = 0; l < 3; ++l) | |
205 | { | |
206 | while (isdigit(*tz)) | |
207 | ++tz; | |
208 | if (l < 2 && *tz == ':') | |
209 | ++tz; | |
210 | } | |
211 | ||
212 | /* Get the DST timezone name (if any). */ | |
213 | if (*tz != '\0') | |
214 | { | |
f65fd747 | 215 | char *n = malloc (strlen (tz) + 1); |
28f540f4 RM |
216 | if (n != NULL) |
217 | { | |
218 | tz_rules[1].name = n; | |
f65fd747 UD |
219 | if (sscanf (tz, "%[^0-9,+-]", tz_rules[1].name) != 1 || |
220 | (l = strlen (tz_rules[1].name)) < 3) | |
28f540f4 RM |
221 | { |
222 | free (n); | |
223 | tz_rules[1].name = (char *) ""; | |
224 | goto done_names; /* Punt on name, set up the offsets. */ | |
225 | } | |
f65fd747 | 226 | n = realloc ((void *) tz_rules[1].name, l + 1); |
28f540f4 RM |
227 | if (n != NULL) |
228 | tz_rules[1].name = n; | |
f65fd747 UD |
229 | |
230 | tz += l; | |
28f540f4 | 231 | } |
fd26970f UD |
232 | |
233 | /* Figure out the DST offset from GMT. */ | |
234 | if (*tz == '-' || *tz == '+') | |
235 | tz_rules[1].offset = *tz++ == '-' ? 1L : -1L; | |
236 | else | |
237 | tz_rules[1].offset = -1L; | |
238 | ||
239 | switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) | |
240 | { | |
241 | default: | |
242 | /* Default to one hour later than standard time. */ | |
243 | tz_rules[1].offset = tz_rules[0].offset + (60 * 60); | |
244 | break; | |
245 | ||
246 | case 1: | |
247 | mm = 0; | |
248 | case 2: | |
249 | ss = 0; | |
250 | case 3: | |
251 | tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) + | |
252 | (min (hh, 23) * (60 * 60))); | |
253 | break; | |
254 | } | |
255 | for (l = 0; l < 3; ++l) | |
256 | { | |
257 | while (isdigit (*tz)) | |
258 | ++tz; | |
259 | if (l < 2 && *tz == ':') | |
260 | ++tz; | |
261 | } | |
28f540f4 | 262 | } |
5290baf0 UD |
263 | else |
264 | /* There is no DST. */ | |
265 | tz_rules[1].name = tz_rules[0].name; | |
28f540f4 | 266 | |
28f540f4 RM |
267 | done_names: |
268 | ||
269 | if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0')) | |
270 | { | |
271 | /* There is no rule. See if there is a default rule file. */ | |
272 | __tzfile_default (tz_rules[0].name, tz_rules[1].name, | |
273 | tz_rules[0].offset, tz_rules[1].offset); | |
274 | if (__use_tzfile) | |
275 | { | |
68dbb3a6 UD |
276 | free (old_tz); |
277 | old_tz = NULL; | |
28f540f4 RM |
278 | return; |
279 | } | |
280 | } | |
281 | ||
282 | /* Figure out the standard <-> DST rules. */ | |
283 | for (whichrule = 0; whichrule < 2; ++whichrule) | |
284 | { | |
285 | register tz_rule *tzr = &tz_rules[whichrule]; | |
1cbca0d9 | 286 | |
28f540f4 RM |
287 | if (*tz == ',') |
288 | { | |
289 | ++tz; | |
290 | if (*tz == '\0') | |
291 | return; | |
292 | } | |
1cbca0d9 | 293 | |
28f540f4 RM |
294 | /* Get the date of the change. */ |
295 | if (*tz == 'J' || isdigit (*tz)) | |
296 | { | |
297 | char *end; | |
298 | tzr->type = *tz == 'J' ? J1 : J0; | |
299 | if (tzr->type == J1 && !isdigit (*++tz)) | |
300 | return; | |
301 | tzr->d = (unsigned short int) strtoul (tz, &end, 10); | |
302 | if (end == tz || tzr->d > 365) | |
303 | return; | |
304 | else if (tzr->type == J1 && tzr->d == 0) | |
305 | return; | |
306 | tz = end; | |
307 | } | |
308 | else if (*tz == 'M') | |
309 | { | |
310 | int n; | |
311 | tzr->type = M; | |
312 | if (sscanf (tz, "M%hu.%hu.%hu%n", | |
313 | &tzr->m, &tzr->n, &tzr->d, &n) != 3 || | |
314 | tzr->m < 1 || tzr->m > 12 || | |
315 | tzr->n < 1 || tzr->n > 5 || tzr->d > 6) | |
316 | return; | |
317 | tz += n; | |
318 | } | |
319 | else if (*tz == '\0') | |
320 | { | |
321 | /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */ | |
322 | tzr->type = M; | |
323 | if (tzr == &tz_rules[0]) | |
324 | { | |
325 | tzr->m = 4; | |
326 | tzr->n = 1; | |
327 | tzr->d = 0; | |
328 | } | |
329 | else | |
330 | { | |
331 | tzr->m = 10; | |
332 | tzr->n = 5; | |
333 | tzr->d = 0; | |
334 | } | |
335 | } | |
336 | else | |
337 | return; | |
1cbca0d9 | 338 | |
28f540f4 RM |
339 | if (*tz != '\0' && *tz != '/' && *tz != ',') |
340 | return; | |
341 | else if (*tz == '/') | |
342 | { | |
343 | /* Get the time of day of the change. */ | |
344 | ++tz; | |
345 | if (*tz == '\0') | |
346 | return; | |
347 | switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) | |
348 | { | |
349 | default: | |
350 | hh = 2; /* Default to 2:00 AM. */ | |
351 | case 1: | |
352 | mm = 0; | |
353 | case 2: | |
354 | ss = 0; | |
355 | case 3: | |
356 | break; | |
357 | } | |
358 | for (l = 0; l < 3; ++l) | |
359 | { | |
f65fd747 | 360 | while (isdigit (*tz)) |
28f540f4 RM |
361 | ++tz; |
362 | if (l < 2 && *tz == ':') | |
363 | ++tz; | |
364 | } | |
365 | tzr->secs = (hh * 60 * 60) + (mm * 60) + ss; | |
366 | } | |
367 | else | |
368 | /* Default to 2:00 AM. */ | |
369 | tzr->secs = 2 * 60 * 60; | |
370 | ||
371 | tzr->computed_for = -1; | |
372 | } | |
28f540f4 RM |
373 | } |
374 | \f | |
375 | /* Maximum length of a timezone name. __tz_compute keeps this up to date | |
376 | (never decreasing it) when ! __use_tzfile. | |
377 | tzfile.c keeps it up to date when __use_tzfile. */ | |
f0f1bf85 | 378 | size_t __tzname_cur_max; |
28f540f4 RM |
379 | |
380 | long int | |
f65fd747 | 381 | __tzname_max () |
28f540f4 | 382 | { |
df4ef2ab | 383 | __tzset_internal (0); |
28f540f4 RM |
384 | |
385 | return __tzname_cur_max; | |
386 | } | |
387 | \f | |
388 | /* Figure out the exact time (as a time_t) in YEAR | |
389 | when the change described by RULE will occur and | |
390 | put it in RULE->change, saving YEAR in RULE->computed_for. | |
391 | Return nonzero if successful, zero on failure. */ | |
392 | static int | |
f65fd747 UD |
393 | compute_change (rule, year) |
394 | tz_rule *rule; | |
395 | int year; | |
28f540f4 RM |
396 | { |
397 | register time_t t; | |
398 | int y; | |
399 | ||
400 | if (year != -1 && rule->computed_for == year) | |
401 | /* Operations on times in 1969 will be slower. Oh well. */ | |
402 | return 1; | |
1cbca0d9 | 403 | |
28f540f4 RM |
404 | /* First set T to January 1st, 0:00:00 GMT in YEAR. */ |
405 | t = 0; | |
406 | for (y = 1970; y < year; ++y) | |
407 | t += SECSPERDAY * (__isleap (y) ? 366 : 365); | |
408 | ||
409 | switch (rule->type) | |
410 | { | |
411 | case J1: | |
412 | /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years. | |
413 | In non-leap years, or if the day number is 59 or less, just | |
414 | add SECSPERDAY times the day number-1 to the time of | |
415 | January 1, midnight, to get the day. */ | |
416 | t += (rule->d - 1) * SECSPERDAY; | |
417 | if (rule->d >= 60 && __isleap (year)) | |
418 | t += SECSPERDAY; | |
419 | break; | |
420 | ||
421 | case J0: | |
422 | /* n - Day of year. | |
423 | Just add SECSPERDAY times the day number to the time of Jan 1st. */ | |
424 | t += rule->d * SECSPERDAY; | |
425 | break; | |
426 | ||
427 | case M: | |
428 | /* Mm.n.d - Nth "Dth day" of month M. */ | |
429 | { | |
430 | register int i, d, m1, yy0, yy1, yy2, dow; | |
f65fd747 | 431 | register const unsigned short int *myday = |
80fd7387 | 432 | &__mon_yday[__isleap (year)][rule->m]; |
28f540f4 RM |
433 | |
434 | /* First add SECSPERDAY for each day in months before M. */ | |
80fd7387 | 435 | t += myday[-1] * SECSPERDAY; |
28f540f4 RM |
436 | |
437 | /* Use Zeller's Congruence to get day-of-week of first day of month. */ | |
438 | m1 = (rule->m + 9) % 12 + 1; | |
439 | yy0 = (rule->m <= 2) ? (year - 1) : year; | |
440 | yy1 = yy0 / 100; | |
441 | yy2 = yy0 % 100; | |
442 | dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; | |
443 | if (dow < 0) | |
444 | dow += 7; | |
445 | ||
446 | /* DOW is the day-of-week of the first day of the month. Get the | |
447 | day-of-month (zero-origin) of the first DOW day of the month. */ | |
448 | d = rule->d - dow; | |
449 | if (d < 0) | |
450 | d += 7; | |
451 | for (i = 1; i < rule->n; ++i) | |
452 | { | |
80fd7387 | 453 | if (d + 7 >= myday[0] - myday[-1]) |
28f540f4 RM |
454 | break; |
455 | d += 7; | |
456 | } | |
457 | ||
458 | /* D is the day-of-month (zero-origin) of the day we want. */ | |
459 | t += d * SECSPERDAY; | |
460 | } | |
461 | break; | |
462 | } | |
463 | ||
464 | /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want. | |
465 | Just add the time of day and local offset from GMT, and we're done. */ | |
466 | ||
68dbb3a6 | 467 | rule->change = t - rule->offset + rule->secs; |
28f540f4 RM |
468 | rule->computed_for = year; |
469 | return 1; | |
470 | } | |
471 | ||
472 | ||
473 | /* Figure out the correct timezone for *TIMER and TM (which must be the same) | |
474 | and set `__tzname', `__timezone', and `__daylight' accordingly. | |
475 | Return nonzero on success, zero on failure. */ | |
476 | int | |
f65fd747 UD |
477 | __tz_compute (timer, tm) |
478 | time_t timer; | |
479 | const struct tm *tm; | |
28f540f4 | 480 | { |
df4ef2ab | 481 | __tzset_internal (0); |
28f540f4 RM |
482 | |
483 | if (! compute_change (&tz_rules[0], 1900 + tm->tm_year) || | |
484 | ! compute_change (&tz_rules[1], 1900 + tm->tm_year)) | |
485 | return 0; | |
486 | ||
487 | __daylight = timer >= tz_rules[0].change && timer < tz_rules[1].change; | |
488 | __timezone = tz_rules[__daylight ? 1 : 0].offset; | |
489 | __tzname[0] = (char *) tz_rules[0].name; | |
490 | __tzname[1] = (char *) tz_rules[1].name; | |
491 | ||
492 | { | |
493 | /* Keep __tzname_cur_max up to date. */ | |
494 | size_t len0 = strlen (__tzname[0]); | |
495 | size_t len1 = strlen (__tzname[1]); | |
496 | if (len0 > __tzname_cur_max) | |
497 | __tzname_cur_max = len0; | |
498 | if (len1 > __tzname_cur_max) | |
499 | __tzname_cur_max = len1; | |
500 | } | |
501 | ||
502 | return 1; | |
503 | } | |
b24be05f RM |
504 | \f |
505 | #include <libc-lock.h> | |
506 | ||
507 | /* This locks all the state variables in tzfile.c and this file. */ | |
508 | __libc_lock_define (, __tzset_lock) | |
509 | ||
510 | /* Reinterpret the TZ environment variable and set `tzname'. */ | |
4311b2a6 | 511 | #undef tzset |
28f540f4 | 512 | |
b24be05f | 513 | void |
10dc2a90 | 514 | __tzset (void) |
b24be05f RM |
515 | { |
516 | __libc_lock_lock (__tzset_lock); | |
68dbb3a6 | 517 | |
df4ef2ab | 518 | __tzset_internal (1); |
68dbb3a6 | 519 | |
860d3729 UD |
520 | if (!__use_tzfile) |
521 | { | |
522 | /* Set `tzname'. */ | |
523 | __tzname[0] = (char *) tz_rules[0].name; | |
524 | __tzname[1] = (char *) tz_rules[1].name; | |
525 | } | |
68dbb3a6 | 526 | |
b24be05f RM |
527 | __libc_lock_unlock (__tzset_lock); |
528 | } | |
10dc2a90 | 529 | weak_alias (__tzset, tzset) |