Edit

kc3-lang/gnulib/lib/getdate.y

Branch :

  • Show log

    Commit

  • Author : Jim Meyering
    Date : 1992-11-01 05:44:30
    Hash : 43e78b97
    Message : Initial revision

  • lib/getdate.y
  • %{
    /* $Revision: 2.1 $
    **
    **  Originally written by Steven M. Bellovin <smb@research.att.com> while
    **  at the University of North Carolina at Chapel Hill.  Later tweaked by
    **  a couple of people on Usenet.  Completely overhauled by Rich $alz
    **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
    **  send any email to Rich.
    **
    **  This grammar has eight shift/reduce conflicts.
    **
    **  This code is in the public domain and has no copyright.
    */
    /* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
    /* SUPPRESS 288 on yyerrlab *//* Label unused */
    
    #ifdef __GNUC__
    #define alloca __builtin_alloca
    #else
    #ifdef sparc
    #include <alloca.h>
    #else
    #ifdef _AIX /* for Bison */
     #pragma alloca
    #else
    char *alloca ();
    #endif
    #endif
    #endif
    
    #include <stdio.h>
    #include <ctype.h>
    
    /* The code at the top of get_date which figures out the offset of the
       current time zone checks various CPP symbols to see if special
       tricks are need, but defaults to using the gettimeofday system call.
       Include <sys/time.h> if that will be used.  */
    
    #if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
    #include <sys/time.h>
    #endif
    
    #if	defined(vms)
    
    #include <types.h>
    #include <time.h>
    
    #else
    
    #include <sys/types.h>
    
    #if	defined(USG) || !defined(HAVE_FTIME)
    /*
    **  If you need to do a tzset() call to set the
    **  timezone, and don't have ftime().
    */
    struct timeb {
        time_t		time;		/* Seconds since the epoch	*/
        unsigned short	millitm;	/* Field not used		*/
        short		timezone;
        short		dstflag;	/* Field not used		*/
    };
    
    #else
    
    #include <sys/timeb.h>
    
    #endif	/* defined(USG) && !defined(HAVE_FTIME) */
    
    #if	defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
    #include <sys/time.h>
    #else
    #if defined(_AIX)
    #include <sys/time.h>
    #endif
    #include <time.h>
    #endif	/* defined(BSD4_2) */
    
    #endif	/* defined(vms) */
    
    #if defined (STDC_HEADERS) || defined (USG)
    #include <string.h>
    #endif
    
    #if sgi
    #undef timezone
    #endif
    
    extern struct tm	*localtime();
    
    #define yyparse getdate_yyparse
    #define yylex getdate_yylex
    #define yyerror getdate_yyerror
    
    #if	!defined(lint) && !defined(SABER)
    static char RCS[] =
    	"$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
    #endif	/* !defined(lint) && !defined(SABER) */
    
    
    #define EPOCH		1970
    #define HOUR(x)		((time_t)(x) * 60)
    #define SECSPERDAY	(24L * 60L * 60L)
    
    
    /*
    **  An entry in the lexical lookup table.
    */
    typedef struct _TABLE {
        char	*name;
        int		type;
        time_t	value;
    } TABLE;
    
    
    /*
    **  Daylight-savings mode:  on, off, or not yet known.
    */
    typedef enum _DSTMODE {
        DSTon, DSToff, DSTmaybe
    } DSTMODE;
    
    /*
    **  Meridian:  am, pm, or 24-hour style.
    */
    typedef enum _MERIDIAN {
        MERam, MERpm, MER24
    } MERIDIAN;
    
    
    /*
    **  Global variables.  We could get rid of most of these by using a good
    **  union as the yacc stack.  (This routine was originally written before
    **  yacc had the %union construct.)  Maybe someday; right now we only use
    **  the %union very rarely.
    */
    static char	*yyInput;
    static DSTMODE	yyDSTmode;
    static time_t	yyDayOrdinal;
    static time_t	yyDayNumber;
    static int	yyHaveDate;
    static int	yyHaveDay;
    static int	yyHaveRel;
    static int	yyHaveTime;
    static int	yyHaveZone;
    static time_t	yyTimezone;
    static time_t	yyDay;
    static time_t	yyHour;
    static time_t	yyMinutes;
    static time_t	yyMonth;
    static time_t	yySeconds;
    static time_t	yyYear;
    static MERIDIAN	yyMeridian;
    static time_t	yyRelMonth;
    static time_t	yyRelSeconds;
    
    %}
    
    %union {
        time_t		Number;
        enum _MERIDIAN	Meridian;
    }
    
    %token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
    %token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
    
    %type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
    %type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
    %type	<Meridian>	tMERIDIAN o_merid
    
    %%
    
    spec	: /* NULL */
    	| spec item
    	;
    
    item	: time {
    	    yyHaveTime++;
    	}
    	| zone {
    	    yyHaveZone++;
    	}
    	| date {
    	    yyHaveDate++;
    	}
    	| day {
    	    yyHaveDay++;
    	}
    	| rel {
    	    yyHaveRel++;
    	}
    	| number
    	;
    
    time	: tUNUMBER tMERIDIAN {
    	    yyHour = $1;
    	    yyMinutes = 0;
    	    yySeconds = 0;
    	    yyMeridian = $2;
    	}
    	| tUNUMBER ':' tUNUMBER o_merid {
    	    yyHour = $1;
    	    yyMinutes = $3;
    	    yySeconds = 0;
    	    yyMeridian = $4;
    	}
    	| tUNUMBER ':' tUNUMBER tSNUMBER {
    	    yyHour = $1;
    	    yyMinutes = $3;
    	    yyMeridian = MER24;
    	    yyDSTmode = DSToff;
    	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
    	}
    	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
    	    yyHour = $1;
    	    yyMinutes = $3;
    	    yySeconds = $5;
    	    yyMeridian = $6;
    	}
    	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
    	    yyHour = $1;
    	    yyMinutes = $3;
    	    yySeconds = $5;
    	    yyMeridian = MER24;
    	    yyDSTmode = DSToff;
    	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
    	}
    	;
    
    zone	: tZONE {
    	    yyTimezone = $1;
    	    yyDSTmode = DSToff;
    	}
    	| tDAYZONE {
    	    yyTimezone = $1;
    	    yyDSTmode = DSTon;
    	}
    	|
    	  tZONE tDST {
    	    yyTimezone = $1;
    	    yyDSTmode = DSTon;
    	}
    	;
    
    day	: tDAY {
    	    yyDayOrdinal = 1;
    	    yyDayNumber = $1;
    	}
    	| tDAY ',' {
    	    yyDayOrdinal = 1;
    	    yyDayNumber = $1;
    	}
    	| tUNUMBER tDAY {
    	    yyDayOrdinal = $1;
    	    yyDayNumber = $2;
    	}
    	;
    
    date	: tUNUMBER '/' tUNUMBER {
    	    yyMonth = $1;
    	    yyDay = $3;
    	}
    	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
    	    yyMonth = $1;
    	    yyDay = $3;
    	    yyYear = $5;
    	}
    	| tUNUMBER tSNUMBER tSNUMBER {
    	    /* ISO 8601 format.  yyyy-mm-dd.  */
    	    yyYear = $1;
    	    yyMonth = -$2;
    	    yyDay = -$3;
    	}
    	| tMONTH tUNUMBER {
    	    yyMonth = $1;
    	    yyDay = $2;
    	}
    	| tMONTH tUNUMBER ',' tUNUMBER {
    	    yyMonth = $1;
    	    yyDay = $2;
    	    yyYear = $4;
    	}
    	| tUNUMBER tMONTH {
    	    yyMonth = $2;
    	    yyDay = $1;
    	}
    	| tUNUMBER tMONTH tUNUMBER {
    	    yyMonth = $2;
    	    yyDay = $1;
    	    yyYear = $3;
    	}
    	;
    
    rel	: relunit tAGO {
    	    yyRelSeconds = -yyRelSeconds;
    	    yyRelMonth = -yyRelMonth;
    	}
    	| relunit
    	;
    
    relunit	: tUNUMBER tMINUTE_UNIT {
    	    yyRelSeconds += $1 * $2 * 60L;
    	}
    	| tSNUMBER tMINUTE_UNIT {
    	    yyRelSeconds += $1 * $2 * 60L;
    	}
    	| tMINUTE_UNIT {
    	    yyRelSeconds += $1 * 60L;
    	}
    	| tSNUMBER tSEC_UNIT {
    	    yyRelSeconds += $1;
    	}
    	| tUNUMBER tSEC_UNIT {
    	    yyRelSeconds += $1;
    	}
    	| tSEC_UNIT {
    	    yyRelSeconds++;
    	}
    	| tSNUMBER tMONTH_UNIT {
    	    yyRelMonth += $1 * $2;
    	}
    	| tUNUMBER tMONTH_UNIT {
    	    yyRelMonth += $1 * $2;
    	}
    	| tMONTH_UNIT {
    	    yyRelMonth += $1;
    	}
    	;
    
    number	: tUNUMBER {
    	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
    		yyYear = $1;
    	    else {
    		if($1>10000) {
    		    time_t date_part;
    
    		    date_part= $1/10000;
    		    yyHaveDate++;
    		    yyDay= (date_part)%100;
    		    yyMonth= (date_part/100)%100;
    		    yyYear = date_part/10000;
    		} 
    	        yyHaveTime++;
    		if ($1 < 100) {
    		    yyHour = $1;
    		    yyMinutes = 0;
    		}
    		else {
    		    yyHour = $1 / 100;
    		    yyMinutes = $1 % 100;
    		}
    		yySeconds = 0;
    		yyMeridian = MER24;
    	    }
    	}
    	;
    
    o_merid	: /* NULL */ {
    	    $$ = MER24;
    	}
    	| tMERIDIAN {
    	    $$ = $1;
    	}
    	;
    
    %%
    
    /* Month and day table. */
    static TABLE	MonthDayTable[] = {
        { "january",	tMONTH,  1 },
        { "february",	tMONTH,  2 },
        { "march",		tMONTH,  3 },
        { "april",		tMONTH,  4 },
        { "may",		tMONTH,  5 },
        { "june",		tMONTH,  6 },
        { "july",		tMONTH,  7 },
        { "august",		tMONTH,  8 },
        { "september",	tMONTH,  9 },
        { "sept",		tMONTH,  9 },
        { "october",	tMONTH, 10 },
        { "november",	tMONTH, 11 },
        { "december",	tMONTH, 12 },
        { "sunday",		tDAY, 0 },
        { "monday",		tDAY, 1 },
        { "tuesday",	tDAY, 2 },
        { "tues",		tDAY, 2 },
        { "wednesday",	tDAY, 3 },
        { "wednes",		tDAY, 3 },
        { "thursday",	tDAY, 4 },
        { "thur",		tDAY, 4 },
        { "thurs",		tDAY, 4 },
        { "friday",		tDAY, 5 },
        { "saturday",	tDAY, 6 },
        { NULL }
    };
    
    /* Time units table. */
    static TABLE	UnitsTable[] = {
        { "year",		tMONTH_UNIT,	12 },
        { "month",		tMONTH_UNIT,	1 },
        { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
        { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
        { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
        { "hour",		tMINUTE_UNIT,	60 },
        { "minute",		tMINUTE_UNIT,	1 },
        { "min",		tMINUTE_UNIT,	1 },
        { "second",		tSEC_UNIT,	1 },
        { "sec",		tSEC_UNIT,	1 },
        { NULL }
    };
    
    /* Assorted relative-time words. */
    static TABLE	OtherTable[] = {
        { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
        { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
        { "today",		tMINUTE_UNIT,	0 },
        { "now",		tMINUTE_UNIT,	0 },
        { "last",		tUNUMBER,	-1 },
        { "this",		tMINUTE_UNIT,	0 },
        { "next",		tUNUMBER,	2 },
        { "first",		tUNUMBER,	1 },
    /*  { "second",		tUNUMBER,	2 }, */
        { "third",		tUNUMBER,	3 },
        { "fourth",		tUNUMBER,	4 },
        { "fifth",		tUNUMBER,	5 },
        { "sixth",		tUNUMBER,	6 },
        { "seventh",	tUNUMBER,	7 },
        { "eighth",		tUNUMBER,	8 },
        { "ninth",		tUNUMBER,	9 },
        { "tenth",		tUNUMBER,	10 },
        { "eleventh",	tUNUMBER,	11 },
        { "twelfth",	tUNUMBER,	12 },
        { "ago",		tAGO,	1 },
        { NULL }
    };
    
    /* The timezone table. */
    /* Some of these are commented out because a time_t can't store a float. */
    static TABLE	TimezoneTable[] = {
        { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
        { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
        { "utc",	tZONE,     HOUR( 0) },
        { "wet",	tZONE,     HOUR( 0) },	/* Western European */
        { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
        { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
        { "at",	tZONE,     HOUR( 2) },	/* Azores */
    #if	0
        /* For completeness.  BST is also British Summer, and GST is
         * also Guam Standard. */
        { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
        { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
    #endif
    #if 0
        { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
        { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
        { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
    #endif
        { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
        { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
        { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
        { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
        { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
        { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
        { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
        { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
        { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
        { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
        { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
        { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
        { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
        { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
        { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
        { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
        { "nt",	tZONE,     HOUR(11) },	/* Nome */
        { "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 */
        { "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 Stanard, 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  }
    };
    
    /* Military timezone table. */
    static TABLE	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 }
    };
    
    
    
    
    /* ARGSUSED */
    int
    yyerror(s)
        char	*s;
    {
      return 0;
    }
    
    
    static time_t
    ToSeconds(Hours, Minutes, Seconds, Meridian)
        time_t	Hours;
        time_t	Minutes;
        time_t	Seconds;
        MERIDIAN	Meridian;
    {
        if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
    	return -1;
        switch (Meridian) {
        case MER24:
    	if (Hours < 0 || Hours > 23)
    	    return -1;
    	return (Hours * 60L + Minutes) * 60L + Seconds;
        case MERam:
    	if (Hours < 1 || Hours > 12)
    	    return -1;
    	return (Hours * 60L + Minutes) * 60L + Seconds;
        case MERpm:
    	if (Hours < 1 || Hours > 12)
    	    return -1;
    	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
        }
        /* NOTREACHED */
    }
    
    
    static time_t
    Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
        time_t	Month;
        time_t	Day;
        time_t	Year;
        time_t	Hours;
        time_t	Minutes;
        time_t	Seconds;
        MERIDIAN	Meridian;
        DSTMODE	DSTmode;
    {
        static int	DaysInMonth[12] = {
    	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };
        time_t	tod;
        time_t	Julian;
        int		i;
    
        if (Year < 0)
    	Year = -Year;
        if (Year < 100)
    	Year += 1900;
        DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
    		    ? 29 : 28;
        if (Year < EPOCH || Year > 1999
         || Month < 1 || Month > 12
         /* Lint fluff:  "conversion from long may lose accuracy" */
         || Day < 1 || Day > DaysInMonth[(int)--Month])
    	return -1;
    
        for (Julian = Day - 1, i = 0; i < Month; i++)
    	Julian += DaysInMonth[i];
        for (i = EPOCH; i < Year; i++)
    	Julian += 365 + (i % 4 == 0);
        Julian *= SECSPERDAY;
        Julian += yyTimezone * 60L;
        if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
    	return -1;
        Julian += tod;
        if (DSTmode == DSTon
         || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
    	Julian -= 60 * 60;
        return Julian;
    }
    
    
    static time_t
    DSTcorrect(Start, Future)
        time_t	Start;
        time_t	Future;
    {
        time_t	StartDay;
        time_t	FutureDay;
    
        StartDay = (localtime(&Start)->tm_hour + 1) % 24;
        FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
        return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
    }
    
    
    static time_t
    RelativeDate(Start, DayOrdinal, DayNumber)
        time_t	Start;
        time_t	DayOrdinal;
        time_t	DayNumber;
    {
        struct tm	*tm;
        time_t	now;
    
        now = Start;
        tm = localtime(&now);
        now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
        now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
        return DSTcorrect(Start, now);
    }
    
    
    static time_t
    RelativeMonth(Start, RelMonth)
        time_t	Start;
        time_t	RelMonth;
    {
        struct tm	*tm;
        time_t	Month;
        time_t	Year;
    
        if (RelMonth == 0)
    	return 0;
        tm = localtime(&Start);
        Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
        Year = Month / 12;
        Month = Month % 12 + 1;
        return DSTcorrect(Start,
    	    Convert(Month, (time_t)tm->tm_mday, Year,
    		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
    		MER24, DSTmaybe));
    }
    
    
    static int
    LookupWord(buff)
        char		*buff;
    {
        register char	*p;
        register char	*q;
        register TABLE	*tp;
        int			i;
        int			abbrev;
    
        /* Make it lowercase. */
        for (p = buff; *p; p++)
    	if (isupper(*p))
    	    *p = tolower(*p);
    
        if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
    	yylval.Meridian = MERam;
    	return tMERIDIAN;
        }
        if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
    	yylval.Meridian = MERpm;
    	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) {
    		yylval.Number = tp->value;
    		return tp->type;
    	    }
    	}
    	else if (strcmp(buff, tp->name) == 0) {
    	    yylval.Number = tp->value;
    	    return tp->type;
    	}
        }
    
        for (tp = TimezoneTable; tp->name; tp++)
    	if (strcmp(buff, tp->name) == 0) {
    	    yylval.Number = tp->value;
    	    return tp->type;
    	}
    
        if (strcmp(buff, "dst") == 0) 
    	return tDST;
    
        for (tp = UnitsTable; tp->name; tp++)
    	if (strcmp(buff, tp->name) == 0) {
    	    yylval.Number = tp->value;
    	    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) {
    		yylval.Number = tp->value;
    		return tp->type;
    	    }
    	buff[i] = 's';		/* Put back for "this" in OtherTable. */
        }
    
        for (tp = OtherTable; tp->name; tp++)
    	if (strcmp(buff, tp->name) == 0) {
    	    yylval.Number = tp->value;
    	    return tp->type;
    	}
    
        /* Military timezones. */
        if (buff[1] == '\0' && isalpha(*buff)) {
    	for (tp = MilitaryTable; tp->name; tp++)
    	    if (strcmp(buff, tp->name) == 0) {
    		yylval.Number = tp->value;
    		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) {
    		yylval.Number = tp->value;
    		return tp->type;
    	    }
    
        return tID;
    }
    
    
    int
    yylex()
    {
        register char	c;
        register char	*p;
        char		buff[20];
        int			Count;
        int			sign;
    
        for ( ; ; ) {
    	while (isspace(*yyInput))
    	    yyInput++;
    
    	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
    	    if (c == '-' || c == '+') {
    		sign = c == '-' ? -1 : 1;
    		if (!isdigit(*++yyInput))
    		    /* skip the '-' sign */
    		    continue;
    	    }
    	    else
    		sign = 0;
    	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
    		yylval.Number = 10 * yylval.Number + c - '0';
    	    yyInput--;
    	    if (sign < 0)
    		yylval.Number = -yylval.Number;
    	    return sign ? tSNUMBER : tUNUMBER;
    	}
    	if (isalpha(c)) {
    	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
    		if (p < &buff[sizeof buff - 1])
    		    *p++ = c;
    	    *p = '\0';
    	    yyInput--;
    	    return LookupWord(buff);
    	}
    	if (c != '(')
    	    return *yyInput++;
    	Count = 0;
    	do {
    	    c = *yyInput++;
    	    if (c == '\0')
    		return c;
    	    if (c == '(')
    		Count++;
    	    else if (c == ')')
    		Count--;
    	} while (Count > 0);
        }
    }
    
    
    time_t
    get_date(p, now)
        char		*p;
        struct timeb	*now;
    {
        struct tm		*tm;
        struct timeb	ftz;
        time_t		Start;
        time_t		tod;
    
        yyInput = p;
        if (now == NULL) {
            now = &ftz;
    #if	!defined(HAVE_FTIME)
    	(void)time(&ftz.time);
    	/* Set the timezone global. */
    	tzset();
    	{
    #if sgi
    	    ftz.timezone = (int) _timezone / 60;
    #else /* not sgi */
    #ifdef __386BSD__
    	    ftz.timezone = 0;
    #else /* neither sgi nor 386BSD */
    #if defined (USG)
    	    extern time_t timezone;
    
    	    ftz.timezone = (int) timezone / 60;
    #else /* neither sgi nor 386BSD nor USG */
    	    struct timeval tv;
    	    struct timezone tz;
    
    	    gettimeofday (&tv, &tz);
    	    ftz.timezone = (int) tz.tz_minuteswest;
    #endif /* neither sgi nor 386BSD nor USG */
    #endif /* neither sgi nor 386BSD */
    #endif /* not sgi */
    	}
    #else /* HAVE_FTIME */
    	(void)ftime(&ftz);
    #endif /* HAVE_FTIME */
        }
    
        tm = localtime(&now->time);
        yyYear = tm->tm_year;
        yyMonth = tm->tm_mon + 1;
        yyDay = tm->tm_mday;
        yyTimezone = now->timezone;
        yyDSTmode = DSTmaybe;
        yyHour = 0;
        yyMinutes = 0;
        yySeconds = 0;
        yyMeridian = MER24;
        yyRelSeconds = 0;
        yyRelMonth = 0;
        yyHaveDate = 0;
        yyHaveDay = 0;
        yyHaveRel = 0;
        yyHaveTime = 0;
        yyHaveZone = 0;
    
        if (yyparse()
         || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
    	return -1;
    
        if (yyHaveDate || yyHaveTime || yyHaveDay) {
    	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
    		    yyMeridian, yyDSTmode);
    	if (Start < 0)
    	    return -1;
        }
        else {
    	Start = now->time;
    	if (!yyHaveRel)
    	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
        }
    
        Start += yyRelSeconds;
        Start += RelativeMonth(Start, yyRelMonth);
    
        if (yyHaveDay && !yyHaveDate) {
    	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
    	Start += tod;
        }
    
        /* Have to do *something* with a legitimate -1 so it's distinguishable
         * from the error return value.  (Alternately could set errno on error.) */
        return Start == -1 ? 0 : Start;
    }
    
    
    #if	defined(TEST)
    
    /* ARGSUSED */
    main(ac, av)
        int		ac;
        char	*av[];
    {
        char	buff[128];
        time_t	d;
    
        (void)printf("Enter date, or blank line to exit.\n\t> ");
        (void)fflush(stdout);
        while (gets(buff) && buff[0]) {
    	d = get_date(buff, (struct timeb *)NULL);
    	if (d == -1)
    	    (void)printf("Bad format - couldn't convert.\n");
    	else
    	    (void)printf("%s", ctime(&d));
    	(void)printf("\t> ");
    	(void)fflush(stdout);
        }
        exit(0);
        /* NOTREACHED */
    }
    #endif	/* defined(TEST) */