Add prototype.
[glibc.git] / time / tzset.c
blob1237c9195c1027b5eb368d7bc60ad98e98aad8f4
1 /* Copyright (C) 1991-1999, 2000, 2001 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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. */
19 #include <ctype.h>
20 #include <errno.h>
21 #include <bits/libc-lock.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
29 #define NOID
30 #include <timezone/tzfile.h>
32 char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
33 int __daylight = 0;
34 long int __timezone = 0L;
36 weak_alias (__tzname, tzname)
37 weak_alias (__daylight, daylight)
38 weak_alias (__timezone, timezone)
40 /* This locks all the state variables in tzfile.c and this file. */
41 __libc_lock_define_initialized (static, tzset_lock)
44 #define min(a, b) ((a) < (b) ? (a) : (b))
45 #define max(a, b) ((a) > (b) ? (a) : (b))
46 #define sign(x) ((x) < 0 ? -1 : 1)
49 /* This structure contains all the information about a
50 timezone given in the POSIX standard TZ envariable. */
51 typedef struct
53 const char *name;
55 /* When to change. */
56 enum { J0, J1, M } type; /* Interpretation of: */
57 unsigned short int m, n, d; /* Month, week, day. */
58 unsigned int secs; /* Time of day. */
60 long int offset; /* Seconds east of GMT (west if < 0). */
62 /* We cache the computed time of change for a
63 given year so we don't have to recompute it. */
64 time_t change; /* When to change to this zone. */
65 int computed_for; /* Year above is computed for. */
66 } tz_rule;
68 /* tz_rules[0] is standard, tz_rules[1] is daylight. */
69 static tz_rule tz_rules[2];
72 static int compute_change __P ((tz_rule *rule, int year)) internal_function;
73 static int tz_compute __P ((const struct tm *tm))
74 internal_function;
75 static void tzset_internal __P ((int always)) internal_function;
77 /* List of buffers containing time zone strings. */
78 struct tzstring_l
80 struct tzstring_l *next;
81 size_t len; /* strlen(data) - doesn't count terminating NUL! */
82 char data[0];
85 struct tzstring_l *tzstring_list;
87 /* Allocate a permanent home for S. It will never be moved or deallocated,
88 but may share space with other strings.
89 Don't modify the returned string. */
90 char *
91 __tzstring (const char *s)
93 char *p;
94 struct tzstring_l *t, *u, *new;
95 size_t len = strlen(s);
97 /* Walk the list and look for a match. If this string is the same
98 as the end of an already-allocated string, it can share space. */
99 for (u = t = tzstring_list; t; u = t, t = t->next)
100 if (len <= t->len)
102 p = &t->data[t->len - len];
103 if (strcmp (s, p) == 0)
104 return p;
107 /* Not found; allocate a new buffer. */
108 new = malloc (sizeof (struct tzstring_l) + len + 1);
109 if (!new)
110 return NULL;
112 new->next = NULL;
113 new->len = len;
114 strcpy (new->data, s);
116 if (u)
117 u->next = new;
118 else
119 tzstring_list = new;
121 return new->data;
124 static char *old_tz;
126 /* Interpret the TZ envariable. */
127 static void
128 internal_function
129 tzset_internal (always)
130 int always;
132 static int is_initialized;
133 register const char *tz;
134 register size_t l;
135 char *tzbuf;
136 unsigned short int hh, mm, ss;
137 unsigned short int whichrule;
139 if (is_initialized && !always)
140 return;
141 is_initialized = 1;
143 /* Examine the TZ environment variable. */
144 tz = getenv ("TZ");
145 if (tz == NULL)
146 /* No user specification; use the site-wide default. */
147 tz = TZDEFAULT;
148 else if (*tz == '\0')
149 /* User specified the empty string; use UTC explicitly. */
150 tz = "Universal";
152 /* A leading colon means "implementation defined syntax".
153 We ignore the colon and always use the same algorithm:
154 try a data file, and if none exists parse the 1003.1 syntax. */
155 if (tz && *tz == ':')
156 ++tz;
158 /* Check whether the value changes since the last run. */
159 if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
160 /* No change, simply return. */
161 return;
163 tz_rules[0].name = NULL;
164 tz_rules[1].name = NULL;
166 /* Save the value of `tz'. */
167 if (old_tz != NULL)
168 free (old_tz);
169 old_tz = tz ? __strdup (tz) : NULL;
171 /* Try to read a data file. */
172 __tzfile_read (tz, 0, NULL);
173 if (__use_tzfile)
174 return;
176 /* No data file found. Default to UTC if nothing specified. */
178 if (tz == NULL || *tz == '\0'
179 || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))
181 tz_rules[0].name = tz_rules[1].name = "UTC";
182 tz_rules[0].type = tz_rules[1].type = J0;
183 tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
184 tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
185 tz_rules[0].secs = tz_rules[1].secs = 0;
186 tz_rules[0].offset = tz_rules[1].offset = 0L;
187 tz_rules[0].change = tz_rules[1].change = (time_t) -1;
188 tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
189 return;
192 /* Clear out old state and reset to unnamed UTC. */
193 memset (tz_rules, 0, sizeof tz_rules);
194 tz_rules[0].name = tz_rules[1].name = "";
196 /* Get the standard timezone name. */
197 tzbuf = strdupa (tz);
199 if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 ||
200 (l = strlen (tzbuf)) < 3)
201 return;
203 tz_rules[0].name = __tzstring (tzbuf);
205 tz += l;
207 /* Figure out the standard offset from UTC. */
208 if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
209 return;
211 if (*tz == '-' || *tz == '+')
212 tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
213 else
214 tz_rules[0].offset = -1L;
215 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
217 default:
218 return;
219 case 1:
220 mm = 0;
221 case 2:
222 ss = 0;
223 case 3:
224 break;
226 tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
227 (min (hh, 23) * 60 * 60));
229 for (l = 0; l < 3; ++l)
231 while (isdigit(*tz))
232 ++tz;
233 if (l < 2 && *tz == ':')
234 ++tz;
237 /* Get the DST timezone name (if any). */
238 if (*tz != '\0')
240 char *n = tzbuf + strlen (tzbuf) + 1;
241 if (sscanf (tz, "%[^0-9,+-]", n) != 1 ||
242 (l = strlen (n)) < 3)
243 goto done_names; /* Punt on name, set up the offsets. */
245 tz_rules[1].name = __tzstring (n);
247 tz += l;
249 /* Figure out the DST offset from GMT. */
250 if (*tz == '-' || *tz == '+')
251 tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
252 else
253 tz_rules[1].offset = -1L;
255 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
257 default:
258 /* Default to one hour later than standard time. */
259 tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
260 break;
262 case 1:
263 mm = 0;
264 case 2:
265 ss = 0;
266 case 3:
267 tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
268 (min (hh, 23) * (60 * 60)));
269 break;
271 for (l = 0; l < 3; ++l)
273 while (isdigit (*tz))
274 ++tz;
275 if (l < 2 && *tz == ':')
276 ++tz;
278 if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
280 /* There is no rule. See if there is a default rule file. */
281 __tzfile_default (tz_rules[0].name, tz_rules[1].name,
282 tz_rules[0].offset, tz_rules[1].offset);
283 if (__use_tzfile)
285 free (old_tz);
286 old_tz = NULL;
287 return;
291 else
293 /* There is no DST. */
294 tz_rules[1].name = tz_rules[0].name;
295 tz_rules[1].offset = tz_rules[0].offset;
296 goto out;
299 done_names:
300 /* Figure out the standard <-> DST rules. */
301 for (whichrule = 0; whichrule < 2; ++whichrule)
303 register tz_rule *tzr = &tz_rules[whichrule];
305 /* Ignore comma to support string following the incorrect
306 specification in early POSIX.1 printings. */
307 tz += *tz == ',';
309 /* Get the date of the change. */
310 if (*tz == 'J' || isdigit (*tz))
312 char *end;
313 tzr->type = *tz == 'J' ? J1 : J0;
314 if (tzr->type == J1 && !isdigit (*++tz))
315 goto out;
316 tzr->d = (unsigned short int) strtoul (tz, &end, 10);
317 if (end == tz || tzr->d > 365)
318 goto out;
319 else if (tzr->type == J1 && tzr->d == 0)
320 goto out;
321 tz = end;
323 else if (*tz == 'M')
325 int n;
326 tzr->type = M;
327 if (sscanf (tz, "M%hu.%hu.%hu%n",
328 &tzr->m, &tzr->n, &tzr->d, &n) != 3 ||
329 tzr->m < 1 || tzr->m > 12 ||
330 tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
331 goto out;
332 tz += n;
334 else if (*tz == '\0')
336 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
337 tzr->type = M;
338 if (tzr == &tz_rules[0])
340 tzr->m = 4;
341 tzr->n = 1;
342 tzr->d = 0;
344 else
346 tzr->m = 10;
347 tzr->n = 5;
348 tzr->d = 0;
351 else
352 goto out;
354 if (*tz != '\0' && *tz != '/' && *tz != ',')
355 goto out;
356 else if (*tz == '/')
358 /* Get the time of day of the change. */
359 ++tz;
360 if (*tz == '\0')
361 goto out;
362 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
364 default:
365 hh = 2; /* Default to 2:00 AM. */
366 case 1:
367 mm = 0;
368 case 2:
369 ss = 0;
370 case 3:
371 break;
373 for (l = 0; l < 3; ++l)
375 while (isdigit (*tz))
376 ++tz;
377 if (l < 2 && *tz == ':')
378 ++tz;
380 tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
382 else
383 /* Default to 2:00 AM. */
384 tzr->secs = 2 * 60 * 60;
386 tzr->computed_for = -1;
389 out:
390 /* We know the offset now, set `__timezone'. */
391 __timezone = -tz_rules[0].offset;
394 /* Maximum length of a timezone name. __tz_compute keeps this up to date
395 (never decreasing it) when ! __use_tzfile.
396 tzfile.c keeps it up to date when __use_tzfile. */
397 size_t __tzname_cur_max;
399 long int
400 __tzname_max ()
402 __libc_lock_lock (tzset_lock);
404 tzset_internal (0);
406 __libc_lock_unlock (tzset_lock);
408 return __tzname_cur_max;
411 /* Figure out the exact time (as a time_t) in YEAR
412 when the change described by RULE will occur and
413 put it in RULE->change, saving YEAR in RULE->computed_for.
414 Return nonzero if successful, zero on failure. */
415 static int
416 internal_function
417 compute_change (rule, year)
418 tz_rule *rule;
419 int year;
421 register time_t t;
423 if (year != -1 && rule->computed_for == year)
424 /* Operations on times in 2 BC will be slower. Oh well. */
425 return 1;
427 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
428 if (year > 1970)
429 t = ((year - 1970) * 365
430 + /* Compute the number of leapdays between 1970 and YEAR
431 (exclusive). There is a leapday every 4th year ... */
432 + ((year - 1) / 4 - 1970 / 4)
433 /* ... except every 100th year ... */
434 - ((year - 1) / 100 - 1970 / 100)
435 /* ... but still every 400th year. */
436 + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY;
437 else
438 t = 0;
440 switch (rule->type)
442 case J1:
443 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
444 In non-leap years, or if the day number is 59 or less, just
445 add SECSPERDAY times the day number-1 to the time of
446 January 1, midnight, to get the day. */
447 t += (rule->d - 1) * SECSPERDAY;
448 if (rule->d >= 60 && __isleap (year))
449 t += SECSPERDAY;
450 break;
452 case J0:
453 /* n - Day of year.
454 Just add SECSPERDAY times the day number to the time of Jan 1st. */
455 t += rule->d * SECSPERDAY;
456 break;
458 case M:
459 /* Mm.n.d - Nth "Dth day" of month M. */
461 unsigned int i;
462 int d, m1, yy0, yy1, yy2, dow;
463 const unsigned short int *myday =
464 &__mon_yday[__isleap (year)][rule->m];
466 /* First add SECSPERDAY for each day in months before M. */
467 t += myday[-1] * SECSPERDAY;
469 /* Use Zeller's Congruence to get day-of-week of first day of month. */
470 m1 = (rule->m + 9) % 12 + 1;
471 yy0 = (rule->m <= 2) ? (year - 1) : year;
472 yy1 = yy0 / 100;
473 yy2 = yy0 % 100;
474 dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
475 if (dow < 0)
476 dow += 7;
478 /* DOW is the day-of-week of the first day of the month. Get the
479 day-of-month (zero-origin) of the first DOW day of the month. */
480 d = rule->d - dow;
481 if (d < 0)
482 d += 7;
483 for (i = 1; i < rule->n; ++i)
485 if (d + 7 >= (int) myday[0] - myday[-1])
486 break;
487 d += 7;
490 /* D is the day-of-month (zero-origin) of the day we want. */
491 t += d * SECSPERDAY;
493 break;
496 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
497 Just add the time of day and local offset from GMT, and we're done. */
499 rule->change = t - rule->offset + rule->secs;
500 rule->computed_for = year;
501 return 1;
505 /* Figure out the correct timezone for TM and set `__tzname',
506 `__timezone', and `__daylight' accordingly. Return nonzero on
507 success, zero on failure. */
508 static int
509 internal_function
510 tz_compute (tm)
511 const struct tm *tm;
513 if (! compute_change (&tz_rules[0], 1900 + tm->tm_year)
514 || ! compute_change (&tz_rules[1], 1900 + tm->tm_year))
515 return 0;
516 /* We have to distinguish between northern and southern hemisphere.
517 For the latter the daylight saving time ends in the next year.
518 It is easier to detect this after first computing the time for the
519 wrong year since now we simply can compare the times to switch. */
520 if (tz_rules[0].change > tz_rules[1].change
521 && ! compute_change (&tz_rules[1], 1900 + tm->tm_year + 1))
522 return 0;
524 __daylight = tz_rules[0].offset != tz_rules[1].offset;
525 __timezone = -tz_rules[0].offset;
526 __tzname[0] = (char *) tz_rules[0].name;
527 __tzname[1] = (char *) tz_rules[1].name;
530 /* Keep __tzname_cur_max up to date. */
531 size_t len0 = strlen (__tzname[0]);
532 size_t len1 = strlen (__tzname[1]);
533 if (len0 > __tzname_cur_max)
534 __tzname_cur_max = len0;
535 if (len1 > __tzname_cur_max)
536 __tzname_cur_max = len1;
539 return 1;
542 /* Reinterpret the TZ environment variable and set `tzname'. */
543 #undef tzset
545 void
546 __tzset (void)
548 __libc_lock_lock (tzset_lock);
550 tzset_internal (1);
552 if (!__use_tzfile)
554 /* Set `tzname'. */
555 __tzname[0] = (char *) tz_rules[0].name;
556 __tzname[1] = (char *) tz_rules[1].name;
559 __libc_lock_unlock (tzset_lock);
561 weak_alias (__tzset, tzset)
563 /* Return the `struct tm' representation of *TIMER in the local timezone.
564 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
565 struct tm *
566 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
568 long int leap_correction;
569 int leap_extra_secs;
571 if (timer == NULL)
573 __set_errno (EINVAL);
574 return NULL;
577 __libc_lock_lock (tzset_lock);
579 /* Update internal database according to current TZ setting.
580 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
581 This is a good idea since this allows at least a bit more parallelism.
582 By analogy we apply the same rule to gmtime_r. */
583 tzset_internal (tp == &_tmbuf);
585 if (__use_tzfile)
587 if (! __tzfile_compute (*timer, use_localtime,
588 &leap_correction, &leap_extra_secs, tp))
589 tp = NULL;
591 else
593 if (! (__offtime (timer, 0, tp) && tz_compute (tp)))
594 tp = NULL;
595 leap_correction = 0L;
596 leap_extra_secs = 0;
599 if (tp)
601 if (use_localtime)
603 if (!__use_tzfile)
605 int isdst = (*timer >= tz_rules[0].change
606 && *timer < tz_rules[1].change);
607 tp->tm_isdst = isdst;
608 tp->tm_zone = __tzname[isdst];
609 tp->tm_gmtoff = tz_rules[isdst].offset;
612 else
614 tp->tm_isdst = 0;
615 tp->tm_zone = "GMT";
616 tp->tm_gmtoff = 0L;
619 if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
620 tp->tm_sec += leap_extra_secs;
621 else
622 tp = NULL;
625 __libc_lock_unlock (tzset_lock);
627 return tp;
631 static void
632 free_mem (void)
634 while (tzstring_list != NULL)
636 struct tzstring_l *old = tzstring_list;
638 tzstring_list = tzstring_list->next;
639 free (old);
641 free (old_tz);
642 old_tz = NULL;
644 text_set_element (__libc_subfreeres, free_mem);