Back to index

glibc  2.9
tzset.c
Go to the documentation of this file.
00001 /* Copyright (C) 1991-2002,2003,2004,2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003 
00004    The GNU C Library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Lesser General Public
00006    License as published by the Free Software Foundation; either
00007    version 2.1 of the License, or (at your option) any later version.
00008 
00009    The GNU C Library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Lesser General Public License for more details.
00013 
00014    You should have received a copy of the GNU Lesser General Public
00015    License along with the GNU C Library; if not, write to the Free
00016    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00017    02111-1307 USA.  */
00018 
00019 #include <ctype.h>
00020 #include <errno.h>
00021 #include <bits/libc-lock.h>
00022 #include <stddef.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <time.h>
00027 
00028 
00029 #define NOID
00030 #include <timezone/tzfile.h>
00031 
00032 char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
00033 int __daylight = 0;
00034 long int __timezone = 0L;
00035 
00036 weak_alias (__tzname, tzname)
00037 weak_alias (__daylight, daylight)
00038 weak_alias (__timezone, timezone)
00039 
00040 /* This locks all the state variables in tzfile.c and this file.  */
00041 __libc_lock_define_initialized (static, tzset_lock)
00042 
00043 
00044 #define       min(a, b)     ((a) < (b) ? (a) : (b))
00045 #define       max(a, b)     ((a) > (b) ? (a) : (b))
00046 #define       sign(x)              ((x) < 0 ? -1 : 1)
00047 
00048 
00049 /* This structure contains all the information about a
00050    timezone given in the POSIX standard TZ envariable.  */
00051 typedef struct
00052   {
00053     const char *name;
00054 
00055     /* When to change.  */
00056     enum { J0, J1, M } type;       /* Interpretation of:  */
00057     unsigned short int m, n, d;    /* Month, week, day.  */
00058     unsigned int secs;             /* Time of day.  */
00059 
00060     long int offset;        /* Seconds east of GMT (west if < 0).  */
00061 
00062     /* We cache the computed time of change for a
00063        given year so we don't have to recompute it.  */
00064     time_t change;   /* When to change to this zone.  */
00065     int computed_for;       /* Year above is computed for.  */
00066   } tz_rule;
00067 
00068 /* tz_rules[0] is standard, tz_rules[1] is daylight.  */
00069 static tz_rule tz_rules[2];
00070 
00071 
00072 static void compute_change (tz_rule *rule, int year) __THROW internal_function;
00073 static void tzset_internal (int always, int explicit)
00074      __THROW internal_function;
00075 
00076 /* List of buffers containing time zone strings. */
00077 struct tzstring_l
00078 {
00079   struct tzstring_l *next;
00080   size_t len;  /* strlen(data) - doesn't count terminating NUL! */
00081   char data[0];
00082 };
00083 
00084 static struct tzstring_l *tzstring_list;
00085 
00086 /* Allocate a permanent home for S.  It will never be moved or deallocated,
00087    but may share space with other strings.
00088    Don't modify the returned string. */
00089 char *
00090 __tzstring (const char *s)
00091 {
00092   char *p;
00093   struct tzstring_l *t, *u, *new;
00094   size_t len = strlen (s);
00095 
00096   /* Walk the list and look for a match.  If this string is the same
00097      as the end of an already-allocated string, it can share space. */
00098   for (u = t = tzstring_list; t; u = t, t = t->next)
00099     if (len <= t->len)
00100       {
00101        p = &t->data[t->len - len];
00102        if (strcmp (s, p) == 0)
00103          return p;
00104       }
00105 
00106   /* Not found; allocate a new buffer. */
00107   new = malloc (sizeof (struct tzstring_l) + len + 1);
00108   if (!new)
00109     return NULL;
00110 
00111   new->next = NULL;
00112   new->len = len;
00113   strcpy (new->data, s);
00114 
00115   if (u)
00116     u->next = new;
00117   else
00118     tzstring_list = new;
00119 
00120   return new->data;
00121 }
00122 
00123 /* Maximum length of a timezone name.  tzset_internal keeps this up to date
00124    (never decreasing it) when ! __use_tzfile.
00125    tzfile.c keeps it up to date when __use_tzfile.  */
00126 size_t __tzname_cur_max;
00127 
00128 long int
00129 __tzname_max ()
00130 {
00131   __libc_lock_lock (tzset_lock);
00132 
00133   tzset_internal (0, 0);
00134 
00135   __libc_lock_unlock (tzset_lock);
00136 
00137   return __tzname_cur_max;
00138 }
00139 
00140 static char *old_tz;
00141 
00142 static void
00143 internal_function
00144 update_vars (void)
00145 {
00146   __daylight = tz_rules[0].offset != tz_rules[1].offset;
00147   __timezone = -tz_rules[0].offset;
00148   __tzname[0] = (char *) tz_rules[0].name;
00149   __tzname[1] = (char *) tz_rules[1].name;
00150 
00151   /* Keep __tzname_cur_max up to date.  */
00152   size_t len0 = strlen (__tzname[0]);
00153   size_t len1 = strlen (__tzname[1]);
00154   if (len0 > __tzname_cur_max)
00155     __tzname_cur_max = len0;
00156   if (len1 > __tzname_cur_max)
00157     __tzname_cur_max = len1;
00158 }
00159 
00160 /* Parse the POSIX TZ-style string.  */
00161 void
00162 __tzset_parse_tz (tz)
00163      const char *tz;
00164 {
00165   register size_t l;
00166   char *tzbuf;
00167   unsigned short int hh, mm, ss;
00168   unsigned short int whichrule;
00169 
00170   /* Clear out old state and reset to unnamed UTC.  */
00171   memset (tz_rules, 0, sizeof tz_rules);
00172   tz_rules[0].name = tz_rules[1].name = "";
00173 
00174   /* Get the standard timezone name.  */
00175   tzbuf = strdupa (tz);
00176 
00177   if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 ||
00178       (l = strlen (tzbuf)) < 3)
00179     goto out;
00180 
00181   tz_rules[0].name = __tzstring (tzbuf);
00182 
00183   tz += l;
00184 
00185   /* Figure out the standard offset from UTC.  */
00186   if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
00187     goto out;
00188 
00189   if (*tz == '-' || *tz == '+')
00190     tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
00191   else
00192     tz_rules[0].offset = -1L;
00193   switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
00194     {
00195     default:
00196       tz_rules[0].offset = 0;
00197       goto out;
00198     case 1:
00199       mm = 0;
00200     case 2:
00201       ss = 0;
00202     case 3:
00203       break;
00204     }
00205   tz_rules[0].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
00206                       (min (hh, 24) * 60 * 60));
00207 
00208   for (l = 0; l < 3; ++l)
00209     {
00210       while (isdigit(*tz))
00211        ++tz;
00212       if (l < 2 && *tz == ':')
00213        ++tz;
00214     }
00215 
00216   /* Get the DST timezone name (if any).  */
00217   if (*tz != '\0')
00218     {
00219       char *n = tzbuf + strlen (tzbuf) + 1;
00220       if (sscanf (tz, "%[^0-9,+-]", n) != 1 ||
00221          (l = strlen (n)) < 3)
00222        goto done_names;     /* Punt on name, set up the offsets.  */
00223 
00224       tz_rules[1].name = __tzstring (n);
00225 
00226       tz += l;
00227 
00228       /* Figure out the DST offset from GMT.  */
00229       if (*tz == '-' || *tz == '+')
00230        tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
00231       else
00232        tz_rules[1].offset = -1L;
00233 
00234       switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
00235        {
00236        default:
00237          /* Default to one hour later than standard time.  */
00238          tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
00239          break;
00240 
00241        case 1:
00242          mm = 0;
00243        case 2:
00244          ss = 0;
00245        case 3:
00246          tz_rules[1].offset *= (min (ss, 59) + (min (mm, 59) * 60) +
00247                              (min (hh, 23) * (60 * 60)));
00248          break;
00249        }
00250       for (l = 0; l < 3; ++l)
00251        {
00252          while (isdigit (*tz))
00253            ++tz;
00254          if (l < 2 && *tz == ':')
00255            ++tz;
00256        }
00257       if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
00258        {
00259          /* There is no rule.  See if there is a default rule file.  */
00260          __tzfile_default (tz_rules[0].name, tz_rules[1].name,
00261                          tz_rules[0].offset, tz_rules[1].offset);
00262          if (__use_tzfile)
00263            {
00264              free (old_tz);
00265              old_tz = NULL;
00266              return;
00267            }
00268        }
00269     }
00270   else
00271     {
00272       /* There is no DST.  */
00273       tz_rules[1].name = tz_rules[0].name;
00274       tz_rules[1].offset = tz_rules[0].offset;
00275       goto out;
00276     }
00277 
00278  done_names:
00279   /* Figure out the standard <-> DST rules.  */
00280   for (whichrule = 0; whichrule < 2; ++whichrule)
00281     {
00282       register tz_rule *tzr = &tz_rules[whichrule];
00283 
00284       /* Ignore comma to support string following the incorrect
00285         specification in early POSIX.1 printings.  */
00286       tz += *tz == ',';
00287 
00288       /* Get the date of the change.  */
00289       if (*tz == 'J' || isdigit (*tz))
00290        {
00291          char *end;
00292          tzr->type = *tz == 'J' ? J1 : J0;
00293          if (tzr->type == J1 && !isdigit (*++tz))
00294            goto out;
00295          tzr->d = (unsigned short int) strtoul (tz, &end, 10);
00296          if (end == tz || tzr->d > 365)
00297            goto out;
00298          else if (tzr->type == J1 && tzr->d == 0)
00299            goto out;
00300          tz = end;
00301        }
00302       else if (*tz == 'M')
00303        {
00304          int n;
00305          tzr->type = M;
00306          if (sscanf (tz, "M%hu.%hu.%hu%n",
00307                     &tzr->m, &tzr->n, &tzr->d, &n) != 3 ||
00308              tzr->m < 1 || tzr->m > 12 ||
00309              tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
00310            goto out;
00311          tz += n;
00312        }
00313       else if (*tz == '\0')
00314        {
00315          /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0".  */
00316          tzr->type = M;
00317          if (tzr == &tz_rules[0])
00318            {
00319              tzr->m = 4;
00320              tzr->n = 1;
00321              tzr->d = 0;
00322            }
00323          else
00324            {
00325              tzr->m = 10;
00326              tzr->n = 5;
00327              tzr->d = 0;
00328            }
00329        }
00330       else
00331        goto out;
00332 
00333       if (*tz != '\0' && *tz != '/' && *tz != ',')
00334        goto out;
00335       else if (*tz == '/')
00336        {
00337          /* Get the time of day of the change.  */
00338          ++tz;
00339          if (*tz == '\0')
00340            goto out;
00341          switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss))
00342            {
00343            default:
00344              hh = 2;        /* Default to 2:00 AM.  */
00345            case 1:
00346              mm = 0;
00347            case 2:
00348              ss = 0;
00349            case 3:
00350              break;
00351            }
00352          for (l = 0; l < 3; ++l)
00353            {
00354              while (isdigit (*tz))
00355               ++tz;
00356              if (l < 2 && *tz == ':')
00357               ++tz;
00358            }
00359          tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
00360        }
00361       else
00362        /* Default to 2:00 AM.  */
00363        tzr->secs = 2 * 60 * 60;
00364 
00365       tzr->computed_for = -1;
00366     }
00367 
00368  out:
00369   update_vars ();
00370 }
00371 
00372 /* Interpret the TZ envariable.  */
00373 static void
00374 internal_function
00375 tzset_internal (always, explicit)
00376      int always;
00377      int explicit;
00378 {
00379   static int is_initialized;
00380   register const char *tz;
00381 
00382   if (is_initialized && !always)
00383     return;
00384   is_initialized = 1;
00385 
00386   /* Examine the TZ environment variable.  */
00387   tz = getenv ("TZ");
00388   if (tz == NULL && !explicit)
00389     /* Use the site-wide default.  This is a file name which means we
00390        would not see changes to the file if we compare only the file
00391        name for change.  We want to notice file changes if tzset() has
00392        been called explicitly.  Leave TZ as NULL in this case.  */
00393     tz = TZDEFAULT;
00394   if (tz && *tz == '\0')
00395     /* User specified the empty string; use UTC explicitly.  */
00396     tz = "Universal";
00397 
00398   /* A leading colon means "implementation defined syntax".
00399      We ignore the colon and always use the same algorithm:
00400      try a data file, and if none exists parse the 1003.1 syntax.  */
00401   if (tz && *tz == ':')
00402     ++tz;
00403 
00404   /* Check whether the value changed since the last run.  */
00405   if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
00406     /* No change, simply return.  */
00407     return;
00408 
00409   if (tz == NULL)
00410     /* No user specification; use the site-wide default.  */
00411     tz = TZDEFAULT;
00412 
00413   tz_rules[0].name = NULL;
00414   tz_rules[1].name = NULL;
00415 
00416   /* Save the value of `tz'.  */
00417   free (old_tz);
00418   old_tz = tz ? __strdup (tz) : NULL;
00419 
00420   /* Try to read a data file.  */
00421   __tzfile_read (tz, 0, NULL);
00422   if (__use_tzfile)
00423     return;
00424 
00425   /* No data file found.  Default to UTC if nothing specified.  */
00426 
00427   if (tz == NULL || *tz == '\0'
00428       || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))
00429     {
00430       tz_rules[0].name = tz_rules[1].name = "UTC";
00431       tz_rules[0].type = tz_rules[1].type = J0;
00432       tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
00433       tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
00434       tz_rules[0].secs = tz_rules[1].secs = 0;
00435       tz_rules[0].offset = tz_rules[1].offset = 0L;
00436       tz_rules[0].change = tz_rules[1].change = (time_t) -1;
00437       tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
00438       update_vars ();
00439       return;
00440     }
00441 
00442   __tzset_parse_tz (tz);
00443 }
00444 
00445 /* Figure out the exact time (as a time_t) in YEAR
00446    when the change described by RULE will occur and
00447    put it in RULE->change, saving YEAR in RULE->computed_for.  */
00448 static void
00449 internal_function
00450 compute_change (rule, year)
00451      tz_rule *rule;
00452      int year;
00453 {
00454   register time_t t;
00455 
00456   if (year != -1 && rule->computed_for == year)
00457     /* Operations on times in 2 BC will be slower.  Oh well.  */
00458     return;
00459 
00460   /* First set T to January 1st, 0:00:00 GMT in YEAR.  */
00461   if (year > 1970)
00462     t = ((year - 1970) * 365
00463         + /* Compute the number of leapdays between 1970 and YEAR
00464              (exclusive).  There is a leapday every 4th year ...  */
00465         + ((year - 1) / 4 - 1970 / 4)
00466         /* ... except every 100th year ... */
00467         - ((year - 1) / 100 - 1970 / 100)
00468         /* ... but still every 400th year.  */
00469         + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY;
00470   else
00471     t = 0;
00472 
00473   switch (rule->type)
00474     {
00475     case J1:
00476       /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
00477         In non-leap years, or if the day number is 59 or less, just
00478         add SECSPERDAY times the day number-1 to the time of
00479         January 1, midnight, to get the day.  */
00480       t += (rule->d - 1) * SECSPERDAY;
00481       if (rule->d >= 60 && __isleap (year))
00482        t += SECSPERDAY;
00483       break;
00484 
00485     case J0:
00486       /* n - Day of year.
00487         Just add SECSPERDAY times the day number to the time of Jan 1st.  */
00488       t += rule->d * SECSPERDAY;
00489       break;
00490 
00491     case M:
00492       /* Mm.n.d - Nth "Dth day" of month M.  */
00493       {
00494        unsigned int i;
00495        int d, m1, yy0, yy1, yy2, dow;
00496        const unsigned short int *myday =
00497          &__mon_yday[__isleap (year)][rule->m];
00498 
00499        /* First add SECSPERDAY for each day in months before M.  */
00500        t += myday[-1] * SECSPERDAY;
00501 
00502        /* Use Zeller's Congruence to get day-of-week of first day of month. */
00503        m1 = (rule->m + 9) % 12 + 1;
00504        yy0 = (rule->m <= 2) ? (year - 1) : year;
00505        yy1 = yy0 / 100;
00506        yy2 = yy0 % 100;
00507        dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
00508        if (dow < 0)
00509          dow += 7;
00510 
00511        /* DOW is the day-of-week of the first day of the month.  Get the
00512           day-of-month (zero-origin) of the first DOW day of the month.  */
00513        d = rule->d - dow;
00514        if (d < 0)
00515          d += 7;
00516        for (i = 1; i < rule->n; ++i)
00517          {
00518            if (d + 7 >= (int) myday[0] - myday[-1])
00519              break;
00520            d += 7;
00521          }
00522 
00523        /* D is the day-of-month (zero-origin) of the day we want.  */
00524        t += d * SECSPERDAY;
00525       }
00526       break;
00527     }
00528 
00529   /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
00530      Just add the time of day and local offset from GMT, and we're done.  */
00531 
00532   rule->change = t - rule->offset + rule->secs;
00533   rule->computed_for = year;
00534 }
00535 
00536 
00537 /* Figure out the correct timezone for TM and set `__tzname',
00538    `__timezone', and `__daylight' accordingly.  */
00539 void
00540 internal_function
00541 __tz_compute (timer, tm, use_localtime)
00542      time_t timer;
00543      struct tm *tm;
00544      int use_localtime;
00545 {
00546   compute_change (&tz_rules[0], 1900 + tm->tm_year);
00547   compute_change (&tz_rules[1], 1900 + tm->tm_year);
00548 
00549   if (use_localtime)
00550     {
00551       int isdst;
00552 
00553       /* We have to distinguish between northern and southern
00554         hemisphere.  For the latter the daylight saving time
00555         ends in the next year.  */
00556       if (__builtin_expect (tz_rules[0].change
00557                          > tz_rules[1].change, 0))
00558        isdst = (timer < tz_rules[1].change
00559                || timer >= tz_rules[0].change);
00560       else
00561        isdst = (timer >= tz_rules[0].change
00562                && timer < tz_rules[1].change);
00563       tm->tm_isdst = isdst;
00564       tm->tm_zone = __tzname[isdst];
00565       tm->tm_gmtoff = tz_rules[isdst].offset;
00566     }
00567 }
00568 
00569 /* Reinterpret the TZ environment variable and set `tzname'.  */
00570 #undef tzset
00571 
00572 void
00573 __tzset (void)
00574 {
00575   __libc_lock_lock (tzset_lock);
00576 
00577   tzset_internal (1, 1);
00578 
00579   if (!__use_tzfile)
00580     {
00581       /* Set `tzname'.  */
00582       __tzname[0] = (char *) tz_rules[0].name;
00583       __tzname[1] = (char *) tz_rules[1].name;
00584     }
00585 
00586   __libc_lock_unlock (tzset_lock);
00587 }
00588 weak_alias (__tzset, tzset)
00589 
00590 /* Return the `struct tm' representation of *TIMER in the local timezone.
00591    Use local time if USE_LOCALTIME is nonzero, UTC otherwise.  */
00592 struct tm *
00593 __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
00594 {
00595   long int leap_correction;
00596   int leap_extra_secs;
00597 
00598   if (timer == NULL)
00599     {
00600       __set_errno (EINVAL);
00601       return NULL;
00602     }
00603 
00604   __libc_lock_lock (tzset_lock);
00605 
00606   /* Update internal database according to current TZ setting.
00607      POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
00608      This is a good idea since this allows at least a bit more parallelism.  */
00609   tzset_internal (tp == &_tmbuf && use_localtime, 1);
00610 
00611   if (__use_tzfile)
00612     __tzfile_compute (*timer, use_localtime, &leap_correction,
00613                     &leap_extra_secs, tp);
00614   else
00615     {
00616       if (! __offtime (timer, 0, tp))
00617        tp = NULL;
00618       else
00619        __tz_compute (*timer, tp, use_localtime);
00620       leap_correction = 0L;
00621       leap_extra_secs = 0;
00622     }
00623 
00624   if (tp)
00625     {
00626       if (! use_localtime)
00627        {
00628          tp->tm_isdst = 0;
00629          tp->tm_zone = "GMT";
00630          tp->tm_gmtoff = 0L;
00631        }
00632 
00633       if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
00634         tp->tm_sec += leap_extra_secs;
00635       else
00636        tp = NULL;
00637     }
00638 
00639   __libc_lock_unlock (tzset_lock);
00640 
00641   return tp;
00642 }
00643 
00644 
00645 libc_freeres_fn (free_mem)
00646 {
00647   while (tzstring_list != NULL)
00648     {
00649       struct tzstring_l *old = tzstring_list;
00650 
00651       tzstring_list = tzstring_list->next;
00652       free (old);
00653     }
00654   free (old_tz);
00655   old_tz = NULL;
00656 }