Add missing .text directives.
[glibc/pb-stable.git] / time / tzset.c
blobb87578fe48a7688980b176cba482b394508dd037
1 /* Copyright (C) 1991-2002,2003,2004,2007,2009 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 tzset_internal (int always, int explicit)
74 __THROW internal_function;
76 /* List of buffers containing time zone strings. */
77 struct tzstring_l
79 struct tzstring_l *next;
80 size_t len; /* strlen(data) - doesn't count terminating NUL! */
81 char data[0];
84 static struct tzstring_l *tzstring_list;
86 /* Allocate a permanent home for S. It will never be moved or deallocated,
87 but may share space with other strings.
88 Don't modify the returned string. */
89 char *
90 __tzstring (const char *s)
92 char *p;
93 struct tzstring_l *t, *u, *new;
94 size_t len = strlen (s);
96 /* Walk the list and look for a match. If this string is the same
97 as the end of an already-allocated string, it can share space. */
98 for (u = t = tzstring_list; t; u = t, t = t->next)
99 if (len <= t->len)
101 p = &t->data[t->len - len];
102 if (strcmp (s, p) == 0)
103 return p;
106 /* Not found; allocate a new buffer. */
107 new = malloc (sizeof (struct tzstring_l) + len + 1);
108 if (!new)
109 return NULL;
111 new->next = NULL;
112 new->len = len;
113 strcpy (new->data, s);
115 if (u)
116 u->next = new;
117 else
118 tzstring_list = new;
120 return new->data;
123 /* Maximum length of a timezone name. tzset_internal keeps this up to date
124 (never decreasing it) when ! __use_tzfile.
125 tzfile.c keeps it up to date when __use_tzfile. */
126 size_t __tzname_cur_max;
128 long int
129 __tzname_max ()
131 __libc_lock_lock (tzset_lock);
133 tzset_internal (0, 0);
135 __libc_lock_unlock (tzset_lock);
137 return __tzname_cur_max;
140 static char *old_tz;
142 static void
143 internal_function
144 update_vars (void)
146 __daylight = tz_rules[0].offset != tz_rules[1].offset;
147 __timezone = -tz_rules[0].offset;
148 __tzname[0] = (char *) tz_rules[0].name;
149 __tzname[1] = (char *) tz_rules[1].name;
151 /* Keep __tzname_cur_max up to date. */
152 size_t len0 = strlen (__tzname[0]);
153 size_t len1 = strlen (__tzname[1]);
154 if (len0 > __tzname_cur_max)
155 __tzname_cur_max = len0;
156 if (len1 > __tzname_cur_max)
157 __tzname_cur_max = len1;
161 static unsigned int
162 __attribute_noinline__
163 compute_offset (unsigned int ss, unsigned int mm, unsigned int hh)
165 return min (ss, 59) + min (mm, 59) * 60 + min (hh, 24) * 60 * 60;
169 /* Parse the POSIX TZ-style string. */
170 void
171 __tzset_parse_tz (tz)
172 const char *tz;
174 unsigned short int hh, mm, ss;
176 /* Clear out old state and reset to unnamed UTC. */
177 memset (tz_rules, '\0', sizeof tz_rules);
178 tz_rules[0].name = tz_rules[1].name = "";
180 /* Get the standard timezone name. */
181 char *tzbuf = strdupa (tz);
183 int consumed;
184 if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1)
186 /* Check for the quoted version. */
187 char *wp = tzbuf;
188 if (__builtin_expect (*tz++ != '<', 0))
189 goto out;
191 while (isalnum (*tz) || *tz == '+' || *tz == '-')
192 *wp++ = *tz++;
193 if (__builtin_expect (*tz++ != '>' || wp - tzbuf < 3, 0))
194 goto out;
195 *wp = '\0';
197 else if (__builtin_expect (consumed < 3, 0))
198 goto out;
199 else
200 tz += consumed;
202 tz_rules[0].name = __tzstring (tzbuf);
204 /* Figure out the standard offset from UTC. */
205 if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
206 goto out;
208 if (*tz == '-' || *tz == '+')
209 tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
210 else
211 tz_rules[0].offset = -1L;
212 switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
213 &hh, &consumed, &mm, &consumed, &ss, &consumed))
215 default:
216 tz_rules[0].offset = 0;
217 goto out;
218 case 1:
219 mm = 0;
220 case 2:
221 ss = 0;
222 case 3:
223 break;
225 tz_rules[0].offset *= compute_offset (ss, mm, hh);
226 tz += consumed;
228 /* Get the DST timezone name (if any). */
229 if (*tz != '\0')
231 if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1)
233 /* Check for the quoted version. */
234 char *wp = tzbuf;
235 const char *rp = tz;
236 if (__builtin_expect (*rp++ != '<', 0))
237 /* Punt on name, set up the offsets. */
238 goto done_names;
240 while (isalnum (*rp) || *rp == '+' || *rp == '-')
241 *wp++ = *rp++;
242 if (__builtin_expect (*rp++ != '>' || wp - tzbuf < 3, 0))
243 /* Punt on name, set up the offsets. */
244 goto done_names;
245 *wp = '\0';
246 tz = rp;
248 else if (__builtin_expect (consumed < 3, 0))
249 /* Punt on name, set up the offsets. */
250 goto done_names;
251 else
252 tz += consumed;
254 tz_rules[1].name = __tzstring (tzbuf);
256 /* Figure out the DST offset from GMT. */
257 if (*tz == '-' || *tz == '+')
258 tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
259 else
260 tz_rules[1].offset = -1L;
262 switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
263 &hh, &consumed, &mm, &consumed, &ss, &consumed))
265 default:
266 /* Default to one hour later than standard time. */
267 tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
268 break;
270 case 1:
271 mm = 0;
272 case 2:
273 ss = 0;
274 case 3:
275 tz_rules[1].offset *= compute_offset (ss, mm, hh);
276 tz += consumed;
277 break;
279 if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
281 /* There is no rule. See if there is a default rule file. */
282 __tzfile_default (tz_rules[0].name, tz_rules[1].name,
283 tz_rules[0].offset, tz_rules[1].offset);
284 if (__use_tzfile)
286 free (old_tz);
287 old_tz = NULL;
288 return;
292 else
294 /* There is no DST. */
295 tz_rules[1].name = tz_rules[0].name;
296 tz_rules[1].offset = tz_rules[0].offset;
297 goto out;
300 done_names:
301 /* Figure out the standard <-> DST rules. */
302 for (unsigned int whichrule = 0; whichrule < 2; ++whichrule)
304 register tz_rule *tzr = &tz_rules[whichrule];
306 /* Ignore comma to support string following the incorrect
307 specification in early POSIX.1 printings. */
308 tz += *tz == ',';
310 /* Get the date of the change. */
311 if (*tz == 'J' || isdigit (*tz))
313 char *end;
314 tzr->type = *tz == 'J' ? J1 : J0;
315 if (tzr->type == J1 && !isdigit (*++tz))
316 goto out;
317 unsigned long int d = strtoul (tz, &end, 10);
318 if (end == tz || d > 365)
319 goto out;
320 if (tzr->type == J1 && d == 0)
321 goto out;
322 tzr->d = d;
323 tz = end;
325 else if (*tz == 'M')
327 tzr->type = M;
328 if (sscanf (tz, "M%hu.%hu.%hu%n",
329 &tzr->m, &tzr->n, &tzr->d, &consumed) != 3
330 || tzr->m < 1 || tzr->m > 12
331 || tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
332 goto out;
333 tz += consumed;
335 else if (*tz == '\0')
337 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
338 tzr->type = M;
339 if (tzr == &tz_rules[0])
341 tzr->m = 4;
342 tzr->n = 1;
343 tzr->d = 0;
345 else
347 tzr->m = 10;
348 tzr->n = 5;
349 tzr->d = 0;
352 else
353 goto out;
355 if (*tz != '\0' && *tz != '/' && *tz != ',')
356 goto out;
357 else if (*tz == '/')
359 /* Get the time of day of the change. */
360 ++tz;
361 if (*tz == '\0')
362 goto out;
363 consumed = 0;
364 switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
365 &hh, &consumed, &mm, &consumed, &ss, &consumed))
367 default:
368 hh = 2; /* Default to 2:00 AM. */
369 case 1:
370 mm = 0;
371 case 2:
372 ss = 0;
373 case 3:
374 break;
376 tz += consumed;
377 tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
379 else
380 /* Default to 2:00 AM. */
381 tzr->secs = 2 * 60 * 60;
383 tzr->computed_for = -1;
386 out:
387 update_vars ();
390 /* Interpret the TZ envariable. */
391 static void
392 internal_function
393 tzset_internal (always, explicit)
394 int always;
395 int explicit;
397 static int is_initialized;
398 register const char *tz;
400 if (is_initialized && !always)
401 return;
402 is_initialized = 1;
404 /* Examine the TZ environment variable. */
405 tz = getenv ("TZ");
406 if (tz == NULL && !explicit)
407 /* Use the site-wide default. This is a file name which means we
408 would not see changes to the file if we compare only the file
409 name for change. We want to notice file changes if tzset() has
410 been called explicitly. Leave TZ as NULL in this case. */
411 tz = TZDEFAULT;
412 if (tz && *tz == '\0')
413 /* User specified the empty string; use UTC explicitly. */
414 tz = "Universal";
416 /* A leading colon means "implementation defined syntax".
417 We ignore the colon and always use the same algorithm:
418 try a data file, and if none exists parse the 1003.1 syntax. */
419 if (tz && *tz == ':')
420 ++tz;
422 /* Check whether the value changed since the last run. */
423 if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
424 /* No change, simply return. */
425 return;
427 if (tz == NULL)
428 /* No user specification; use the site-wide default. */
429 tz = TZDEFAULT;
431 tz_rules[0].name = NULL;
432 tz_rules[1].name = NULL;
434 /* Save the value of `tz'. */
435 free (old_tz);
436 old_tz = tz ? __strdup (tz) : NULL;
438 /* Try to read a data file. */
439 __tzfile_read (tz, 0, NULL);
440 if (__use_tzfile)
441 return;
443 /* No data file found. Default to UTC if nothing specified. */
445 if (tz == NULL || *tz == '\0'
446 || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))
448 memset (tz_rules, '\0', sizeof tz_rules);
449 tz_rules[0].name = tz_rules[1].name = "UTC";
450 if (J0 != 0)
451 tz_rules[0].type = tz_rules[1].type = J0;
452 tz_rules[0].change = tz_rules[1].change = (time_t) -1;
453 update_vars ();
454 return;
457 __tzset_parse_tz (tz);
460 /* Figure out the exact time (as a time_t) in YEAR
461 when the change described by RULE will occur and
462 put it in RULE->change, saving YEAR in RULE->computed_for. */
463 static void
464 internal_function
465 compute_change (rule, year)
466 tz_rule *rule;
467 int year;
469 register time_t t;
471 if (year != -1 && rule->computed_for == year)
472 /* Operations on times in 2 BC will be slower. Oh well. */
473 return;
475 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
476 if (year > 1970)
477 t = ((year - 1970) * 365
478 + /* Compute the number of leapdays between 1970 and YEAR
479 (exclusive). There is a leapday every 4th year ... */
480 + ((year - 1) / 4 - 1970 / 4)
481 /* ... except every 100th year ... */
482 - ((year - 1) / 100 - 1970 / 100)
483 /* ... but still every 400th year. */
484 + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY;
485 else
486 t = 0;
488 switch (rule->type)
490 case J1:
491 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
492 In non-leap years, or if the day number is 59 or less, just
493 add SECSPERDAY times the day number-1 to the time of
494 January 1, midnight, to get the day. */
495 t += (rule->d - 1) * SECSPERDAY;
496 if (rule->d >= 60 && __isleap (year))
497 t += SECSPERDAY;
498 break;
500 case J0:
501 /* n - Day of year.
502 Just add SECSPERDAY times the day number to the time of Jan 1st. */
503 t += rule->d * SECSPERDAY;
504 break;
506 case M:
507 /* Mm.n.d - Nth "Dth day" of month M. */
509 unsigned int i;
510 int d, m1, yy0, yy1, yy2, dow;
511 const unsigned short int *myday =
512 &__mon_yday[__isleap (year)][rule->m];
514 /* First add SECSPERDAY for each day in months before M. */
515 t += myday[-1] * SECSPERDAY;
517 /* Use Zeller's Congruence to get day-of-week of first day of month. */
518 m1 = (rule->m + 9) % 12 + 1;
519 yy0 = (rule->m <= 2) ? (year - 1) : year;
520 yy1 = yy0 / 100;
521 yy2 = yy0 % 100;
522 dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
523 if (dow < 0)
524 dow += 7;
526 /* DOW is the day-of-week of the first day of the month. Get the
527 day-of-month (zero-origin) of the first DOW day of the month. */
528 d = rule->d - dow;
529 if (d < 0)
530 d += 7;
531 for (i = 1; i < rule->n; ++i)
533 if (d + 7 >= (int) myday[0] - myday[-1])
534 break;
535 d += 7;
538 /* D is the day-of-month (zero-origin) of the day we want. */
539 t += d * SECSPERDAY;
541 break;
544 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
545 Just add the time of day and local offset from GMT, and we're done. */
547 rule->change = t - rule->offset + rule->secs;
548 rule->computed_for = year;
552 /* Figure out the correct timezone for TM and set `__tzname',
553 `__timezone', and `__daylight' accordingly. */
554 void
555 internal_function
556 __tz_compute (timer, tm, use_localtime)
557 time_t timer;
558 struct tm *tm;
559 int use_localtime;
561 compute_change (&tz_rules[0], 1900 + tm->tm_year);
562 compute_change (&tz_rules[1], 1900 + tm->tm_year);
564 if (use_localtime)
566 int isdst;
568 /* We have to distinguish between northern and southern
569 hemisphere. For the latter the daylight saving time
570 ends in the next year. */
571 if (__builtin_expect (tz_rules[0].change
572 > tz_rules[1].change, 0))
573 isdst = (timer < tz_rules[1].change
574 || timer >= tz_rules[0].change);
575 else
576 isdst = (timer >= tz_rules[0].change
577 && timer < tz_rules[1].change);
578 tm->tm_isdst = isdst;
579 tm->tm_zone = __tzname[isdst];
580 tm->tm_gmtoff = tz_rules[isdst].offset;
584 /* Reinterpret the TZ environment variable and set `tzname'. */
585 #undef tzset
587 void
588 __tzset (void)
590 __libc_lock_lock (tzset_lock);
592 tzset_internal (1, 1);
594 if (!__use_tzfile)
596 /* Set `tzname'. */
597 __tzname[0] = (char *) tz_rules[0].name;
598 __tzname[1] = (char *) tz_rules[1].name;
601 __libc_lock_unlock (tzset_lock);
603 weak_alias (__tzset, tzset)
605 /* Return the `struct tm' representation of *TIMER in the local timezone.
606 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
607 struct tm *
608 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
610 long int leap_correction;
611 int leap_extra_secs;
613 if (timer == NULL)
615 __set_errno (EINVAL);
616 return NULL;
619 __libc_lock_lock (tzset_lock);
621 /* Update internal database according to current TZ setting.
622 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
623 This is a good idea since this allows at least a bit more parallelism. */
624 tzset_internal (tp == &_tmbuf && use_localtime, 1);
626 if (__use_tzfile)
627 __tzfile_compute (*timer, use_localtime, &leap_correction,
628 &leap_extra_secs, tp);
629 else
631 if (! __offtime (timer, 0, tp))
632 tp = NULL;
633 else
634 __tz_compute (*timer, tp, use_localtime);
635 leap_correction = 0L;
636 leap_extra_secs = 0;
639 if (tp)
641 if (! use_localtime)
643 tp->tm_isdst = 0;
644 tp->tm_zone = "GMT";
645 tp->tm_gmtoff = 0L;
648 if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
649 tp->tm_sec += leap_extra_secs;
650 else
651 tp = NULL;
654 __libc_lock_unlock (tzset_lock);
656 return tp;
660 libc_freeres_fn (free_mem)
662 while (tzstring_list != NULL)
664 struct tzstring_l *old = tzstring_list;
666 tzstring_list = tzstring_list->next;
667 free (old);
669 free (old_tz);
670 old_tz = NULL;