(compute_change): Replace slow loop to compute T by simple algorithm.
[kopensolaris-gnu/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 }