]>
Commit | Line | Data |
---|---|---|
1cbca0d9 | 1 | /* Copyright (C) 1991, 92, 93, 94, 95, 96 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 RM |
76 | \f |
77 | int __tzset_run = 0; | |
78 | ||
79 | /* Interpret the TZ envariable. */ | |
80 | void | |
f65fd747 | 81 | __tzset () |
28f540f4 | 82 | { |
f65fd747 | 83 | register const char *tz; |
28f540f4 RM |
84 | register size_t l; |
85 | unsigned short int hh, mm, ss; | |
86 | unsigned short int whichrule; | |
87 | ||
88 | /* Free old storage. */ | |
89 | if (tz_rules[0].name != NULL && *tz_rules[0].name != '\0') | |
84724245 | 90 | { |
f65fd747 | 91 | free((void *) tz_rules[0].name); |
84724245 RM |
92 | tz_rules[0].name = NULL; |
93 | } | |
28f540f4 RM |
94 | if (tz_rules[1].name != NULL && *tz_rules[1].name != '\0' && |
95 | tz_rules[1].name != tz_rules[0].name) | |
84724245 | 96 | { |
f65fd747 | 97 | free((void *) tz_rules[1].name); |
84724245 RM |
98 | tz_rules[1].name = NULL; |
99 | } | |
28f540f4 | 100 | |
59dd8641 RM |
101 | /* Examine the TZ environment variable. */ |
102 | tz = getenv ("TZ"); | |
28f540f4 | 103 | |
a3b58440 RM |
104 | /* A leading colon means "implementation defined syntax". |
105 | We ignore the colon and always use the same algorithm: | |
106 | try a data file, and if none exists parse the 1003.1 syntax. */ | |
107 | if (tz && *tz == ':') | |
108 | ++tz; | |
109 | ||
110 | /* Try to read a data file. */ | |
111 | __tzfile_read (tz); | |
112 | if (__use_tzfile) | |
28f540f4 | 113 | { |
a3b58440 RM |
114 | __tzset_run = 1; |
115 | return; | |
28f540f4 RM |
116 | } |
117 | ||
a3b58440 RM |
118 | /* No data file found. Default to UTC if nothing specified. */ |
119 | ||
28f540f4 RM |
120 | if (tz == NULL || *tz == '\0') |
121 | { | |
a3b58440 RM |
122 | static const char UTC[] = "UTC"; |
123 | size_t len = sizeof UTC; | |
124 | tz_rules[0].name = (char *) malloc(len); | |
125 | if (tz_rules[0].name == NULL) | |
126 | return; | |
127 | tz_rules[1].name = (char *) malloc(len); | |
128 | if (tz_rules[1].name == NULL) | |
129 | return; | |
f65fd747 UD |
130 | memcpy ((void *) tz_rules[0].name, UTC, len); |
131 | memcpy ((void *) tz_rules[1].name, UTC, len); | |
a3b58440 RM |
132 | tz_rules[0].type = tz_rules[1].type = J0; |
133 | tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0; | |
134 | tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0; | |
135 | tz_rules[0].secs = tz_rules[1].secs = 0; | |
136 | tz_rules[0].offset = tz_rules[1].offset = 0L; | |
137 | tz_rules[0].change = tz_rules[1].change = (time_t) -1; | |
138 | tz_rules[0].computed_for = tz_rules[1].computed_for = 0; | |
28f540f4 RM |
139 | __tzset_run = 1; |
140 | return; | |
141 | } | |
142 | ||
933e73fa | 143 | /* Clear out old state and reset to unnamed UTC. */ |
28f540f4 RM |
144 | memset (tz_rules, 0, sizeof tz_rules); |
145 | tz_rules[0].name = tz_rules[1].name = (char *) ""; | |
146 | ||
147 | /* Get the standard timezone name. */ | |
148 | tz_rules[0].name = (char *) malloc (strlen (tz) + 1); | |
149 | if (tz_rules[0].name == NULL) | |
150 | /* Don't set __tzset_run so we will try again. */ | |
151 | return; | |
152 | ||
153 | if (sscanf(tz, "%[^0-9,+-]", tz_rules[0].name) != 1 || | |
154 | (l = strlen(tz_rules[0].name)) < 3) | |
155 | { | |
156 | free (tz_rules[0].name); | |
157 | tz_rules[0].name = (char *) ""; | |
158 | return; | |
159 | } | |
160 | ||
161 | { | |
f65fd747 | 162 | char *n = realloc ((void *) tz_rules[0].name, l + 1); |
28f540f4 RM |
163 | if (n != NULL) |
164 | tz_rules[0].name = n; | |
165 | } | |
166 | ||
167 | tz += l; | |
168 | ||
933e73fa | 169 | /* Figure out the standard offset from UTC. */ |
28f540f4 RM |
170 | if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit(*tz))) |
171 | return; | |
172 | ||
173 | if (*tz == '-' || *tz == '+') | |
174 | tz_rules[0].offset = *tz++ == '-' ? 1L : -1L; | |
175 | else | |
176 | tz_rules[0].offset = -1L; | |
177 | switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) | |
178 | { | |
179 | default: | |
180 | return; | |
181 | case 1: | |
182 | mm = 0; | |
183 | case 2: | |
184 | ss = 0; | |
185 | case 3: | |
186 | break; | |
187 | } | |
f65fd747 UD |
188 | tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) + |
189 | (min (hh, 23) * 60 * 60)); | |
28f540f4 RM |
190 | |
191 | for (l = 0; l < 3; ++l) | |
192 | { | |
193 | while (isdigit(*tz)) | |
194 | ++tz; | |
195 | if (l < 2 && *tz == ':') | |
196 | ++tz; | |
197 | } | |
198 | ||
199 | /* Get the DST timezone name (if any). */ | |
200 | if (*tz != '\0') | |
201 | { | |
f65fd747 | 202 | char *n = malloc (strlen (tz) + 1); |
28f540f4 RM |
203 | if (n != NULL) |
204 | { | |
205 | tz_rules[1].name = n; | |
f65fd747 UD |
206 | if (sscanf (tz, "%[^0-9,+-]", tz_rules[1].name) != 1 || |
207 | (l = strlen (tz_rules[1].name)) < 3) | |
28f540f4 RM |
208 | { |
209 | free (n); | |
210 | tz_rules[1].name = (char *) ""; | |
211 | goto done_names; /* Punt on name, set up the offsets. */ | |
212 | } | |
f65fd747 | 213 | n = realloc ((void *) tz_rules[1].name, l + 1); |
28f540f4 RM |
214 | if (n != NULL) |
215 | tz_rules[1].name = n; | |
f65fd747 UD |
216 | |
217 | tz += l; | |
28f540f4 RM |
218 | } |
219 | } | |
220 | ||
28f540f4 RM |
221 | /* Figure out the DST offset from GMT. */ |
222 | if (*tz == '-' || *tz == '+') | |
223 | tz_rules[1].offset = *tz++ == '-' ? 1L : -1L; | |
224 | else | |
225 | tz_rules[1].offset = -1L; | |
226 | ||
227 | switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) | |
228 | { | |
229 | default: | |
230 | /* Default to one hour later than standard time. */ | |
231 | tz_rules[1].offset = tz_rules[0].offset + (60 * 60); | |
232 | break; | |
233 | ||
234 | case 1: | |
235 | mm = 0; | |
236 | case 2: | |
237 | ss = 0; | |
238 | case 3: | |
f65fd747 UD |
239 | tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) + |
240 | (min (hh, 23) * (60 * 60))); | |
28f540f4 RM |
241 | break; |
242 | } | |
243 | for (l = 0; l < 3; ++l) | |
244 | { | |
245 | while (isdigit (*tz)) | |
246 | ++tz; | |
247 | if (l < 2 && *tz == ':') | |
248 | ++tz; | |
249 | } | |
250 | ||
251 | done_names: | |
252 | ||
253 | if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0')) | |
254 | { | |
255 | /* There is no rule. See if there is a default rule file. */ | |
256 | __tzfile_default (tz_rules[0].name, tz_rules[1].name, | |
257 | tz_rules[0].offset, tz_rules[1].offset); | |
258 | if (__use_tzfile) | |
259 | { | |
260 | __tzset_run = 1; | |
261 | return; | |
262 | } | |
263 | } | |
264 | ||
265 | /* Figure out the standard <-> DST rules. */ | |
266 | for (whichrule = 0; whichrule < 2; ++whichrule) | |
267 | { | |
268 | register tz_rule *tzr = &tz_rules[whichrule]; | |
1cbca0d9 | 269 | |
28f540f4 RM |
270 | if (*tz == ',') |
271 | { | |
272 | ++tz; | |
273 | if (*tz == '\0') | |
274 | return; | |
275 | } | |
1cbca0d9 | 276 | |
28f540f4 RM |
277 | /* Get the date of the change. */ |
278 | if (*tz == 'J' || isdigit (*tz)) | |
279 | { | |
280 | char *end; | |
281 | tzr->type = *tz == 'J' ? J1 : J0; | |
282 | if (tzr->type == J1 && !isdigit (*++tz)) | |
283 | return; | |
284 | tzr->d = (unsigned short int) strtoul (tz, &end, 10); | |
285 | if (end == tz || tzr->d > 365) | |
286 | return; | |
287 | else if (tzr->type == J1 && tzr->d == 0) | |
288 | return; | |
289 | tz = end; | |
290 | } | |
291 | else if (*tz == 'M') | |
292 | { | |
293 | int n; | |
294 | tzr->type = M; | |
295 | if (sscanf (tz, "M%hu.%hu.%hu%n", | |
296 | &tzr->m, &tzr->n, &tzr->d, &n) != 3 || | |
297 | tzr->m < 1 || tzr->m > 12 || | |
298 | tzr->n < 1 || tzr->n > 5 || tzr->d > 6) | |
299 | return; | |
300 | tz += n; | |
301 | } | |
302 | else if (*tz == '\0') | |
303 | { | |
304 | /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */ | |
305 | tzr->type = M; | |
306 | if (tzr == &tz_rules[0]) | |
307 | { | |
308 | tzr->m = 4; | |
309 | tzr->n = 1; | |
310 | tzr->d = 0; | |
311 | } | |
312 | else | |
313 | { | |
314 | tzr->m = 10; | |
315 | tzr->n = 5; | |
316 | tzr->d = 0; | |
317 | } | |
318 | } | |
319 | else | |
320 | return; | |
1cbca0d9 | 321 | |
28f540f4 RM |
322 | if (*tz != '\0' && *tz != '/' && *tz != ',') |
323 | return; | |
324 | else if (*tz == '/') | |
325 | { | |
326 | /* Get the time of day of the change. */ | |
327 | ++tz; | |
328 | if (*tz == '\0') | |
329 | return; | |
330 | switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) | |
331 | { | |
332 | default: | |
333 | hh = 2; /* Default to 2:00 AM. */ | |
334 | case 1: | |
335 | mm = 0; | |
336 | case 2: | |
337 | ss = 0; | |
338 | case 3: | |
339 | break; | |
340 | } | |
341 | for (l = 0; l < 3; ++l) | |
342 | { | |
f65fd747 | 343 | while (isdigit (*tz)) |
28f540f4 RM |
344 | ++tz; |
345 | if (l < 2 && *tz == ':') | |
346 | ++tz; | |
347 | } | |
348 | tzr->secs = (hh * 60 * 60) + (mm * 60) + ss; | |
349 | } | |
350 | else | |
351 | /* Default to 2:00 AM. */ | |
352 | tzr->secs = 2 * 60 * 60; | |
353 | ||
354 | tzr->computed_for = -1; | |
355 | } | |
356 | ||
357 | __tzset_run = 1; | |
358 | } | |
359 | \f | |
360 | /* Maximum length of a timezone name. __tz_compute keeps this up to date | |
361 | (never decreasing it) when ! __use_tzfile. | |
362 | tzfile.c keeps it up to date when __use_tzfile. */ | |
f0f1bf85 | 363 | size_t __tzname_cur_max; |
28f540f4 RM |
364 | |
365 | long int | |
f65fd747 | 366 | __tzname_max () |
28f540f4 RM |
367 | { |
368 | if (! __tzset_run) | |
369 | __tzset (); | |
370 | ||
371 | return __tzname_cur_max; | |
372 | } | |
373 | \f | |
374 | /* Figure out the exact time (as a time_t) in YEAR | |
375 | when the change described by RULE will occur and | |
376 | put it in RULE->change, saving YEAR in RULE->computed_for. | |
377 | Return nonzero if successful, zero on failure. */ | |
378 | static int | |
f65fd747 UD |
379 | compute_change (rule, year) |
380 | tz_rule *rule; | |
381 | int year; | |
28f540f4 RM |
382 | { |
383 | register time_t t; | |
384 | int y; | |
385 | ||
386 | if (year != -1 && rule->computed_for == year) | |
387 | /* Operations on times in 1969 will be slower. Oh well. */ | |
388 | return 1; | |
1cbca0d9 | 389 | |
28f540f4 RM |
390 | /* First set T to January 1st, 0:00:00 GMT in YEAR. */ |
391 | t = 0; | |
392 | for (y = 1970; y < year; ++y) | |
393 | t += SECSPERDAY * (__isleap (y) ? 366 : 365); | |
394 | ||
395 | switch (rule->type) | |
396 | { | |
397 | case J1: | |
398 | /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years. | |
399 | In non-leap years, or if the day number is 59 or less, just | |
400 | add SECSPERDAY times the day number-1 to the time of | |
401 | January 1, midnight, to get the day. */ | |
402 | t += (rule->d - 1) * SECSPERDAY; | |
403 | if (rule->d >= 60 && __isleap (year)) | |
404 | t += SECSPERDAY; | |
405 | break; | |
406 | ||
407 | case J0: | |
408 | /* n - Day of year. | |
409 | Just add SECSPERDAY times the day number to the time of Jan 1st. */ | |
410 | t += rule->d * SECSPERDAY; | |
411 | break; | |
412 | ||
413 | case M: | |
414 | /* Mm.n.d - Nth "Dth day" of month M. */ | |
415 | { | |
416 | register int i, d, m1, yy0, yy1, yy2, dow; | |
f65fd747 | 417 | register const unsigned short int *myday = |
80fd7387 | 418 | &__mon_yday[__isleap (year)][rule->m]; |
28f540f4 RM |
419 | |
420 | /* First add SECSPERDAY for each day in months before M. */ | |
80fd7387 | 421 | t += myday[-1] * SECSPERDAY; |
28f540f4 RM |
422 | |
423 | /* Use Zeller's Congruence to get day-of-week of first day of month. */ | |
424 | m1 = (rule->m + 9) % 12 + 1; | |
425 | yy0 = (rule->m <= 2) ? (year - 1) : year; | |
426 | yy1 = yy0 / 100; | |
427 | yy2 = yy0 % 100; | |
428 | dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; | |
429 | if (dow < 0) | |
430 | dow += 7; | |
431 | ||
432 | /* DOW is the day-of-week of the first day of the month. Get the | |
433 | day-of-month (zero-origin) of the first DOW day of the month. */ | |
434 | d = rule->d - dow; | |
435 | if (d < 0) | |
436 | d += 7; | |
437 | for (i = 1; i < rule->n; ++i) | |
438 | { | |
80fd7387 | 439 | if (d + 7 >= myday[0] - myday[-1]) |
28f540f4 RM |
440 | break; |
441 | d += 7; | |
442 | } | |
443 | ||
444 | /* D is the day-of-month (zero-origin) of the day we want. */ | |
445 | t += d * SECSPERDAY; | |
446 | } | |
447 | break; | |
448 | } | |
449 | ||
450 | /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want. | |
451 | Just add the time of day and local offset from GMT, and we're done. */ | |
452 | ||
453 | rule->change = t + rule->offset + rule->secs; | |
454 | rule->computed_for = year; | |
455 | return 1; | |
456 | } | |
457 | ||
458 | ||
459 | /* Figure out the correct timezone for *TIMER and TM (which must be the same) | |
460 | and set `__tzname', `__timezone', and `__daylight' accordingly. | |
461 | Return nonzero on success, zero on failure. */ | |
462 | int | |
f65fd747 UD |
463 | __tz_compute (timer, tm) |
464 | time_t timer; | |
465 | const struct tm *tm; | |
28f540f4 RM |
466 | { |
467 | if (! __tzset_run) | |
468 | __tzset (); | |
469 | ||
470 | if (! compute_change (&tz_rules[0], 1900 + tm->tm_year) || | |
471 | ! compute_change (&tz_rules[1], 1900 + tm->tm_year)) | |
472 | return 0; | |
473 | ||
474 | __daylight = timer >= tz_rules[0].change && timer < tz_rules[1].change; | |
475 | __timezone = tz_rules[__daylight ? 1 : 0].offset; | |
476 | __tzname[0] = (char *) tz_rules[0].name; | |
477 | __tzname[1] = (char *) tz_rules[1].name; | |
478 | ||
479 | { | |
480 | /* Keep __tzname_cur_max up to date. */ | |
481 | size_t len0 = strlen (__tzname[0]); | |
482 | size_t len1 = strlen (__tzname[1]); | |
483 | if (len0 > __tzname_cur_max) | |
484 | __tzname_cur_max = len0; | |
485 | if (len1 > __tzname_cur_max) | |
486 | __tzname_cur_max = len1; | |
487 | } | |
488 | ||
489 | return 1; | |
490 | } | |
b24be05f RM |
491 | \f |
492 | #include <libc-lock.h> | |
493 | ||
494 | /* This locks all the state variables in tzfile.c and this file. */ | |
495 | __libc_lock_define (, __tzset_lock) | |
496 | ||
497 | /* Reinterpret the TZ environment variable and set `tzname'. */ | |
4311b2a6 | 498 | #undef tzset |
28f540f4 | 499 | |
b24be05f | 500 | void |
4311b2a6 RM |
501 | #ifdef weak_function |
502 | weak_function | |
503 | #endif | |
b24be05f RM |
504 | tzset (void) |
505 | { | |
506 | __libc_lock_lock (__tzset_lock); | |
507 | __tzset (); | |
508 | __libc_lock_unlock (__tzset_lock); | |
509 | } |