Skip to content
getdate.c.cvs 63.3 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
    { "idlw",	tZONE,     HOUR (12) },	/* International Date Line West */
    { "cet",	tZONE,     -HOUR (1) },	/* Central European */
    { "met",	tZONE,     -HOUR (1) },	/* Middle European */
    { "mewt",	tZONE,     -HOUR (1) },	/* Middle European Winter */
    { "mest",	tDAYZONE,  -HOUR (1) },	/* Middle European Summer */
    { "mesz",	tDAYZONE,  -HOUR (1) },	/* Middle European Summer */
    { "swt",	tZONE,     -HOUR (1) },	/* Swedish Winter */
    { "sst",	tDAYZONE,  -HOUR (1) },	/* Swedish Summer */
    { "fwt",	tZONE,     -HOUR (1) },	/* French Winter */
    { "fst",	tDAYZONE,  -HOUR (1) },	/* French Summer */
    { "eet",	tZONE,     -HOUR (2) },	/* Eastern Europe, USSR Zone 1 */
    { "bt",	tZONE,     -HOUR (3) },	/* Baghdad, USSR Zone 2 */
#if 0
    { "it",	tZONE,     -HOUR (3.5) },/* Iran */
#endif
    { "zp4",	tZONE,     -HOUR (4) },	/* USSR Zone 3 */
    { "zp5",	tZONE,     -HOUR (5) },	/* USSR Zone 4 */
#if 0
    { "ist",	tZONE,     -HOUR (5.5) },/* Indian Standard */
#endif
    { "zp6",	tZONE,     -HOUR (6) },	/* USSR Zone 5 */
#if	0
    /* For completeness.  NST is also Newfoundland Standard, and SST is
     * also Swedish Summer. */
    { "nst",	tZONE,     -HOUR (6.5) },/* North Sumatra */
    { "sst",	tZONE,     -HOUR (7) },	/* South Sumatra, USSR Zone 6 */
#endif	/* 0 */
    { "wast",	tZONE,     -HOUR (7) },	/* West Australian Standard */
    { "wadt",	tDAYZONE,  -HOUR (7) },	/* West Australian Daylight */
#if 0
    { "jt",	tZONE,     -HOUR (7.5) },/* Java (3pm in Cronusland!) */
#endif
    { "cct",	tZONE,     -HOUR (8) },	/* China Coast, USSR Zone 7 */
    { "jst",	tZONE,     -HOUR (9) },	/* Japan Standard, USSR Zone 8 */
#if 0
    { "cast",	tZONE,     -HOUR (9.5) },/* Central Australian Standard */
    { "cadt",	tDAYZONE,  -HOUR (9.5) },/* Central Australian Daylight */
#endif
    { "east",	tZONE,     -HOUR (10) },	/* Eastern Australian Standard */
    { "eadt",	tDAYZONE,  -HOUR (10) },	/* Eastern Australian Daylight */
    { "gst",	tZONE,     -HOUR (10) },	/* Guam Standard, USSR Zone 9 */
    { "nzt",	tZONE,     -HOUR (12) },	/* New Zealand */
    { "nzst",	tZONE,     -HOUR (12) },	/* New Zealand Standard */
    { "nzdt",	tDAYZONE,  -HOUR (12) },	/* New Zealand Daylight */
    { "idle",	tZONE,     -HOUR (12) },	/* International Date Line East */
    {  NULL, 0, 0  }
};

/* Military timezone table. */
static TABLE const MilitaryTable[] = {
    { "a",	tZONE,	HOUR (  1) },
    { "b",	tZONE,	HOUR (  2) },
    { "c",	tZONE,	HOUR (  3) },
    { "d",	tZONE,	HOUR (  4) },
    { "e",	tZONE,	HOUR (  5) },
    { "f",	tZONE,	HOUR (  6) },
    { "g",	tZONE,	HOUR (  7) },
    { "h",	tZONE,	HOUR (  8) },
    { "i",	tZONE,	HOUR (  9) },
    { "k",	tZONE,	HOUR ( 10) },
    { "l",	tZONE,	HOUR ( 11) },
    { "m",	tZONE,	HOUR ( 12) },
    { "n",	tZONE,	HOUR (- 1) },
    { "o",	tZONE,	HOUR (- 2) },
    { "p",	tZONE,	HOUR (- 3) },
    { "q",	tZONE,	HOUR (- 4) },
    { "r",	tZONE,	HOUR (- 5) },
    { "s",	tZONE,	HOUR (- 6) },
    { "t",	tZONE,	HOUR (- 7) },
    { "u",	tZONE,	HOUR (- 8) },
    { "v",	tZONE,	HOUR (- 9) },
    { "w",	tZONE,	HOUR (-10) },
    { "x",	tZONE,	HOUR (-11) },
    { "y",	tZONE,	HOUR (-12) },
    { "z",	tZONE,	HOUR (  0) },
    { NULL, 0, 0 }
};




/* ARGSUSED */
static int
yyerror (const char *s ATTRIBUTE_UNUSED)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  return 0;
}

static int
ToHour (int Hours, MERIDIAN Meridian)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  switch (Meridian)
    {
    case MER24:
      if (Hours < 0 || Hours > 23)
	return -1;
      return Hours;
    case MERam:
      if (Hours < 1 || Hours > 12)
	return -1;
      if (Hours == 12)
	Hours = 0;
      return Hours;
    case MERpm:
      if (Hours < 1 || Hours > 12)
	return -1;
      if (Hours == 12)
	Hours = 0;
      return Hours + 12;
    default:
      break; /* used to do abort() here */
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
  /* NOTREACHED - but make gcc happy! */
Guenter Knauf's avatar
Guenter Knauf committed
  return -1;
Daniel Stenberg's avatar
Daniel Stenberg committed
}

static int
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  if (Year < 0)
    Year = -Year;

  /* XPG4 suggests that years 00-68 map to 2000-2068, and
     years 69-99 map to 1969-1999.  */
  if (Year < 69)
    Year += 2000;
  else if (Year < 100)
    Year += 1900;

  return Year;
}

static int
LookupWord (YYSTYPE *yylval, char *buff)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  char *p;
  char *q;
  const TABLE *tp;
  size_t i;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int abbrev;

  /* Make it lowercase. */
  for (p = buff; *p; p++)
    if (ISUPPER ((unsigned char) *p))
Daniel Stenberg's avatar
Daniel Stenberg committed

  if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
    {
Daniel Stenberg's avatar
Daniel Stenberg committed
      yylval->Meridian = MERam;
Daniel Stenberg's avatar
Daniel Stenberg committed
      return tMERIDIAN;
    }
  if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
    {
Daniel Stenberg's avatar
Daniel Stenberg committed
      yylval->Meridian = MERpm;
Daniel Stenberg's avatar
Daniel Stenberg committed
      return tMERIDIAN;
    }

  /* See if we have an abbreviation for a month. */
  if (strlen (buff) == 3)
    abbrev = 1;
  else if (strlen (buff) == 4 && buff[3] == '.')
    {
      abbrev = 1;
      buff[3] = '\0';
    }
  else
    abbrev = 0;

  for (tp = MonthDayTable; tp->name; tp++)
    {
      if (abbrev)
	{
	  if (strncmp (buff, tp->name, 3) == 0)
	    {
Daniel Stenberg's avatar
Daniel Stenberg committed
	      yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	      return tp->type;
	    }
	}
      else if (strcmp (buff, tp->name) == 0)
	{
Daniel Stenberg's avatar
Daniel Stenberg committed
	  yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	  return tp->type;
	}
    }

  for (tp = TimezoneTable; tp->name; tp++)
    if (strcmp (buff, tp->name) == 0)
      {
Daniel Stenberg's avatar
Daniel Stenberg committed
	yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	return tp->type;
      }

  if (strcmp (buff, "dst") == 0)
    return tDST;

  for (tp = UnitsTable; tp->name; tp++)
    if (strcmp (buff, tp->name) == 0)
      {
Daniel Stenberg's avatar
Daniel Stenberg committed
	yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	return tp->type;
      }

  /* Strip off any plural and try the units table again. */
  i = strlen (buff) - 1;
  if (buff[i] == 's')
    {
      buff[i] = '\0';
      for (tp = UnitsTable; tp->name; tp++)
	if (strcmp (buff, tp->name) == 0)
	  {
Daniel Stenberg's avatar
Daniel Stenberg committed
	    yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	    return tp->type;
	  }
      buff[i] = 's';		/* Put back for "this" in OtherTable. */
    }

  for (tp = OtherTable; tp->name; tp++)
    if (strcmp (buff, tp->name) == 0)
      {
Daniel Stenberg's avatar
Daniel Stenberg committed
	yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	return tp->type;
      }

  /* Military timezones. */
  if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff))
    {
      for (tp = MilitaryTable; tp->name; tp++)
	if (strcmp (buff, tp->name) == 0)
	  {
Daniel Stenberg's avatar
Daniel Stenberg committed
	    yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	    return tp->type;
	  }
    }

  /* Drop out any periods and try the timezone table again. */
  for (i = 0, p = q = buff; *q; q++)
    if (*q != '.')
      *p++ = *q;
    else
      i++;
  *p = '\0';
  if (i)
    for (tp = TimezoneTable; tp->name; tp++)
      if (strcmp (buff, tp->name) == 0)
	{
Daniel Stenberg's avatar
Daniel Stenberg committed
	  yylval->Number = tp->value;
Daniel Stenberg's avatar
Daniel Stenberg committed
	  return tp->type;
	}

  return tID;
}

static int
yylex (YYSTYPE *yylval, void *cookie)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  register unsigned char c;
  register char *p;
  char buff[20];
  int Count;
  int sign;

  for (;;)
    {
Daniel Stenberg's avatar
Daniel Stenberg committed
      while (ISSPACE ((unsigned char) *context->yyInput))
	context->yyInput++;
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
      if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+')
Daniel Stenberg's avatar
Daniel Stenberg committed
	{
	  if (c == '-' || c == '+')
	    {
	      sign = c == '-' ? -1 : 1;
Daniel Stenberg's avatar
Daniel Stenberg committed
	      if (!ISDIGIT (*++context->yyInput))
Daniel Stenberg's avatar
Daniel Stenberg committed
		/* skip the '-' sign */
		continue;
	    }
	  else
	    sign = 0;
Daniel Stenberg's avatar
Daniel Stenberg committed
	  for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);)
	    yylval->Number = 10 * yylval->Number + c - '0';
	  context->yyInput--;
Daniel Stenberg's avatar
Daniel Stenberg committed
	  if (sign < 0)
Daniel Stenberg's avatar
Daniel Stenberg committed
	    yylval->Number = -yylval->Number;
Daniel Stenberg's avatar
Daniel Stenberg committed
	  return sign ? tSNUMBER : tUNUMBER;
	}
      if (ISALPHA (c))
	{
Daniel Stenberg's avatar
Daniel Stenberg committed
	  for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';)
Daniel Stenberg's avatar
Daniel Stenberg committed
	    if (p < &buff[sizeof buff - 1])
	      *p++ = c;
	  *p = '\0';
Daniel Stenberg's avatar
Daniel Stenberg committed
	  context->yyInput--;
	  return LookupWord (yylval, buff);
Daniel Stenberg's avatar
Daniel Stenberg committed
	}
      if (c != '(')
Daniel Stenberg's avatar
Daniel Stenberg committed
	return *context->yyInput++;
Daniel Stenberg's avatar
Daniel Stenberg committed
      Count = 0;
      do
	{
Daniel Stenberg's avatar
Daniel Stenberg committed
	  c = *context->yyInput++;
Daniel Stenberg's avatar
Daniel Stenberg committed
	  if (c == '\0')
	    return c;
	  if (c == '(')
	    Count++;
	  else if (c == ')')
	    Count--;
	}
      while (Count > 0);
    }
}

#define TM_YEAR_ORIGIN 1900

/* Yield A - B, measured in seconds.  */
static long
difftm (struct tm *a, struct tm *b)
{
  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
  long days = (
  /* difference in day of year */
		a->tm_yday - b->tm_yday
  /* + intervening leap days */
		+ ((ay >> 2) - (by >> 2))
		- (ay / 100 - by / 100)
		+ ((ay / 100 >> 2) - (by / 100 >> 2))
  /* + difference in years * 365 */
		+ (long) (ay - by) * 365
  );
  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
		+ (a->tm_min - b->tm_min))
	  + (a->tm_sec - b->tm_sec));
}

time_t
curl_getdate (const char *p, const time_t *now)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct tm tm, tm0, *tmp;
  time_t Start;
  CURL_CONTEXT cookie;
#ifdef HAVE_LOCALTIME_R
  struct tm keeptime;
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  cookie.yyInput = p;
Daniel Stenberg's avatar
Daniel Stenberg committed
  Start = now ? *now : time ((time_t *) NULL);
  tmp = (struct tm *)localtime_r(&Start, &keeptime);
Daniel Stenberg's avatar
Daniel Stenberg committed
  tmp = localtime (&Start);
Daniel Stenberg's avatar
Daniel Stenberg committed
  if (!tmp)
    return -1;
Daniel Stenberg's avatar
Daniel Stenberg committed
  cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
  cookie.yyMonth = tmp->tm_mon + 1;
  cookie.yyDay = tmp->tm_mday;
  cookie.yyHour = tmp->tm_hour;
  cookie.yyMinutes = tmp->tm_min;
  cookie.yySeconds = tmp->tm_sec;
Daniel Stenberg's avatar
Daniel Stenberg committed
  tm.tm_isdst = tmp->tm_isdst;
Daniel Stenberg's avatar
Daniel Stenberg committed
  cookie.yyMeridian = MER24;
  cookie.yyRelSeconds = 0;
  cookie.yyRelMinutes = 0;
  cookie.yyRelHour = 0;
  cookie.yyRelDay = 0;
  cookie.yyRelMonth = 0;
  cookie.yyRelYear = 0;
  cookie.yyHaveDate = 0;
  cookie.yyHaveDay = 0;
  cookie.yyHaveRel = 0;
  cookie.yyHaveTime = 0;
  cookie.yyHaveZone = 0;

  if (yyparse (&cookie)
      || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 ||
      cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1)
Daniel Stenberg's avatar
Daniel Stenberg committed
    return -1;

Daniel Stenberg's avatar
Daniel Stenberg committed
  tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear;
  tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth;
  tm.tm_mday = cookie.yyDay + cookie.yyRelDay;
  if (cookie.yyHaveTime ||
      (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay))
Daniel Stenberg's avatar
Daniel Stenberg committed
    {
Daniel Stenberg's avatar
Daniel Stenberg committed
      tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian);
Daniel Stenberg's avatar
Daniel Stenberg committed
      if (tm.tm_hour < 0)
	return -1;
Daniel Stenberg's avatar
Daniel Stenberg committed
      tm.tm_min = cookie.yyMinutes;
      tm.tm_sec = cookie.yySeconds;
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
  else
    {
      tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
  tm.tm_hour += cookie.yyRelHour;
  tm.tm_min += cookie.yyRelMinutes;
  tm.tm_sec += cookie.yyRelSeconds;
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* Let mktime deduce tm_isdst if we have an absolute timestamp,
     or if the relative timestamp mentions days, months, or years.  */
Daniel Stenberg's avatar
Daniel Stenberg committed
  if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime |
      cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear)
Daniel Stenberg's avatar
Daniel Stenberg committed
    tm.tm_isdst = -1;

  tm0 = tm;

  Start = mktime (&tm);

  if (Start == (time_t) -1)
    {

      /* Guard against falsely reporting errors near the time_t boundaries
         when parsing times in other time zones.  For example, if the min
         time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
         of UTC, then the min localtime value is 1970-01-01 08:00:00; if
         we apply mktime to 1970-01-01 00:00:00 we will get an error, so
         we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
         zone by 24 hours to compensate.  This algorithm assumes that
         there is no DST transition within a day of the time_t boundaries.  */
Daniel Stenberg's avatar
Daniel Stenberg committed
      if (cookie.yyHaveZone)
Daniel Stenberg's avatar
Daniel Stenberg committed
	{
	  tm = tm0;
	  if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
	    {
	      tm.tm_mday++;
Daniel Stenberg's avatar
Daniel Stenberg committed
	      cookie.yyTimezone -= 24 * 60;
Daniel Stenberg's avatar
Daniel Stenberg committed
	    }
	  else
	    {
	      tm.tm_mday--;
Daniel Stenberg's avatar
Daniel Stenberg committed
	      cookie.yyTimezone += 24 * 60;
Daniel Stenberg's avatar
Daniel Stenberg committed
	    }
	  Start = mktime (&tm);
	}

      if (Start == (time_t) -1)
	return Start;
    }

Daniel Stenberg's avatar
Daniel Stenberg committed
  if (cookie.yyHaveDay && !cookie.yyHaveDate)
Daniel Stenberg's avatar
Daniel Stenberg committed
    {
Daniel Stenberg's avatar
Daniel Stenberg committed
      tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7
		     + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal)));
Daniel Stenberg's avatar
Daniel Stenberg committed
      Start = mktime (&tm);
      if (Start == (time_t) -1)
	return Start;
    }

Daniel Stenberg's avatar
Daniel Stenberg committed
  if (cookie.yyHaveZone)
Daniel Stenberg's avatar
Daniel Stenberg committed
    {
      long delta;
      struct tm *gmt;
#ifdef HAVE_GMTIME_R
      /* thread-safe version */
      struct tm keeptime2;
      gmt = (struct tm *)gmtime_r(&Start, &keeptime2);
#else
      gmt = gmtime(&Start);
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
      if (!gmt)
	return -1;
Daniel Stenberg's avatar
Daniel Stenberg committed
      delta = cookie.yyTimezone * 60L + difftm (&tm, gmt);
Daniel Stenberg's avatar
Daniel Stenberg committed
      if ((Start + delta < Start) != (delta < 0))
	return -1;		/* time_t overflow */
      Start += delta;
    }

  return Start;
}