]> sourceware.org Git - glibc.git/blob - time/tzset.c
6af2fbaf004770cc17d4186db58c58b71040b083
[glibc.git] / time / tzset.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
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.
8
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.
13
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. */
18
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>
27
28
29 #define NOID
30 #include <timezone/tzfile.h>
31
32 char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
33 int __daylight = 0;
34 long int __timezone = 0L;
35
36 weak_alias (__tzname, tzname)
37 weak_alias (__daylight, daylight)
38 weak_alias (__timezone, timezone)
39
40 /* This locks all the state variables in tzfile.c and this file. */
41 __libc_lock_define (static, tzset_lock)
42
43
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)
47
48
49 /* This structure contains all the information about a
50 timezone given in the POSIX standard TZ envariable. */
51 typedef struct
52 {
53 const char *name;
54
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. */
59
60 long int offset; /* Seconds east of GMT (west if < 0). */
61
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;
67
68 /* tz_rules[0] is standard, tz_rules[1] is daylight. */
69 static tz_rule tz_rules[2];
70
71
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;
76 \f
77 /* List of buffers containing time zone strings. */
78 struct tzstring_l
79 {
80 struct tzstring_l *next;
81 size_t len; /* strlen(data) - doesn't count terminating NUL! */
82 char data[0];
83 };
84
85 struct tzstring_l *tzstring_list;
86
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)
92 {
93 char *p;
94 struct tzstring_l *t, *u, *new;
95 size_t len = strlen(s);
96
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)
101 {
102 p = &t->data[t->len - len];
103 if (strcmp (s, p) == 0)
104 return p;
105 }
106
107 /* Not found; allocate a new buffer. */
108 new = malloc (sizeof (struct tzstring_l) + len + 1);
109 if (!new)
110 return NULL;
111
112 new->next = NULL;
113 new->len = len;
114 strcpy (new->data, s);
115
116 if (u)
117 u->next = new;
118 else
119 tzstring_list = new;
120
121 return new->data;
122 }
123 \f
124 static char *old_tz;
125
126 /* Interpret the TZ envariable. */
127 static void
128 internal_function
129 tzset_internal (always)
130 int always;
131 {
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;
138
139 if (is_initialized && !always)
140 return;
141 is_initialized = 1;
142
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";
151
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;
157
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;
162
163 tz_rules[0].name = NULL;
164 tz_rules[1].name = NULL;
165
166 /* Save the value of `tz'. */
167 if (old_tz != NULL)
168 free (old_tz);
169 old_tz = tz ? __strdup (tz) : NULL;
170
171 /* Try to read a data file. */
172 __tzfile_read (tz, 0, NULL);
173 if (__use_tzfile)
174 return;
175
176 /* No data file found. Default to UTC if nothing specified. */
177
178 if (tz == NULL || *tz == '\0')
179 {
180 tz_rules[0].name = tz_rules[1].name = "UTC";
181 tz_rules[0].type = tz_rules[1].type = J0;
182 tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
183 tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
184 tz_rules[0].secs = tz_rules[1].secs = 0;
185 tz_rules[0].offset = tz_rules[1].offset = 0L;
186 tz_rules[0].change = tz_rules[1].change = (time_t) -1;
187 tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
188 return;
189 }
190
191 /* Clear out old state and reset to unnamed UTC. */
192 memset (tz_rules, 0, sizeof tz_rules);
193 tz_rules[0].name = tz_rules[1].name = "";
194
195 /* Get the standard timezone name. */
196 tzbuf = strdupa (tz);
197
198 if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 ||
199 (l = strlen (tzbuf)) < 3)
200 return;
201
202 tz_rules[0].name = __tzstring (tzbuf);
203
204 tz += l;
205
206 /* Figure out the standard offset from UTC. */
207 if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
208 return;
209
210 if (*tz == '-' || *tz == '+')
211 tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
212 else
213 tz_rules[0].offset = -1L;
214 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
215 {
216 default:
217 return;
218 case 1:
219 mm = 0;
220 case 2:
221 ss = 0;
222 case 3:
223 break;
224 }
225 tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
226 (min (hh, 23) * 60 * 60));
227
228 for (l = 0; l < 3; ++l)
229 {
230 while (isdigit(*tz))
231 ++tz;
232 if (l < 2 && *tz == ':')
233 ++tz;
234 }
235
236 /* Get the DST timezone name (if any). */
237 if (*tz != '\0')
238 {
239 char *n = tzbuf + strlen (tzbuf) + 1;
240 if (sscanf (tz, "%[^0-9,+-]", n) != 1 ||
241 (l = strlen (n)) < 3)
242 goto done_names; /* Punt on name, set up the offsets. */
243
244 tz_rules[1].name = __tzstring (n);
245
246 tz += l;
247
248 /* Figure out the DST offset from GMT. */
249 if (*tz == '-' || *tz == '+')
250 tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
251 else
252 tz_rules[1].offset = -1L;
253
254 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
255 {
256 default:
257 /* Default to one hour later than standard time. */
258 tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
259 break;
260
261 case 1:
262 mm = 0;
263 case 2:
264 ss = 0;
265 case 3:
266 tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
267 (min (hh, 23) * (60 * 60)));
268 break;
269 }
270 for (l = 0; l < 3; ++l)
271 {
272 while (isdigit (*tz))
273 ++tz;
274 if (l < 2 && *tz == ':')
275 ++tz;
276 }
277 if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
278 {
279 /* There is no rule. See if there is a default rule file. */
280 __tzfile_default (tz_rules[0].name, tz_rules[1].name,
281 tz_rules[0].offset, tz_rules[1].offset);
282 if (__use_tzfile)
283 {
284 free (old_tz);
285 old_tz = NULL;
286 return;
287 }
288 }
289 }
290 else
291 {
292 /* There is no DST. */
293 tz_rules[1].name = tz_rules[0].name;
294 tz_rules[1].offset = tz_rules[0].offset;
295 goto out;
296 }
297
298 done_names:
299 /* Figure out the standard <-> DST rules. */
300 for (whichrule = 0; whichrule < 2; ++whichrule)
301 {
302 register tz_rule *tzr = &tz_rules[whichrule];
303
304 /* Ignore comma to support string following the incorrect
305 specification in early POSIX.1 printings. */
306 tz += *tz == ',';
307
308 /* Get the date of the change. */
309 if (*tz == 'J' || isdigit (*tz))
310 {
311 char *end;
312 tzr->type = *tz == 'J' ? J1 : J0;
313 if (tzr->type == J1 && !isdigit (*++tz))
314 goto out;
315 tzr->d = (unsigned short int) strtoul (tz, &end, 10);
316 if (end == tz || tzr->d > 365)
317 goto out;
318 else if (tzr->type == J1 && tzr->d == 0)
319 goto out;
320 tz = end;
321 }
322 else if (*tz == 'M')
323 {
324 int n;
325 tzr->type = M;
326 if (sscanf (tz, "M%hu.%hu.%hu%n",
327 &tzr->m, &tzr->n, &tzr->d, &n) != 3 ||
328 tzr->m < 1 || tzr->m > 12 ||
329 tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
330 goto out;
331 tz += n;
332 }
333 else if (*tz == '\0')
334 {
335 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
336 tzr->type = M;
337 if (tzr == &tz_rules[0])
338 {
339 tzr->m = 4;
340 tzr->n = 1;
341 tzr->d = 0;
342 }
343 else
344 {
345 tzr->m = 10;
346 tzr->n = 5;
347 tzr->d = 0;
348 }
349 }
350 else
351 goto out;
352
353 if (*tz != '\0' && *tz != '/' && *tz != ',')
354 goto out;
355 else if (*tz == '/')
356 {
357 /* Get the time of day of the change. */
358 ++tz;
359 if (*tz == '\0')
360 goto out;
361 switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
362 {
363 default:
364 hh = 2; /* Default to 2:00 AM. */
365 case 1:
366 mm = 0;
367 case 2:
368 ss = 0;
369 case 3:
370 break;
371 }
372 for (l = 0; l < 3; ++l)
373 {
374 while (isdigit (*tz))
375 ++tz;
376 if (l < 2 && *tz == ':')
377 ++tz;
378 }
379 tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
380 }
381 else
382 /* Default to 2:00 AM. */
383 tzr->secs = 2 * 60 * 60;
384
385 tzr->computed_for = -1;
386 }
387
388 out:
389 /* We know the offset now, set `__timezone'. */
390 __timezone = -tz_rules[0].offset;
391 }
392 \f
393 /* Maximum length of a timezone name. __tz_compute keeps this up to date
394 (never decreasing it) when ! __use_tzfile.
395 tzfile.c keeps it up to date when __use_tzfile. */
396 size_t __tzname_cur_max;
397
398 long int
399 __tzname_max ()
400 {
401 __libc_lock_lock (tzset_lock);
402
403 tzset_internal (0);
404
405 __libc_lock_unlock (tzset_lock);
406
407 return __tzname_cur_max;
408 }
409 \f
410 /* Figure out the exact time (as a time_t) in YEAR
411 when the change described by RULE will occur and
412 put it in RULE->change, saving YEAR in RULE->computed_for.
413 Return nonzero if successful, zero on failure. */
414 static int
415 internal_function
416 compute_change (rule, year)
417 tz_rule *rule;
418 int year;
419 {
420 register time_t t;
421
422 if (year != -1 && rule->computed_for == year)
423 /* Operations on times in 2 BC will be slower. Oh well. */
424 return 1;
425
426 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
427 if (year > 1970)
428 t = ((year - 1970) * 365
429 + /* Compute the number of leapdays between 1970 and YEAR
430 (exclusive). There is a leapday every 4th year ... */
431 + ((year - 1) / 4 - 1970 / 4)
432 /* ... except every 100th year ... */
433 - ((year - 1) / 100 - 1970 / 100)
434 /* ... but still every 400th year. */
435 + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY;
436 else
437 t = 0;
438
439 switch (rule->type)
440 {
441 case J1:
442 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
443 In non-leap years, or if the day number is 59 or less, just
444 add SECSPERDAY times the day number-1 to the time of
445 January 1, midnight, to get the day. */
446 t += (rule->d - 1) * SECSPERDAY;
447 if (rule->d >= 60 && __isleap (year))
448 t += SECSPERDAY;
449 break;
450
451 case J0:
452 /* n - Day of year.
453 Just add SECSPERDAY times the day number to the time of Jan 1st. */
454 t += rule->d * SECSPERDAY;
455 break;
456
457 case M:
458 /* Mm.n.d - Nth "Dth day" of month M. */
459 {
460 unsigned int i;
461 int d, m1, yy0, yy1, yy2, dow;
462 const unsigned short int *myday =
463 &__mon_yday[__isleap (year)][rule->m];
464
465 /* First add SECSPERDAY for each day in months before M. */
466 t += myday[-1] * SECSPERDAY;
467
468 /* Use Zeller's Congruence to get day-of-week of first day of month. */
469 m1 = (rule->m + 9) % 12 + 1;
470 yy0 = (rule->m <= 2) ? (year - 1) : year;
471 yy1 = yy0 / 100;
472 yy2 = yy0 % 100;
473 dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
474 if (dow < 0)
475 dow += 7;
476
477 /* DOW is the day-of-week of the first day of the month. Get the
478 day-of-month (zero-origin) of the first DOW day of the month. */
479 d = rule->d - dow;
480 if (d < 0)
481 d += 7;
482 for (i = 1; i < rule->n; ++i)
483 {
484 if (d + 7 >= (int) myday[0] - myday[-1])
485 break;
486 d += 7;
487 }
488
489 /* D is the day-of-month (zero-origin) of the day we want. */
490 t += d * SECSPERDAY;
491 }
492 break;
493 }
494
495 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
496 Just add the time of day and local offset from GMT, and we're done. */
497
498 rule->change = t - rule->offset + rule->secs;
499 rule->computed_for = year;
500 return 1;
501 }
502
503
504 /* Figure out the correct timezone for TM and set `__tzname',
505 `__timezone', and `__daylight' accordingly. Return nonzero on
506 success, zero on failure. */
507 static int
508 internal_function
509 tz_compute (tm)
510 const struct tm *tm;
511 {
512 if (! compute_change (&tz_rules[0], 1900 + tm->tm_year)
513 || ! compute_change (&tz_rules[1], 1900 + tm->tm_year))
514 return 0;
515 /* We have to distinguish between northern and southern hemisphere.
516 For the later the daylight saving time ends in the next year.
517 It is easier to detect this after first computing the time for the
518 wrong year since now we simply can compare the times to switch. */
519 if (tz_rules[0].change > tz_rules[1].change
520 && ! compute_change (&tz_rules[1], 1900 + tm->tm_year + 1))
521 return 0;
522
523 __daylight = tz_rules[0].offset != tz_rules[1].offset;
524 __timezone = -tz_rules[0].offset;
525 __tzname[0] = (char *) tz_rules[0].name;
526 __tzname[1] = (char *) tz_rules[1].name;
527
528 {
529 /* Keep __tzname_cur_max up to date. */
530 size_t len0 = strlen (__tzname[0]);
531 size_t len1 = strlen (__tzname[1]);
532 if (len0 > __tzname_cur_max)
533 __tzname_cur_max = len0;
534 if (len1 > __tzname_cur_max)
535 __tzname_cur_max = len1;
536 }
537
538 return 1;
539 }
540 \f
541 /* Reinterpret the TZ environment variable and set `tzname'. */
542 #undef tzset
543
544 void
545 __tzset (void)
546 {
547 __libc_lock_lock (tzset_lock);
548
549 tzset_internal (1);
550
551 if (!__use_tzfile)
552 {
553 /* Set `tzname'. */
554 __tzname[0] = (char *) tz_rules[0].name;
555 __tzname[1] = (char *) tz_rules[1].name;
556 }
557
558 __libc_lock_unlock (tzset_lock);
559 }
560 weak_alias (__tzset, tzset)
561 \f
562 /* Return the `struct tm' representation of *TIMER in the local timezone.
563 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
564 struct tm *
565 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
566 {
567 long int leap_correction;
568 int leap_extra_secs;
569
570 if (timer == NULL)
571 {
572 __set_errno (EINVAL);
573 return NULL;
574 }
575
576 __libc_lock_lock (tzset_lock);
577
578 /* Update internal database according to current TZ setting.
579 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
580 This is a good idea since this allows at least a bit more parallelism.
581 By analogy we apply the same rule to gmtime_r. */
582 tzset_internal (tp == &_tmbuf);
583
584 if (__use_tzfile)
585 {
586 if (! __tzfile_compute (*timer, use_localtime,
587 &leap_correction, &leap_extra_secs, tp))
588 tp = NULL;
589 }
590 else
591 {
592 if (! (__offtime (timer, 0, tp) && tz_compute (tp)))
593 tp = NULL;
594 leap_correction = 0L;
595 leap_extra_secs = 0;
596 }
597
598 if (tp)
599 {
600 if (use_localtime)
601 {
602 if (!__use_tzfile)
603 {
604 int isdst = (*timer >= tz_rules[0].change
605 && *timer < tz_rules[1].change);
606 tp->tm_isdst = isdst;
607 tp->tm_zone = __tzname[isdst];
608 tp->tm_gmtoff = tz_rules[isdst].offset;
609 }
610 }
611 else
612 {
613 tp->tm_isdst = 0;
614 tp->tm_zone = "GMT";
615 tp->tm_gmtoff = 0L;
616 }
617
618 if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
619 tp->tm_sec += leap_extra_secs;
620 else
621 tp = NULL;
622 }
623
624 __libc_lock_unlock (tzset_lock);
625
626 return tp;
627 }
This page took 0.068635 seconds and 4 git commands to generate.