file tst-aiod4.c was initially added on branch fedora-branch.
[glibc/history.git] / time / tzset.c
blob0479abb38a4ffc7e9160193a9c32b675227264b2
1 /* Copyright (C) 1991-2002,2003,2004 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 Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 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 void compute_change (tz_rule *rule, int year) __THROW internal_function;
73 static void tz_compute (const struct tm *tm) __THROW internal_function;
74 static void tzset_internal (int always, int explicit)
75 __THROW 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 static 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 /* Maximum length of a timezone name. tzset_internal keeps this up to date
125 (never decreasing it) when ! __use_tzfile.
126 tzfile.c keeps it up to date when __use_tzfile. */
127 size_t __tzname_cur_max;
129 long int
130 __tzname_max ()
132 __libc_lock_lock (tzset_lock);
134 tzset_internal (0, 0);
136 __libc_lock_unlock (tzset_lock);
138 return __tzname_cur_max;
141 static char *old_tz;
143 /* Interpret the TZ envariable. */
144 static void
145 internal_function
146 tzset_internal (always, explicit)
147 int always;
148 int explicit;
150 static int is_initialized;
151 register const char *tz;
152 register size_t l;
153 char *tzbuf;
154 unsigned short int hh, mm, ss;
155 unsigned short int whichrule;
157 if (is_initialized && !always)
158 return;
159 is_initialized = 1;
161 /* Examine the TZ environment variable. */
162 tz = getenv ("TZ");
163 if (tz == NULL && !explicit)
164 /* Use the site-wide default. This is a file name which means we
165 would not see changes to the file if we compare only the file
166 name for change. We want to notice file changes if tzset() has
167 been called explicitly. Leave TZ as NULL in this case. */
168 tz = TZDEFAULT;
169 if (tz && *tz == '\0')
170 /* User specified the empty string; use UTC explicitly. */
171 tz = "Universal";
173 /* A leading colon means "implementation defined syntax".
174 We ignore the colon and always use the same algorithm:
175 try a data file, and if none exists parse the 1003.1 syntax. */
176 if (tz && *tz == ':')
177 ++tz;
179 /* Check whether the value changes since the last run. */
180 if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
181 /* No change, simply return. */
182 return;
184 if (tz == NULL)
185 /* No user specification; use the site-wide default. */
186 tz = TZDEFAULT;
188 tz_rules[0].name = NULL;
189 tz_rules[1].name = NULL;
191 /* Save the value of `tz'. */
192 if (old_tz != NULL)
193 free (old_tz);
194 old_tz = tz ? __strdup (tz) : NULL;
196 /* Try to read a data file. */
197 __tzfile_read (tz, 0, NULL);
198 if (__use_tzfile)
199 return;
201 /* No data file found. Default to UTC if nothing specified. */
203 if (tz == NULL || *tz == '\0'
204 || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))
206 tz_rules[0].name = tz_rules[1].name = "UTC";
207 tz_rules[0].type = tz_rules[1].type = J0;
208 tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
209 tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
210 tz_rules[0].secs = tz_rules[1].secs = 0;
211 tz_rules[0].offset = tz_rules[1].offset = 0L;
212 tz_rules[0].change = tz_rules[1].change = (time_t) -1;
213 tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
214 goto out;
217 /* Clear out old state and reset to unnamed UTC. */
218 memset (tz_rules, 0, sizeof tz_rules);
219 tz_rules[0].name = tz_rules[1].name = "";
221 /* Get the standard timezone name. */
222 tzbuf = strdupa (tz);
224 if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 ||
225 (l = strlen (tzbuf)) < 3)
226 goto out;
228 tz_rules[0].name = __tzstring (tzbuf);
230 tz += l;
232 /* Figure out the standard offset from UTC. */
233 if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
234 goto out;
236 if (*tz == '-' || *tz == '+')
237 tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
238 else
239 tz_rules[0].offset = -1L;
240 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
242 default:
243 tz_rules[0].offset = 0;
244 goto out;
245 case 1:
246 mm = 0;
247 case 2:
248 ss = 0;
249 case 3:
250 break;
252 tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
253 (min (hh, 24) * 60 * 60));
255 for (l = 0; l < 3; ++l)
257 while (isdigit(*tz))
258 ++tz;
259 if (l < 2 && *tz == ':')
260 ++tz;
263 /* Get the DST timezone name (if any). */
264 if (*tz != '\0')
266 char *n = tzbuf + strlen (tzbuf) + 1;
267 if (sscanf (tz, "%[^0-9,+-]", n) != 1 ||
268 (l = strlen (n)) < 3)
269 goto done_names; /* Punt on name, set up the offsets. */
271 tz_rules[1].name = __tzstring (n);
273 tz += l;
275 /* Figure out the DST offset from GMT. */
276 if (*tz == '-' || *tz == '+')
277 tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
278 else
279 tz_rules[1].offset = -1L;
281 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
283 default:
284 /* Default to one hour later than standard time. */
285 tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
286 break;
288 case 1:
289 mm = 0;
290 case 2:
291 ss = 0;
292 case 3:
293 tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
294 (min (hh, 23) * (60 * 60)));
295 break;
297 for (l = 0; l < 3; ++l)
299 while (isdigit (*tz))
300 ++tz;
301 if (l < 2 && *tz == ':')
302 ++tz;
304 if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
306 /* There is no rule. See if there is a default rule file. */
307 __tzfile_default (tz_rules[0].name, tz_rules[1].name,
308 tz_rules[0].offset, tz_rules[1].offset);
309 if (__use_tzfile)
311 free (old_tz);
312 old_tz = NULL;
313 return;
317 else
319 /* There is no DST. */
320 tz_rules[1].name = tz_rules[0].name;
321 tz_rules[1].offset = tz_rules[0].offset;
322 goto out;
325 done_names:
326 /* Figure out the standard <-> DST rules. */
327 for (whichrule = 0; whichrule < 2; ++whichrule)
329 register tz_rule *tzr = &tz_rules[whichrule];
331 /* Ignore comma to support string following the incorrect
332 specification in early POSIX.1 printings. */
333 tz += *tz == ',';
335 /* Get the date of the change. */
336 if (*tz == 'J' || isdigit (*tz))
338 char *end;
339 tzr->type = *tz == 'J' ? J1 : J0;
340 if (tzr->type == J1 && !isdigit (*++tz))
341 goto out;
342 tzr->d = (unsigned short int) strtoul (tz, &end, 10);
343 if (end == tz || tzr->d > 365)
344 goto out;
345 else if (tzr->type == J1 && tzr->d == 0)
346 goto out;
347 tz = end;
349 else if (*tz == 'M')
351 int n;
352 tzr->type = M;
353 if (sscanf (tz, "M%hu.%hu.%hu%n",
354 &tzr->m, &tzr->n, &tzr->d, &n) != 3 ||
355 tzr->m < 1 || tzr->m > 12 ||
356 tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
357 goto out;
358 tz += n;
360 else if (*tz == '\0')
362 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
363 tzr->type = M;
364 if (tzr == &tz_rules[0])
366 tzr->m = 4;
367 tzr->n = 1;
368 tzr->d = 0;
370 else
372 tzr->m = 10;
373 tzr->n = 5;
374 tzr->d = 0;
377 else
378 goto out;
380 if (*tz != '\0' && *tz != '/' && *tz != ',')
381 goto out;
382 else if (*tz == '/')
384 /* Get the time of day of the change. */
385 ++tz;
386 if (*tz == '\0')
387 goto out;
388 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
390 default:
391 hh = 2; /* Default to 2:00 AM. */
392 case 1:
393 mm = 0;
394 case 2:
395 ss = 0;
396 case 3:
397 break;
399 for (l = 0; l < 3; ++l)
401 while (isdigit (*tz))
402 ++tz;
403 if (l < 2 && *tz == ':')
404 ++tz;
406 tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
408 else
409 /* Default to 2:00 AM. */
410 tzr->secs = 2 * 60 * 60;
412 tzr->computed_for = -1;
415 out:
416 __daylight = tz_rules[0].offset != tz_rules[1].offset;
417 __timezone = -tz_rules[0].offset;
418 __tzname[0] = (char *) tz_rules[0].name;
419 __tzname[1] = (char *) tz_rules[1].name;
422 /* Keep __tzname_cur_max up to date. */
423 size_t len0 = strlen (__tzname[0]);
424 size_t len1 = strlen (__tzname[1]);
425 if (len0 > __tzname_cur_max)
426 __tzname_cur_max = len0;
427 if (len1 > __tzname_cur_max)
428 __tzname_cur_max = len1;
432 /* Figure out the exact time (as a time_t) in YEAR
433 when the change described by RULE will occur and
434 put it in RULE->change, saving YEAR in RULE->computed_for. */
435 static void
436 internal_function
437 compute_change (rule, year)
438 tz_rule *rule;
439 int year;
441 register time_t t;
443 if (year != -1 && rule->computed_for == year)
444 /* Operations on times in 2 BC will be slower. Oh well. */
445 return;
447 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
448 if (year > 1970)
449 t = ((year - 1970) * 365
450 + /* Compute the number of leapdays between 1970 and YEAR
451 (exclusive). There is a leapday every 4th year ... */
452 + ((year - 1) / 4 - 1970 / 4)
453 /* ... except every 100th year ... */
454 - ((year - 1) / 100 - 1970 / 100)
455 /* ... but still every 400th year. */
456 + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY;
457 else
458 t = 0;
460 switch (rule->type)
462 case J1:
463 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
464 In non-leap years, or if the day number is 59 or less, just
465 add SECSPERDAY times the day number-1 to the time of
466 January 1, midnight, to get the day. */
467 t += (rule->d - 1) * SECSPERDAY;
468 if (rule->d >= 60 && __isleap (year))
469 t += SECSPERDAY;
470 break;
472 case J0:
473 /* n - Day of year.
474 Just add SECSPERDAY times the day number to the time of Jan 1st. */
475 t += rule->d * SECSPERDAY;
476 break;
478 case M:
479 /* Mm.n.d - Nth "Dth day" of month M. */
481 unsigned int i;
482 int d, m1, yy0, yy1, yy2, dow;
483 const unsigned short int *myday =
484 &__mon_yday[__isleap (year)][rule->m];
486 /* First add SECSPERDAY for each day in months before M. */
487 t += myday[-1] * SECSPERDAY;
489 /* Use Zeller's Congruence to get day-of-week of first day of month. */
490 m1 = (rule->m + 9) % 12 + 1;
491 yy0 = (rule->m <= 2) ? (year - 1) : year;
492 yy1 = yy0 / 100;
493 yy2 = yy0 % 100;
494 dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
495 if (dow < 0)
496 dow += 7;
498 /* DOW is the day-of-week of the first day of the month. Get the
499 day-of-month (zero-origin) of the first DOW day of the month. */
500 d = rule->d - dow;
501 if (d < 0)
502 d += 7;
503 for (i = 1; i < rule->n; ++i)
505 if (d + 7 >= (int) myday[0] - myday[-1])
506 break;
507 d += 7;
510 /* D is the day-of-month (zero-origin) of the day we want. */
511 t += d * SECSPERDAY;
513 break;
516 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
517 Just add the time of day and local offset from GMT, and we're done. */
519 rule->change = t - rule->offset + rule->secs;
520 rule->computed_for = year;
524 /* Figure out the correct timezone for TM and set `__tzname',
525 `__timezone', and `__daylight' accordingly. */
526 static void
527 internal_function
528 tz_compute (tm)
529 const struct tm *tm;
531 compute_change (&tz_rules[0], 1900 + tm->tm_year);
532 compute_change (&tz_rules[1], 1900 + tm->tm_year);
535 /* Reinterpret the TZ environment variable and set `tzname'. */
536 #undef tzset
538 void
539 __tzset (void)
541 __libc_lock_lock (tzset_lock);
543 tzset_internal (1, 1);
545 if (!__use_tzfile)
547 /* Set `tzname'. */
548 __tzname[0] = (char *) tz_rules[0].name;
549 __tzname[1] = (char *) tz_rules[1].name;
552 __libc_lock_unlock (tzset_lock);
554 weak_alias (__tzset, tzset)
556 /* Return the `struct tm' representation of *TIMER in the local timezone.
557 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
558 struct tm *
559 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
561 long int leap_correction;
562 int leap_extra_secs;
564 if (timer == NULL)
566 __set_errno (EINVAL);
567 return NULL;
570 __libc_lock_lock (tzset_lock);
572 /* Update internal database according to current TZ setting.
573 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
574 This is a good idea since this allows at least a bit more parallelism.
575 By analogy we apply the same rule to gmtime_r. */
576 tzset_internal (tp == &_tmbuf, 0);
578 if (__use_tzfile)
579 __tzfile_compute (*timer, use_localtime, &leap_correction,
580 &leap_extra_secs, tp);
581 else
583 if (! __offtime (timer, 0, tp))
584 tp = NULL;
585 else
586 tz_compute (tp);
587 leap_correction = 0L;
588 leap_extra_secs = 0;
591 if (tp)
593 if (use_localtime)
595 if (!__use_tzfile)
597 int isdst;
599 /* We have to distinguish between northern and southern
600 hemisphere. For the latter the daylight saving time
601 ends in the next year. */
602 if (__builtin_expect (tz_rules[0].change
603 > tz_rules[1].change, 0))
604 isdst = (*timer < tz_rules[1].change
605 || *timer >= tz_rules[0].change);
606 else
607 isdst = (*timer >= tz_rules[0].change
608 && *timer < tz_rules[1].change);
609 tp->tm_isdst = isdst;
610 tp->tm_zone = __tzname[isdst];
611 tp->tm_gmtoff = tz_rules[isdst].offset;
614 else
616 tp->tm_isdst = 0;
617 tp->tm_zone = "GMT";
618 tp->tm_gmtoff = 0L;
621 if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
622 tp->tm_sec += leap_extra_secs;
623 else
624 tp = NULL;
627 __libc_lock_unlock (tzset_lock);
629 return tp;
633 libc_freeres_fn (free_mem)
635 while (tzstring_list != NULL)
637 struct tzstring_l *old = tzstring_list;
639 tzstring_list = tzstring_list->next;
640 free (old);
642 free (old_tz);
643 old_tz = NULL;