home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Header: unctime.y,v 2.5 90/05/09 12:25:10 chip Exp $
- *
- * Conversion of ctime-style date string back to a time_t.
- * This module is in a different style from the rest of deliver
- * because Chip Salzenberg didn't write it.
- *
- * $Log: unctime.y,v $
- * Revision 2.5 90/05/09 12:25:10 chip
- * Use isxxx() and toxxx() portably. Test for exact match on timezone
- * names. Terminate token strings properly. [From G. Paul Ziemba]
- *
- * Revision 2.4 89/12/19 16:29:49 network
- * Make timezone handling portable to System V.
- *
- * Revision 2.3 89/12/14 17:33:56 network
- * Fix for Ultrix, which doesn't define DST_CAN.
- *
- * Revision 2.2 89/10/31 12:15:45 network
- * Fix erroneous cast in BSD-specific code.
- *
- * Revision 2.1 89/06/09 12:25:42 network
- * Update RCS revisions.
- *
- * Revision 1.3 89/06/09 12:24:01 network
- * Baseline for 2.0 release.
- *
- */
-
- /* time_t
- unctime(s)
- char *s;
-
- Convert s, which may be in almost any reasonable date format, to
- a time_t integer suitable for consumption by ctime(3). Coincidentally
- destroys the contents of s. Return -1 if s is not a recognizable legal date.
-
- Any parts of the time left unspecified take on the current values.
-
- "4 CST + 23[:10]" adds 23 minutes and optionally 10 seconds to the correction.
- "# nnnnnn" forces exactly nnnnnn seconds GMT since Jan. 1, 1970.
-
- Copyright 1988, Michael J. Haertel. Use this routine at your own risk.
- You may redistribute verbatim copies of this file. You may redistribute
- modified versions of this file so long as (1) you state who last changed
- it, and (2) this copyright notice appears unmodified.
-
- Some debugging by W. Anthony Smith.
-
- Bug fix, minor enhancements, and non-BSD modifications by David MacKenzie.
-
- Several changes by Chip Salzenberg for use with deliver:
- Include "deliver.h".
- Handle military timezones as per RFC822.
- Elimination of "#if FTIME" in favor of "#if !USG".
- Don't modify input string.
- Consider extra junk in date string an error.
- */
-
- %{
- #include "deliver.h"
- #include <ctype.h>
- #ifdef BSD
- # include <sys/time.h>
- #else
- # include <time.h>
- # ifndef USG
- # include <sys/timeb.h>
- # endif
- #endif
-
- extern long atol();
-
- /* Delta is correction to turn specified time into GMT. */
- /* if (zoneflag), a timezone was explicitly specified. */
- static year, month, day, hour, minute, second, delta, zoneflag, errorflag, iflag;
- static long iresult;
-
- #define YYSTYPE long
- %}
-
- %token NUM MONTH AM PM
-
- %%
-
- date:
- day time year
- | day year time
- | time day year
- | time day
- | day time
- | day year
- | day
- | time
- | '#' NUM { iflag = TRUE; iresult = $2; }
- ; /* previous line forces exact time in seconds GMT */
-
- day:
- NUM MONTH { month = $2; day = $1; }
- | MONTH NUM { month = $1; day = $2; }
- | NUM '/' NUM { month = $1; day = $3; }
- ;
-
- year:
- ',' NUM { year = $2; }
- | '/' NUM { year = $2; }
- | NUM { year = $1; }
- ;
-
- time:
- clock AM { hour %= 12; }
- | clock PM { hour = hour % 12 + 12; }
- | clock
- ;
-
- clock:
- NUM ':' NUM ':' NUM { hour = $1; minute = $3; second = $5; }
- | NUM ':' NUM { hour = $1; minute = $3; }
- ;
-
- %%
-
- /* Return 0 if s is the same word as t.
- Return 1 if s is a prefix of t; e.g. prefix("mar", "march") returns 1.
- Return -1 if s is not a prefix of t.
- Note that comparison is case-insensitive. */
- static
- wordeq(s,t)
- char *s, *t;
- {
- while (*s && *t)
- {
- int i, j;
-
- i = *s++;
- j = *t++;
- if (isascii(i) && isupper(i))
- i = tolower(i);
- if (isascii(j) && isupper(j))
- j = tolower(j);
- if (i != j)
- return -1;
- }
-
- if (*s)
- return -1;
- return (*t == 0);
- }
-
- static char *lexptr;
-
- static void
- initlex(s)
- char *s;
- {
- lexptr = s;
- }
-
- static char *
- months[] =
- {
- "jan",
- "feb",
- "mar",
- "apr",
- "may",
- "jun",
- "jul",
- "aug",
- "sep",
- "oct",
- "nov",
- "dec",
- 0
- };
-
- struct zonename
- {
- char *name; /* Name of the time zone. */
- int delta; /* Correction to add to GMT (in minutes) */
- };
-
- static struct zonename zones[] =
- {
- "gmt", 0,
- "ut", 0,
- "est", -5 * 60, /* North American time zones */
- "edt", -6 * 60,
- "cst", -6 * 60,
- "cdt", -7 * 60,
- "mst", -7 * 60,
- "mdt", -8 * 60,
- "pst", -8 * 60,
- "pdt", -9 * 60,
- "z", 0, /* Military time zones */
- "a", -1 * 60,
- "b", -2 * 60,
- "c", -3 * 60,
- "d", -4 * 60,
- "e", -5 * 60,
- "f", -6 * 60,
- "g", -7 * 60,
- "h", -8 * 60,
- "i", -9 * 60,
- "k", -10 * 60,
- "l", -11 * 60,
- "m", -12 * 60,
- "n", 1 * 60,
- "o", 2 * 60,
- "p", 3 * 60,
- "q", 4 * 60,
- "r", 5 * 60,
- "s", 6 * 60,
- "t", 7 * 60,
- "u", 8 * 60,
- "v", 9 * 60,
- "w", 10 * 60,
- "x", 11 * 60,
- "y", 12 * 60,
- 0, 0
- };
-
- /* Lexical analyzer. Gather alphabetics into tokens; if they are unknown
- strings ignore them, and if they are months return the appropriate value.
- If the token is the name of the time zone set delta = correction and
- zoneflag = TRUE, and skip ahead to the next token (the parser itself
- never sees time zones).
- If the token is a number, return its value.
- If it is a punctuation mark, return the character code.
- Ignore white space. */
- static
- yylex()
- {
- register i;
- char token[40]; /* Probably paranoid. */
-
- for (;;)
- {
- while (isascii(*lexptr) && isspace(*lexptr))
- lexptr++;
- if (*lexptr == 0)
- return 0;
- else if (isascii(*lexptr) && isalpha(*lexptr))
- {
- i = 0;
- while (isascii(*lexptr) && isalpha(*lexptr))
- token[i++] = *lexptr++;
- token[i] = '\0';
- for (i = 0; months[i]; i++)
- if (wordeq(months[i],token) >= 0)
- {
- yylval = i + 1;
- return MONTH;
- }
- for (i = 0; zones[i].name; i++)
- if (wordeq(zones[i].name,token) == 0)
- {
- int oper, next;
-
- zoneflag = TRUE;
- delta = zones[i].delta;
- oper = yylex();
- /* Syntax: "4 CST + 23[:10]" adds 23 minutes and
- optionally 10 seconds to delta (the correction). */
- if (oper == '+' || oper == '-')
- {
- (void) yylex();
- delta += (oper == '+' ? 60 : -60) * yylval;
- next = yylex();
- if (next == ':')
- {
- (void) yylex();
- delta += (oper == '+' ? 1 : -1) * yylval;
- }
- else
- return next;
- }
- else
- return oper;
- }
- if (wordeq("pm",token) == 0 || wordeq("p.m.", token) == 0)
- return PM;
- if (wordeq("am",token) == 0 || wordeq("a.m.", token) == 0)
- return AM;
- continue;
- }
- else if (isascii(*lexptr) && isdigit(*lexptr))
- {
- i = 0;
- while (isascii(*lexptr) && isdigit(*lexptr))
- token[i++] = *lexptr++;
- token[i] = '\0';
- yylval = atoi(token);
- return NUM;
- }
- else
- return *lexptr++;
- }
- }
-
- /* ARGSUSED */
- static
- yyerror(s)
- char *s;
- {
- errorflag = TRUE;
- }
-
- /* Is y a leap year? */
- #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
-
- /* Number of leap years from 1970 to y (not including y itself) */
- #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
-
- /* This macro returns the "day" number of the sunday immediately
- preceding or equal to the argument in the current year. */
- #define FIRST_SUNDAY 3
- #define dayofepoch(day) ((day) + (year - 1970) * 365 + nleap(year))
- #define sunday(day) ((day) - (dayofepoch(day) + 7 - FIRST_SUNDAY) % 7)
-
- /* correction()
- returns the daylight savings correction in seconds to ADD to GMT
- to get correct local time.
- Since we are converting local back to GMT, we SUBTRACT this later on
- (local = gmt + correction(); gmt = local - correction()).
-
- While we're at it, we also add the longitude correction for minutes
- west of Greenwich. To do this, we have all these fascinating tables
- here . . . */
-
- #ifdef BSD
-
- struct dstinfo
- {
- int year; /* Info is for this year, or default if zero. */
- int start; /* DST begins sunday before this day. */
- int end; /* DST ends sunday before this day. */
- };
-
- /* USA. */
- static struct dstinfo
- usa_dst[] =
- {
- 1974, 5, 333,
- 1975, 58, 303,
- 0, 119, 303
- };
-
- /* Australia. */
- static struct dstinfo
- aus_dst[] =
- {
- 1970, 999, 0,
- 1971, 303, 0,
- 1972, 303, 58,
- 0, 303, 65
- };
-
- /* Western Europe. */
- static struct dstinfo
- weur_dst[] =
- {
- 1983, 89, 296,
- 0, 89, 303
- };
-
- /* Middle Europe (also used for Eastern Europe, for lack of better
- information). */
- static struct dstinfo
- meur_dst[] =
- {
- 1983, 89, 296,
- 0, 89, 272
- };
-
- /* Canada is same as US, except no early 70's insanity. */
- #ifdef DST_CAN
- static struct dstinfo
- can_dst[] =
- {
- 0, 119, 303
- };
- #endif
-
- struct dst_rules
- {
- int magic; /* Gettimeofday magic number for rule type */
- struct dstinfo *entry; /* Pointer to struct dstinfo array. */
- int correction; /* Correction in minutes to GMT. */
- };
-
- static struct dst_rules
- dstrules[] =
- {
- DST_USA, usa_dst, 60,
- DST_AUST, aus_dst, -60, /* Southern hemisphere */
- DST_WET, weur_dst, 60,
- DST_MET, meur_dst, 60,
- DST_EET, meur_dst, 60,
- #ifdef DST_CAN
- DST_CAN, can_dst, 60,
- #endif
- -1, 0, 0
- };
-
- static
- correction(day,tz)
- int day; /* Day number in current year. */
- struct timezone *tz;
- {
- int i, correc = 0;
- struct dstinfo *dst;
-
- /* Did the user specify in the input string a timezone correction to use? */
- if (zoneflag)
- return delta * 60;
-
- /* Since no correction was explicitly specified, we use local time zone and
- DST, as returned by gettimeofday() earlier . . . */
- if (tz->tz_dsttime)
- for (i = 0; dstrules[i].magic != -1; i++)
- if (dstrules[i].magic == tz->tz_dsttime)
- {
- dst = dstrules[i].entry;
- while (dst->year != year && dst->year)
- dst++;
- if (sunday(dst->start) <= day && day <= sunday(dst->end)
- /* For some reason, DST starts/ends at 2 am sunday mornings. */
- && !(day == sunday(dst->start) && hour < 2)
- && !(day == sunday(dst->end) && hour >= 2))
- correc = dstrules[i].correction;
- break;
- }
- correc -= tz->tz_minuteswest;
- return correc * 60;
- }
-
- #else /* !BSD */
-
- static
- correction()
- {
- #ifdef USG
- extern long timezone;
- #else
- struct timeb tb;
- #endif
-
- /* Did the user specify in the input string a timezone correction to use? */
- if (zoneflag)
- return delta * 60;
-
- /* Since no correction was explicitly specified, we use local time zone. */
- #ifdef USG
- return (int) -timezone;
- #else
- ftime(&tb);
- return tb.timezone * -60;
- #endif
- }
-
- #endif /* !BSD */
-
- static short
- monthlens[] =
- {
- 31, /* January */
- 28, /* February */
- 31, /* March */
- 30, /* April */
- 31, /* May */
- 30, /* June */
- 31, /* July */
- 31, /* August */
- 30, /* September */
- 31, /* October */
- 30, /* November */
- 31 /* December */
- };
-
- time_t
- unctime(s)
- char *s;
- {
- #ifdef BSD
- struct timeval tv;
- struct timezone tz;
- #else
- time_t now;
- #endif
- struct tm *tm;
- int dayofyear;
-
- #ifdef BSD
- (void) gettimeofday(&tv,&tz);
- /* The cast is required to shut lint up. Berkeley goes to all the effort
- to define time_t, why don't they use it? */
- tm = localtime((time_t *) &tv.tv_sec);
- #else
- (void) time(&now);
- tm = localtime(&now);
- #endif
- year = tm->tm_year;
- month = tm->tm_mon + 1;
- day = tm->tm_mday;
- hour = tm->tm_hour;
- minute = tm->tm_min;
- second = tm->tm_sec;
- zoneflag = FALSE;
- errorflag = FALSE;
-
- initlex(s);
- (void) yyparse();
-
- if (errorflag)
- return -1;
-
- /* If garbage beyond valid date, that's an error. */
- while (isascii(*lexptr) && isspace(*lexptr))
- ++lexptr;
- if (*lexptr)
- return -1;
-
- /* User forced the exact time in seconds GMT, no further work necessary. */
- if (iflag)
- return iresult;
-
- /* Try to keep the year reasonable (i.e., within the domain of ctime()). */
- if (year < 1970)
- year += 1900;
- if (year < 1970)
- year += 100;
-
- /* Check for preposterous months/days/times. */
- if (month < 1 || month > 12 || day < 1 ||
- day > monthlens[month - 1] && !(month == 2 && day == 29 && leap(year))
- || hour > 23 || minute > 59 || second > 59)
- return -1;
-
- /* Mostly for convenience in sunday() macro, we use zero-origin days. */
- dayofyear = day - 1;
- if (month > 2 && leap(year))
- ++dayofyear;
- while (--month > 0)
- dayofyear += monthlens[month - 1];
-
- /* Wow! */
- return 86400 * (dayofyear + 365 * (year - 1970) + nleap(year))
- + 3600 * hour + 60 * minute + second
- #ifdef BSD
- - correction(dayofyear,&tz)
- #else
- - correction()
- #endif
- ;
- }
-