1 /* Copyright (C) 1991,92,93,94,95,96,97,98 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. */
21 #include <bits/libc-lock.h>
28 /* Defined in mktime.c. */
29 extern const unsigned short int __mon_yday[2][13];
31 /* Defined in localtime.c. */
32 extern struct tm _tmbuf;
35 #include <timezone/tzfile.h>
37 extern int __use_tzfile;
38 extern void __tzfile_read __P ((const char *file));
39 extern int __tzfile_compute __P ((time_t timer, int use_localtime,
40 long int *leap_correct, int *leap_hit,
42 extern void __tzfile_default __P ((const char *std, const char *dst,
43 long int stdoff, long int dstoff));
44 extern char *__tzstring __P ((const char *string));
46 char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
48 long int __timezone = 0L;
50 weak_alias (__tzname, tzname)
51 weak_alias (__daylight, daylight)
52 weak_alias (__timezone, timezone)
54 /* This locks all the state variables in tzfile.c and this file. */
55 __libc_lock_define (static, tzset_lock)
58 #define min(a, b) ((a) < (b) ? (a) : (b))
59 #define max(a, b) ((a) > (b) ? (a) : (b))
60 #define sign(x) ((x) < 0 ? -1 : 1)
63 /* This structure contains all the information about a
64 timezone given in the POSIX standard TZ envariable. */
70 enum { J0, J1, M } type; /* Interpretation of: */
71 unsigned short int m, n, d; /* Month, week, day. */
72 unsigned int secs; /* Time of day. */
74 long int offset; /* Seconds east of GMT (west if < 0). */
76 /* We cache the computed time of change for a
77 given year so we don't have to recompute it. */
78 time_t change; /* When to change to this zone. */
79 int computed_for; /* Year above is computed for. */
82 /* tz_rules[0] is standard, tz_rules[1] is daylight. */
83 static tz_rule tz_rules[2];
86 static int compute_change __P ((tz_rule *rule, int year)) internal_function;
87 static int tz_compute __P ((time_t timer, const struct tm *tm))
89 static void tzset_internal __P ((int always)) internal_function;
91 /* List of buffers containing time zone strings. */
94 struct tzstring_l *next;
95 size_t len; /* strlen(data) - doesn't count terminating NUL! */
99 struct tzstring_l *tzstring_list;
101 /* Allocate a permanent home for S. It will never be moved or deallocated,
102 but may share space with other strings.
103 Don't modify the returned string. */
105 __tzstring (const char *s)
108 struct tzstring_l *t, *u, *new;
109 size_t len = strlen(s);
111 /* Walk the list and look for a match. If this string is the same
112 as the end of an already-allocated string, it can share space. */
113 for (u = t = tzstring_list; t; u = t, t = t->next)
116 p = &t->data[t->len - len];
117 if (strcmp (s, p) == 0)
121 /* Not found; allocate a new buffer. */
122 new = malloc (sizeof (struct tzstring_l) + len + 1);
128 strcpy (new->data, s);
138 static char *old_tz = NULL;
140 /* Interpret the TZ envariable. */
143 tzset_internal (always)
146 static int is_initialized = 0;
147 register const char *tz;
150 unsigned short int hh, mm, ss;
151 unsigned short int whichrule;
153 if (is_initialized && !always)
157 /* Examine the TZ environment variable. */
160 /* No user specification; use the site-wide default. */
162 else if (*tz == '\0')
163 /* User specified the empty string; use UTC explicitly. */
166 /* A leading colon means "implementation defined syntax".
167 We ignore the colon and always use the same algorithm:
168 try a data file, and if none exists parse the 1003.1 syntax. */
169 if (tz && *tz == ':')
172 /* Check whether the value changes since the last run. */
173 if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
174 /* No change, simply return. */
177 tz_rules[0].name = NULL;
178 tz_rules[1].name = NULL;
180 /* Save the value of `tz'. */
183 old_tz = tz ? __strdup (tz) : NULL;
185 /* Try to read a data file. */
190 /* No data file found. Default to UTC if nothing specified. */
192 if (tz == NULL || *tz == '\0')
194 tz_rules[0].name = tz_rules[1].name = "UTC";
195 tz_rules[0].type = tz_rules[1].type = J0;
196 tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
197 tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
198 tz_rules[0].secs = tz_rules[1].secs = 0;
199 tz_rules[0].offset = tz_rules[1].offset = 0L;
200 tz_rules[0].change = tz_rules[1].change = (time_t) -1;
201 tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
205 /* Clear out old state and reset to unnamed UTC. */
206 memset (tz_rules, 0, sizeof tz_rules);
207 tz_rules[0].name = tz_rules[1].name = "";
209 /* Get the standard timezone name. */
210 tzbuf = strdupa (tz);
212 if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 ||
213 (l = strlen (tzbuf)) < 3)
216 tz_rules[0].name = __tzstring (tzbuf);
220 /* Figure out the standard offset from UTC. */
221 if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
224 if (*tz == '-' || *tz == '+')
225 tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
227 tz_rules[0].offset = -1L;
228 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
239 tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
240 (min (hh, 23) * 60 * 60));
242 for (l = 0; l < 3; ++l)
246 if (l < 2 && *tz == ':')
250 /* Get the DST timezone name (if any). */
253 char *n = tzbuf + strlen (tzbuf) + 1;
254 if (sscanf (tz, "%[^0-9,+-]", n) != 1 ||
255 (l = strlen (n)) < 3)
256 goto done_names; /* Punt on name, set up the offsets. */
258 tz_rules[1].name = __tzstring (n);
262 /* Figure out the DST offset from GMT. */
263 if (*tz == '-' || *tz == '+')
264 tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
266 tz_rules[1].offset = -1L;
268 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
271 /* Default to one hour later than standard time. */
272 tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
280 tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
281 (min (hh, 23) * (60 * 60)));
284 for (l = 0; l < 3; ++l)
286 while (isdigit (*tz))
288 if (l < 2 && *tz == ':')
291 if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
293 /* There is no rule. See if there is a default rule file. */
294 __tzfile_default (tz_rules[0].name, tz_rules[1].name,
295 tz_rules[0].offset, tz_rules[1].offset);
306 /* There is no DST. */
307 tz_rules[1].name = tz_rules[0].name;
308 tz_rules[1].offset = tz_rules[0].offset;
313 /* Figure out the standard <-> DST rules. */
314 for (whichrule = 0; whichrule < 2; ++whichrule)
316 register tz_rule *tzr = &tz_rules[whichrule];
318 /* Ignore comma to support string following the incorrect
319 specification in early POSIX.1 printings. */
322 /* Get the date of the change. */
323 if (*tz == 'J' || isdigit (*tz))
326 tzr->type = *tz == 'J' ? J1 : J0;
327 if (tzr->type == J1 && !isdigit (*++tz))
329 tzr->d = (unsigned short int) strtoul (tz, &end, 10);
330 if (end == tz || tzr->d > 365)
332 else if (tzr->type == J1 && tzr->d == 0)
340 if (sscanf (tz, "M%hu.%hu.%hu%n",
341 &tzr->m, &tzr->n, &tzr->d, &n) != 3 ||
342 tzr->m < 1 || tzr->m > 12 ||
343 tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
347 else if (*tz == '\0')
349 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
351 if (tzr == &tz_rules[0])
367 if (*tz != '\0' && *tz != '/' && *tz != ',')
371 /* Get the time of day of the change. */
375 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
378 hh = 2; /* Default to 2:00 AM. */
386 for (l = 0; l < 3; ++l)
388 while (isdigit (*tz))
390 if (l < 2 && *tz == ':')
393 tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
396 /* Default to 2:00 AM. */
397 tzr->secs = 2 * 60 * 60;
399 tzr->computed_for = -1;
403 /* We know the offset now, set `__timezone'. */
404 __timezone = -tz_rules[0].offset;
407 /* Maximum length of a timezone name. __tz_compute keeps this up to date
408 (never decreasing it) when ! __use_tzfile.
409 tzfile.c keeps it up to date when __use_tzfile. */
410 size_t __tzname_cur_max;
415 __libc_lock_lock (tzset_lock);
419 __libc_lock_unlock (tzset_lock);
421 return __tzname_cur_max;
424 /* Figure out the exact time (as a time_t) in YEAR
425 when the change described by RULE will occur and
426 put it in RULE->change, saving YEAR in RULE->computed_for.
427 Return nonzero if successful, zero on failure. */
430 compute_change (rule, year)
437 if (year != -1 && rule->computed_for == year)
438 /* Operations on times in 1969 will be slower. Oh well. */
441 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
443 for (y = 1970; y < year; ++y)
444 t += SECSPERDAY * (__isleap (y) ? 366 : 365);
449 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
450 In non-leap years, or if the day number is 59 or less, just
451 add SECSPERDAY times the day number-1 to the time of
452 January 1, midnight, to get the day. */
453 t += (rule->d - 1) * SECSPERDAY;
454 if (rule->d >= 60 && __isleap (year))
460 Just add SECSPERDAY times the day number to the time of Jan 1st. */
461 t += rule->d * SECSPERDAY;
465 /* Mm.n.d - Nth "Dth day" of month M. */
468 int d, m1, yy0, yy1, yy2, dow;
469 const unsigned short int *myday =
470 &__mon_yday[__isleap (year)][rule->m];
472 /* First add SECSPERDAY for each day in months before M. */
473 t += myday[-1] * SECSPERDAY;
475 /* Use Zeller's Congruence to get day-of-week of first day of month. */
476 m1 = (rule->m + 9) % 12 + 1;
477 yy0 = (rule->m <= 2) ? (year - 1) : year;
480 dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
484 /* DOW is the day-of-week of the first day of the month. Get the
485 day-of-month (zero-origin) of the first DOW day of the month. */
489 for (i = 1; i < rule->n; ++i)
491 if (d + 7 >= (int) myday[0] - myday[-1])
496 /* D is the day-of-month (zero-origin) of the day we want. */
502 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
503 Just add the time of day and local offset from GMT, and we're done. */
505 rule->change = t - rule->offset + rule->secs;
506 rule->computed_for = year;
511 /* Figure out the correct timezone for *TIMER and TM (which must be the same)
512 and set `__tzname', `__timezone', and `__daylight' accordingly.
513 Return nonzero on success, zero on failure. */
516 tz_compute (timer, tm)
520 if (! compute_change (&tz_rules[0], 1900 + tm->tm_year)
521 || ! compute_change (&tz_rules[1], 1900 + tm->tm_year))
523 /* We have to distinguish between northern and southern hemisphere.
524 For the later the daylight saving time ends in the next year.
525 It is easier to detect this after first computing the time for the
526 wrong year since now we simply can compare the times to switch. */
527 if (tz_rules[0].change > tz_rules[1].change
528 && ! compute_change (&tz_rules[1], 1900 + tm->tm_year + 1))
531 __daylight = tz_rules[0].offset != tz_rules[1].offset;
532 __timezone = -tz_rules[0].offset;
533 __tzname[0] = (char *) tz_rules[0].name;
534 __tzname[1] = (char *) tz_rules[1].name;
537 /* Keep __tzname_cur_max up to date. */
538 size_t len0 = strlen (__tzname[0]);
539 size_t len1 = strlen (__tzname[1]);
540 if (len0 > __tzname_cur_max)
541 __tzname_cur_max = len0;
542 if (len1 > __tzname_cur_max)
543 __tzname_cur_max = len1;
549 /* Reinterpret the TZ environment variable and set `tzname'. */
555 __libc_lock_lock (tzset_lock);
562 __tzname[0] = (char *) tz_rules[0].name;
563 __tzname[1] = (char *) tz_rules[1].name;
566 __libc_lock_unlock (tzset_lock);
568 weak_alias (__tzset, tzset)
570 /* Return the `struct tm' representation of *TIMER in the local timezone.
571 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
573 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
575 long int leap_correction;
580 __set_errno (EINVAL);
584 __libc_lock_lock (tzset_lock);
586 /* Update internal database according to current TZ setting.
587 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
588 This is a good idea since this allows at least a bit more parallelism.
589 By analogy we apply the same rule to gmtime_r. */
590 tzset_internal (tp == &_tmbuf);
594 if (! __tzfile_compute (*timer, use_localtime,
595 &leap_correction, &leap_extra_secs, tp))
600 if (! (__offtime (timer, 0, tp) && tz_compute (*timer, tp)))
602 leap_correction = 0L;
612 int isdst = (*timer >= tz_rules[0].change
613 && *timer < tz_rules[1].change);
614 tp->tm_isdst = isdst;
615 tp->tm_zone = __tzname[isdst];
616 tp->tm_gmtoff = tz_rules[isdst].offset;
626 if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
627 tp->tm_sec += leap_extra_secs;
632 __libc_lock_unlock (tzset_lock);