static int tzparse()

in libs/libc/time/lib_localtime.c [1495:1895]


static int tzparse(FAR const char *name, FAR struct state_s *sp,
                   FAR struct state_s *basep)
{
  FAR const char *stdname;
  FAR const char *dstname;
  size_t stdlen;
  size_t dstlen;
  size_t charcnt;
  int_fast32_t stdoffset;
  int_fast32_t dstoffset;
  FAR char *cp;
  int load_ok;
  time_t atlo = TIME_T_MIN;
  time_t leaplo = TIME_T_MIN;

  stdname = name;
  if (*name == '<')
    {
      name++;
      stdname = name;
      name = getqzname(name, '>');
      if (*name != '>')
        {
          return -1;
        }

      stdlen = name - stdname;
      name++;
    }
  else
    {
      name = getzname(name);
      stdlen = name - stdname;
    }

  if (stdlen == 0)
    {
      return -1;
    }

  name = getoffset(name, &stdoffset);
  if (name == NULL)
    {
      return -1;
    }

  charcnt = stdlen + 1;
  if (sizeof(sp->chars) < charcnt)
    {
      return -1;
    }

  if (basep)
    {
      if (0 < basep->timecnt)
        {
          atlo = basep->ats[basep->timecnt - 1];
        }

      load_ok = -1;
      sp->leapcnt = basep->leapcnt;
      memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof(*sp->lsis));
    }
  else
    {
      load_ok = tzload(TZDEFRULES, sp, FALSE);
      if (load_ok != 0)
        {
          sp->leapcnt = 0; /* so, we're off a little */
        }
    }

  if (sp->leapcnt > 0)
    {
      leaplo = sp->lsis[sp->leapcnt - 1].ls_trans;
    }

  if (*name != '\0')
    {
      if (*name == '<')
        {
          dstname = ++name;
          name = getqzname(name, '>');
          if (*name != '>')
            {
              return -1;
            }

          dstlen = name - dstname;
          name++;
        }
      else
        {
          dstname = name;
          name = getzname(name);
          dstlen = name - dstname;      /* length of DST zone name */
        }

      if (dstlen == 0)
        {
          return -1;
        }

      charcnt += dstlen + 1;
      if (sizeof(sp->chars) < charcnt)
        {
          return -1;
        }

      if (*name != '\0' && *name != ',' && *name != ';')
        {
          name = getoffset(name, &dstoffset);
          if (name == NULL)
            {
              return -1;
            }
        }
      else
        {
          dstoffset = stdoffset - SECSPERHOUR;
        }

      if (*name == '\0' && load_ok != 0)
        {
          name = TZDEFRULESTRING;
        }

      if (*name == ',' || *name == ';')
        {
          struct rule_s start;
          struct rule_s end;
          int year;
          int yearlim;
          int yearbeg;
          int timecnt;
          time_t janfirst;
          int_fast32_t janoffset = 0;

          ++name;
          if ((name = getrule(name, &start)) == NULL)
            {
              return -1;
            }

          if (*name++ != ',')
            {
              return -1;
            }

          if ((name = getrule(name, &end)) == NULL)
            {
              return -1;
            }

          if (*name != '\0')
            {
              return -1;
            }

          sp->typecnt = 2;      /* standard time and DST */

          /* Two transitions per year, from EPOCH_YEAR forward */

          init_ttinfo(&sp->ttis[0], -stdoffset, FALSE, 0);
          init_ttinfo(&sp->ttis[1], -dstoffset, TRUE, stdlen + 1);
          sp->defaulttype = 0;
          timecnt = 0;
          janfirst = 0;
          yearbeg = EPOCH_YEAR;

          do
            {
              int_fast32_t yearsecs;

              yearsecs = g_year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
              yearbeg--;
              if (increment_overflow_time(&janfirst, -yearsecs))
                {
                  janoffset = -yearsecs;
                  break;
                }
            }
          while (atlo < janfirst && (EPOCH_YEAR -
                                     YEARSPERREPEAT / 2 < yearbeg));

          while (true)
            {
              int_fast32_t yearsecs;
              int yearbeg1 = yearbeg;
              time_t janfirst1 = janfirst;

              yearsecs = g_year_lengths[isleap(yearbeg)] * SECSPERDAY;
              if (increment_overflow_time(&janfirst1, yearsecs) ||
                  increment_overflow(&yearbeg1, 1) || atlo <= janfirst1)
                {
                  break;
                }

              yearbeg = yearbeg1;
              janfirst = janfirst1;
            }

          yearlim = yearbeg;
          if (increment_overflow(&yearlim, YEARSPERREPEAT + 1))
            {
              yearlim = INT_MAX;
            }

          for (year = yearbeg; year < yearlim; year++)
            {
              int_fast32_t
                starttime  = transtime(year, &start, stdoffset);
              int_fast32_t
                endtime    = transtime(year, &end, dstoffset);
              int_fast32_t
                yearsecs   = (g_year_lengths[isleap(year)] * SECSPERDAY);

              int reversed = endtime < starttime;
              if (reversed)
                {
                  int_fast32_t swap = starttime;
                  starttime = endtime;
                  endtime = swap;
                }

              if (reversed ||
                   (starttime < endtime &&
                    (endtime - starttime < yearsecs)))
                {
                  if (TZ_MAX_TIMES - 2 < timecnt)
                    {
                      break;
                    }

                  sp->ats[timecnt] = janfirst;
                  if (!increment_overflow_time(&sp->ats[timecnt],
                                               janoffset + starttime) &&
                      atlo <= sp->ats[timecnt])
                    {
                      sp->types[timecnt++] = !reversed;
                    }

                  sp->ats[timecnt] = janfirst;
                  if (!increment_overflow_time(&sp->ats[timecnt],
                                               janoffset + endtime) &&
                      atlo <= sp->ats[timecnt])
                    {
                      sp->types[timecnt++] = reversed;
                    }
                }

              if (endtime < leaplo)
                {
                  yearlim = year;
                  if (increment_overflow(&yearlim, YEARSPERREPEAT + 1))
                    {
                      yearlim = INT_MAX;
                    }
                }

              if (increment_overflow_time(&janfirst, janoffset + yearsecs))
                {
                  break;
                }

              janoffset = 0;
            }

          sp->timecnt = timecnt;
          if (!timecnt)
            {
              sp->ttis[0] = sp->ttis[1];
              sp->typecnt = 1; /* Perpetual DST.  */
            }
          else if (YEARSPERREPEAT < year - yearbeg)
            {
              sp->goback = sp->goahead = TRUE;
            }
        }
      else
        {
          int_fast32_t theirstdoffset;
          int_fast32_t theirdstoffset;
          int_fast32_t theiroffset;
          int isdst;
          int i;
          int j;

          if (*name != '\0')
            {
              return -1;
            }

          /* Initial value of theirstdoffset */

          theirstdoffset = 0;
          for (i = 0; i < sp->timecnt; ++i)
            {
              j = sp->types[i];
              if (!sp->ttis[j].tt_isdst)
                {
                  theirstdoffset = -sp->ttis[j].tt_utoff;
                  break;
                }
            }

          theirdstoffset = 0;
          for (i = 0; i < sp->timecnt; ++i)
            {
              j = sp->types[i];
              if (!sp->ttis[j].tt_isdst)
                {
                  theirdstoffset = -sp->ttis[j].tt_utoff;
                  break;
                }
            }

          /* Initially we're assumed to be in standard time */

          isdst = -1;

          /* Now juggle transition times and types
           * tracking offsets as you do.
           */

          for (i = 0; i < sp->timecnt; ++i)
            {
              j = sp->types[i];
              sp->types[i] = sp->ttis[j].tt_isdst;
              if (sp->ttis[j].tt_ttisut)
                {
                  /* No adjustment to transition time */
                }
              else
                {
                  /* If daylight saving time is in
                   * effect, and the transition time was
                   * not specified as standard time, add
                   * the daylight saving time offset to
                   * the transition time; otherwise, add
                   * the standard time offset to the
                   * transition time.
                   */

                  /* Transitions from DST to DDST
                   * will effectively disappear since
                   * POSIX provides for only one DST
                   * offset.
                   */

                  if (isdst && !sp->ttis[j].tt_ttisstd)
                    {
                      sp->ats[i] += dstoffset - theirdstoffset;
                    }
                  else
                    {
                      sp->ats[i] += stdoffset - theirstdoffset;
                    }
                }

              theiroffset = -sp->ttis[j].tt_utoff;
              if (sp->ttis[j].tt_isdst)
                {
                  theirstdoffset = theiroffset;
                }
              else
                {
                  theirdstoffset = theiroffset;
                }
            }

          /* Finally, fill in ttis */

          init_ttinfo(&sp->ttis[0], -stdoffset, FALSE, 0);
          init_ttinfo(&sp->ttis[1], -dstoffset, TRUE, stdlen + 1);
          sp->typecnt = 2;
          sp->defaulttype = 0;
        }
    }
  else
    {
      dstlen = 0;
      sp->typecnt = 1;          /* only standard time */
      sp->timecnt = 0;
      init_ttinfo(&sp->ttis[0], -stdoffset, FALSE, 0);
      sp->defaulttype = 0;
    }

  sp->charcnt = charcnt;
  cp = sp->chars;
  memcpy(cp, stdname, stdlen);
  cp += stdlen;
  *cp++ = '\0';
  if (dstlen != 0)
    {
      memcpy(cp, dstname, dstlen);
      *(cp + dstlen) = '\0';
    }

  return 0;
}