(tz_compute): Correct last patch and describe this in a comment.
[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 /* Defined in mktime.c.  */
29 extern const unsigned short int __mon_yday[2][13];
30
31 /* Defined in localtime.c.  */
32 extern struct tm _tmbuf;
33
34 #define NOID
35 #include <timezone/tzfile.h>
36
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,
41                                   struct tm *tp));
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));
45
46 char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
47 int __daylight = 0;
48 long int __timezone = 0L;
49
50 weak_alias (__tzname, tzname)
51 weak_alias (__daylight, daylight)
52 weak_alias (__timezone, timezone)
53
54 /* This locks all the state variables in tzfile.c and this file.  */
55 __libc_lock_define (static, tzset_lock)
56
57
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)
61
62
63 /* This structure contains all the information about a
64    timezone given in the POSIX standard TZ envariable.  */
65 typedef struct
66   {
67     const char *name;
68
69     /* When to change.  */
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.  */
73
74     long int offset;            /* Seconds east of GMT (west if < 0).  */
75
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.  */
80   } tz_rule;
81
82 /* tz_rules[0] is standard, tz_rules[1] is daylight.  */
83 static tz_rule tz_rules[2];
84
85
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))
88      internal_function;
89 static void tzset_internal __P ((int always)) internal_function;
90 \f
91 /* List of buffers containing time zone strings. */
92 struct tzstring_l
93 {
94   struct tzstring_l *next;
95   size_t len;  /* strlen(data) - doesn't count terminating NUL! */
96   char data[0];
97 };
98
99 struct tzstring_l *tzstring_list;
100
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. */
104 char *
105 __tzstring (const char *s)
106 {
107   char *p;
108   struct tzstring_l *t, *u, *new;
109   size_t len = strlen(s);
110
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)
114     if (len <= t->len)
115       {
116         p = &t->data[t->len - len];
117         if (strcmp (s, p) == 0)
118           return p;
119       }
120
121   /* Not found; allocate a new buffer. */
122   new = malloc (sizeof (struct tzstring_l) + len + 1);
123   if (!new)
124     return NULL;
125
126   new->next = NULL;
127   new->len = len;
128   strcpy (new->data, s);
129
130   if (u)
131     u->next = new;
132   else
133     tzstring_list = new;
134
135   return new->data;
136 }
137 \f
138 static char *old_tz = NULL;
139
140 /* Interpret the TZ envariable.  */
141 static void
142 internal_function
143 tzset_internal (always)
144      int always;
145 {
146   static int is_initialized = 0;
147   register const char *tz;
148   register size_t l;
149   char *tzbuf;
150   unsigned short int hh, mm, ss;
151   unsigned short int whichrule;
152
153   if (is_initialized && !always)
154     return;
155   is_initialized = 1;
156
157   /* Examine the TZ environment variable.  */
158   tz = getenv ("TZ");
159   if (tz == NULL)
160     /* No user specification; use the site-wide default.  */
161     tz = TZDEFAULT;
162   else if (*tz == '\0')
163     /* User specified the empty string; use UTC explicitly.  */
164     tz = "Universal";
165
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 == ':')
170     ++tz;
171
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.  */
175     return;
176
177   tz_rules[0].name = NULL;
178   tz_rules[1].name = NULL;
179
180   /* Save the value of `tz'.  */
181   if (old_tz != NULL)
182     free (old_tz);
183   old_tz = tz ? __strdup (tz) : NULL;
184
185   /* Try to read a data file.  */
186   __tzfile_read (tz);
187   if (__use_tzfile)
188     return;
189
190   /* No data file found.  Default to UTC if nothing specified.  */
191
192   if (tz == NULL || *tz == '\0')
193     {
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;
202       return;
203     }
204
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 = "";
208
209   /* Get the standard timezone name.  */
210   tzbuf = strdupa (tz);
211
212   if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 ||
213       (l = strlen (tzbuf)) < 3)
214     return;
215
216   tz_rules[0].name = __tzstring (tzbuf);
217
218   tz += l;
219
220   /* Figure out the standard offset from UTC.  */
221   if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
222     return;
223
224   if (*tz == '-' || *tz == '+')
225     tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
226   else
227     tz_rules[0].offset = -1L;
228   switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
229     {
230     default:
231       return;
232     case 1:
233       mm = 0;
234     case 2:
235       ss = 0;
236     case 3:
237       break;
238     }
239   tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
240                          (min (hh, 23) * 60 * 60));
241
242   for (l = 0; l < 3; ++l)
243     {
244       while (isdigit(*tz))
245         ++tz;
246       if (l < 2 && *tz == ':')
247         ++tz;
248     }
249
250   /* Get the DST timezone name (if any).  */
251   if (*tz != '\0')
252     {
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.  */
257
258       tz_rules[1].name = __tzstring (n);
259
260       tz += l;
261
262       /* Figure out the DST offset from GMT.  */
263       if (*tz == '-' || *tz == '+')
264         tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
265       else
266         tz_rules[1].offset = -1L;
267
268       switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
269         {
270         default:
271           /* Default to one hour later than standard time.  */
272           tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
273           break;
274
275         case 1:
276           mm = 0;
277         case 2:
278           ss = 0;
279         case 3:
280           tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
281                                  (min (hh, 23) * (60 * 60)));
282           break;
283         }
284       for (l = 0; l < 3; ++l)
285         {
286           while (isdigit (*tz))
287             ++tz;
288           if (l < 2 && *tz == ':')
289             ++tz;
290         }
291       if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
292         {
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);
296           if (__use_tzfile)
297             {
298               free (old_tz);
299               old_tz = NULL;
300               return;
301             }
302         }
303     }
304   else
305     {
306       /* There is no DST.  */
307       tz_rules[1].name = tz_rules[0].name;
308       tz_rules[1].offset = tz_rules[0].offset;
309       goto out;
310     }
311
312  done_names:
313   /* Figure out the standard <-> DST rules.  */
314   for (whichrule = 0; whichrule < 2; ++whichrule)
315     {
316       register tz_rule *tzr = &tz_rules[whichrule];
317
318       /* Ignore comma to support string following the incorrect
319          specification in early POSIX.1 printings.  */
320       tz += *tz == ',';
321
322       /* Get the date of the change.  */
323       if (*tz == 'J' || isdigit (*tz))
324         {
325           char *end;
326           tzr->type = *tz == 'J' ? J1 : J0;
327           if (tzr->type == J1 && !isdigit (*++tz))
328             goto out;
329           tzr->d = (unsigned short int) strtoul (tz, &end, 10);
330           if (end == tz || tzr->d > 365)
331             goto out;
332           else if (tzr->type == J1 && tzr->d == 0)
333             goto out;
334           tz = end;
335         }
336       else if (*tz == 'M')
337         {
338           int n;
339           tzr->type = M;
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)
344             goto out;
345           tz += n;
346         }
347       else if (*tz == '\0')
348         {
349           /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0".  */
350           tzr->type = M;
351           if (tzr == &tz_rules[0])
352             {
353               tzr->m = 4;
354               tzr->n = 1;
355               tzr->d = 0;
356             }
357           else
358             {
359               tzr->m = 10;
360               tzr->n = 5;
361               tzr->d = 0;
362             }
363         }
364       else
365         goto out;
366
367       if (*tz != '\0' && *tz != '/' && *tz != ',')
368         goto out;
369       else if (*tz == '/')
370         {
371           /* Get the time of day of the change.  */
372           ++tz;
373           if (*tz == '\0')
374             goto out;
375           switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
376             {
377             default:
378               hh = 2;           /* Default to 2:00 AM.  */
379             case 1:
380               mm = 0;
381             case 2:
382               ss = 0;
383             case 3:
384               break;
385             }
386           for (l = 0; l < 3; ++l)
387             {
388               while (isdigit (*tz))
389                 ++tz;
390               if (l < 2 && *tz == ':')
391                 ++tz;
392             }
393           tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
394         }
395       else
396         /* Default to 2:00 AM.  */
397         tzr->secs = 2 * 60 * 60;
398
399       tzr->computed_for = -1;
400     }
401
402  out:
403   /* We know the offset now, set `__timezone'.  */
404   __timezone = -tz_rules[0].offset;
405 }
406 \f
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;
411
412 long int
413 __tzname_max ()
414 {
415   __libc_lock_lock (tzset_lock);
416
417   tzset_internal (0);
418
419   __libc_lock_unlock (tzset_lock);
420
421   return __tzname_cur_max;
422 }
423 \f
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.  */
428 static int
429 internal_function
430 compute_change (rule, year)
431      tz_rule *rule;
432      int year;
433 {
434   register time_t t;
435   int y;
436
437   if (year != -1 && rule->computed_for == year)
438     /* Operations on times in 1969 will be slower.  Oh well.  */
439     return 1;
440
441   /* First set T to January 1st, 0:00:00 GMT in YEAR.  */
442   t = 0;
443   for (y = 1970; y < year; ++y)
444     t += SECSPERDAY * (__isleap (y) ? 366 : 365);
445
446   switch (rule->type)
447     {
448     case J1:
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))
455         t += SECSPERDAY;
456       break;
457
458     case J0:
459       /* n - Day of year.
460          Just add SECSPERDAY times the day number to the time of Jan 1st.  */
461       t += rule->d * SECSPERDAY;
462       break;
463
464     case M:
465       /* Mm.n.d - Nth "Dth day" of month M.  */
466       {
467         unsigned int i;
468         int d, m1, yy0, yy1, yy2, dow;
469         const unsigned short int *myday =
470           &__mon_yday[__isleap (year)][rule->m];
471
472         /* First add SECSPERDAY for each day in months before M.  */
473         t += myday[-1] * SECSPERDAY;
474
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;
478         yy1 = yy0 / 100;
479         yy2 = yy0 % 100;
480         dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
481         if (dow < 0)
482           dow += 7;
483
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.  */
486         d = rule->d - dow;
487         if (d < 0)
488           d += 7;
489         for (i = 1; i < rule->n; ++i)
490           {
491             if (d + 7 >= (int) myday[0] - myday[-1])
492               break;
493             d += 7;
494           }
495
496         /* D is the day-of-month (zero-origin) of the day we want.  */
497         t += d * SECSPERDAY;
498       }
499       break;
500     }
501
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.  */
504
505   rule->change = t - rule->offset + rule->secs;
506   rule->computed_for = year;
507   return 1;
508 }
509
510
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.  */
514 static int
515 internal_function
516 tz_compute (timer, tm)
517      time_t timer;
518      const struct tm *tm;
519 {
520   if (! compute_change (&tz_rules[0], 1900 + tm->tm_year)
521       || ! compute_change (&tz_rules[1], 1900 + tm->tm_year))
522     return 0;
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))
529     return 0;
530
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;
535
536   {
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;
544   }
545
546   return 1;
547 }
548 \f
549 /* Reinterpret the TZ environment variable and set `tzname'.  */
550 #undef tzset
551
552 void
553 __tzset (void)
554 {
555   __libc_lock_lock (tzset_lock);
556
557   tzset_internal (1);
558
559   if (!__use_tzfile)
560     {
561       /* Set `tzname'.  */
562       __tzname[0] = (char *) tz_rules[0].name;
563       __tzname[1] = (char *) tz_rules[1].name;
564     }
565
566   __libc_lock_unlock (tzset_lock);
567 }
568 weak_alias (__tzset, tzset)
569 \f
570 /* Return the `struct tm' representation of *TIMER in the local timezone.
571    Use local time if USE_LOCALTIME is nonzero, UTC otherwise.  */
572 struct tm *
573 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
574 {
575   long int leap_correction;
576   int leap_extra_secs;
577
578   if (timer == NULL)
579     {
580       __set_errno (EINVAL);
581       return NULL;
582     }
583
584   __libc_lock_lock (tzset_lock);
585
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);
591
592   if (__use_tzfile)
593     {
594       if (! __tzfile_compute (*timer, use_localtime,
595                               &leap_correction, &leap_extra_secs, tp))
596         tp = NULL;
597     }
598   else
599     {
600       if (! (__offtime (timer, 0, tp) && tz_compute (*timer, tp)))
601         tp = NULL;
602       leap_correction = 0L;
603       leap_extra_secs = 0;
604     }
605
606   if (tp)
607     {
608       if (use_localtime)
609         {
610           if (!__use_tzfile)
611             {
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;
617             }
618         }
619       else
620         {
621           tp->tm_isdst = 0;
622           tp->tm_zone = "GMT";
623           tp->tm_gmtoff = 0L;
624         }
625
626       if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
627         tp->tm_sec += leap_extra_secs;
628       else
629         tp = NULL;
630     }
631
632   __libc_lock_unlock (tzset_lock);
633
634   return tp;
635 }