Removed in laer C9x draft.
[kopensolaris-gnu/glibc.git] / time / tzset.c
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.
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 = NULL;
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 = 0;
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);
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   int y;
422
423   if (year != -1 && rule->computed_for == year)
424     /* Operations on times in 1969 will be slower.  Oh well.  */
425     return 1;
426
427   /* First set T to January 1st, 0:00:00 GMT in YEAR.  */
428   t = 0;
429   for (y = 1970; y < year; ++y)
430     t += SECSPERDAY * (__isleap (y) ? 366 : 365);
431
432   switch (rule->type)
433     {
434     case J1:
435       /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
436          In non-leap years, or if the day number is 59 or less, just
437          add SECSPERDAY times the day number-1 to the time of
438          January 1, midnight, to get the day.  */
439       t += (rule->d - 1) * SECSPERDAY;
440       if (rule->d >= 60 && __isleap (year))
441         t += SECSPERDAY;
442       break;
443
444     case J0:
445       /* n - Day of year.
446          Just add SECSPERDAY times the day number to the time of Jan 1st.  */
447       t += rule->d * SECSPERDAY;
448       break;
449
450     case M:
451       /* Mm.n.d - Nth "Dth day" of month M.  */
452       {
453         unsigned int i;
454         int d, m1, yy0, yy1, yy2, dow;
455         const unsigned short int *myday =
456           &__mon_yday[__isleap (year)][rule->m];
457
458         /* First add SECSPERDAY for each day in months before M.  */
459         t += myday[-1] * SECSPERDAY;
460
461         /* Use Zeller's Congruence to get day-of-week of first day of month. */
462         m1 = (rule->m + 9) % 12 + 1;
463         yy0 = (rule->m <= 2) ? (year - 1) : year;
464         yy1 = yy0 / 100;
465         yy2 = yy0 % 100;
466         dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
467         if (dow < 0)
468           dow += 7;
469
470         /* DOW is the day-of-week of the first day of the month.  Get the
471            day-of-month (zero-origin) of the first DOW day of the month.  */
472         d = rule->d - dow;
473         if (d < 0)
474           d += 7;
475         for (i = 1; i < rule->n; ++i)
476           {
477             if (d + 7 >= (int) myday[0] - myday[-1])
478               break;
479             d += 7;
480           }
481
482         /* D is the day-of-month (zero-origin) of the day we want.  */
483         t += d * SECSPERDAY;
484       }
485       break;
486     }
487
488   /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
489      Just add the time of day and local offset from GMT, and we're done.  */
490
491   rule->change = t - rule->offset + rule->secs;
492   rule->computed_for = year;
493   return 1;
494 }
495
496
497 /* Figure out the correct timezone for TM and set `__tzname',
498    `__timezone', and `__daylight' accordingly.  Return nonzero on
499    success, zero on failure.  */
500 static int
501 internal_function
502 tz_compute (tm)
503      const struct tm *tm;
504 {
505   if (! compute_change (&tz_rules[0], 1900 + tm->tm_year)
506       || ! compute_change (&tz_rules[1], 1900 + tm->tm_year))
507     return 0;
508   /* We have to distinguish between northern and southern hemisphere.
509      For the later the daylight saving time ends in the next year.
510      It is easier to detect this after first computing the time for the
511      wrong year since now we simply can compare the times to switch.  */
512   if (tz_rules[0].change > tz_rules[1].change
513       && ! compute_change (&tz_rules[1], 1900 + tm->tm_year + 1))
514     return 0;
515
516   __daylight = tz_rules[0].offset != tz_rules[1].offset;
517   __timezone = -tz_rules[0].offset;
518   __tzname[0] = (char *) tz_rules[0].name;
519   __tzname[1] = (char *) tz_rules[1].name;
520
521   {
522     /* Keep __tzname_cur_max up to date.  */
523     size_t len0 = strlen (__tzname[0]);
524     size_t len1 = strlen (__tzname[1]);
525     if (len0 > __tzname_cur_max)
526       __tzname_cur_max = len0;
527     if (len1 > __tzname_cur_max)
528       __tzname_cur_max = len1;
529   }
530
531   return 1;
532 }
533 \f
534 /* Reinterpret the TZ environment variable and set `tzname'.  */
535 #undef tzset
536
537 void
538 __tzset (void)
539 {
540   __libc_lock_lock (tzset_lock);
541
542   tzset_internal (1);
543
544   if (!__use_tzfile)
545     {
546       /* Set `tzname'.  */
547       __tzname[0] = (char *) tz_rules[0].name;
548       __tzname[1] = (char *) tz_rules[1].name;
549     }
550
551   __libc_lock_unlock (tzset_lock);
552 }
553 weak_alias (__tzset, tzset)
554 \f
555 /* Return the `struct tm' representation of *TIMER in the local timezone.
556    Use local time if USE_LOCALTIME is nonzero, UTC otherwise.  */
557 struct tm *
558 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
559 {
560   long int leap_correction;
561   int leap_extra_secs;
562
563   if (timer == NULL)
564     {
565       __set_errno (EINVAL);
566       return NULL;
567     }
568
569   __libc_lock_lock (tzset_lock);
570
571   /* Update internal database according to current TZ setting.
572      POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
573      This is a good idea since this allows at least a bit more parallelism.
574      By analogy we apply the same rule to gmtime_r.  */
575   tzset_internal (tp == &_tmbuf);
576
577   if (__use_tzfile)
578     {
579       if (! __tzfile_compute (*timer, use_localtime,
580                               &leap_correction, &leap_extra_secs, tp))
581         tp = NULL;
582     }
583   else
584     {
585       if (! (__offtime (timer, 0, tp) && tz_compute (tp)))
586         tp = NULL;
587       leap_correction = 0L;
588       leap_extra_secs = 0;
589     }
590
591   if (tp)
592     {
593       if (use_localtime)
594         {
595           if (!__use_tzfile)
596             {
597               int isdst = (*timer >= tz_rules[0].change
598                            && *timer < tz_rules[1].change);
599               tp->tm_isdst = isdst;
600               tp->tm_zone = __tzname[isdst];
601               tp->tm_gmtoff = tz_rules[isdst].offset;
602             }
603         }
604       else
605         {
606           tp->tm_isdst = 0;
607           tp->tm_zone = "GMT";
608           tp->tm_gmtoff = 0L;
609         }
610
611       if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
612         tp->tm_sec += leap_extra_secs;
613       else
614         tp = NULL;
615     }
616
617   __libc_lock_unlock (tzset_lock);
618
619   return tp;
620 }