194630Sobrien%{ 294630Sobrien/* 394630Sobrien** Originally written by Steven M. Bellovin <smb@research.att.com> while 494630Sobrien** at the University of North Carolina at Chapel Hill. Later tweaked by 594630Sobrien** a couple of people on Usenet. Completely overhauled by Rich $alz 694630Sobrien** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; 794630Sobrien** 894630Sobrien** This grammar has 10 shift/reduce conflicts. 994630Sobrien** 1094630Sobrien** This code is in the public domain and has no copyright. 1194630Sobrien*/ 1294630Sobrien/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ 1394630Sobrien/* SUPPRESS 288 on yyerrlab *//* Label unused */ 1494630Sobrien 15116333Smarkm#include <sys/cdefs.h> 16116333Smarkm__FBSDID("$FreeBSD$"); 1794630Sobrien 18200462Sdelphij#include <stdio.h> 1994630Sobrien#include <ctype.h> 2094630Sobrien 2194630Sobrien/* The code at the top of get_date which figures out the offset of the 2294630Sobrien current time zone checks various CPP symbols to see if special 2394630Sobrien tricks are need, but defaults to using the gettimeofday system call. 2494630Sobrien Include <sys/time.h> if that will be used. */ 2594630Sobrien 2694630Sobrien#if defined(vms) 2794630Sobrien# include <types.h> 2894630Sobrien#else /* defined(vms) */ 2994630Sobrien# include <sys/types.h> 3094630Sobrien# include <sys/time.h> 3194630Sobrien#endif /* !defined(vms) */ 3294630Sobrien 3394792Sobrien#if defined (__STDC__) || defined (USG) 3494630Sobrien#include <string.h> 3594630Sobrien#endif 3694630Sobrien 3794630Sobrien/* Some old versions of bison generate parsers that use bcopy. 3894630Sobrien That loses on systems that don't provide the function, so we have 3994630Sobrien to redefine it here. */ 4094630Sobrien#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) 4194630Sobrien#define bcopy(from, to, len) memcpy ((to), (from), (len)) 4294630Sobrien#endif 4394630Sobrien 4494792Sobrien#if defined (__STDC__) 4594630Sobrien#include <stdlib.h> 4694630Sobrien#endif 4794630Sobrien 4894630Sobrien/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS 4994630Sobrien releases): 5094630Sobrien 5194630Sobrien We don't want to mess with all the portability hassles of alloca. 5294630Sobrien In particular, most (all?) versions of bison will use alloca in 5394630Sobrien their parser. If bison works on your system (e.g. it should work 5494630Sobrien with gcc), then go ahead and use it, but the more general solution 5594630Sobrien is to use byacc instead of bison, which should generate a portable 5694630Sobrien parser. I played with adding "#define alloca dont_use_alloca", to 5794630Sobrien give an error if the parser generator uses alloca (and thus detect 5894630Sobrien unportable getdate.c's), but that seems to cause as many problems 5994630Sobrien as it solves. */ 6094630Sobrien 61116333Smarkm#include <time.h> 6294630Sobrien 6394630Sobrien#define yyparse getdate_yyparse 6494630Sobrien#define yylex getdate_yylex 6594630Sobrien#define yyerror getdate_yyerror 6694630Sobrien 67116333Smarkmstatic int yyparse(void); 68116333Smarkmstatic int yylex(void); 69116333Smarkmstatic int yyerror(const char *); 7094630Sobrien 71203723Sedtime_t get_date(char *); 72116333Smarkm 7394630Sobrien#define EPOCH 1970 7494630Sobrien#define HOUR(x) ((time_t)(x) * 60) 7594630Sobrien#define SECSPERDAY (24L * 60L * 60L) 7694630Sobrien 7794630Sobrien 7894630Sobrien/* 7994630Sobrien** An entry in the lexical lookup table. 8094630Sobrien*/ 8194630Sobrientypedef struct _TABLE { 82116333Smarkm const char *name; 8394630Sobrien int type; 8494630Sobrien time_t value; 8594630Sobrien} TABLE; 8694630Sobrien 8794630Sobrien 8894630Sobrien/* 8994630Sobrien** Daylight-savings mode: on, off, or not yet known. 9094630Sobrien*/ 9194630Sobrientypedef enum _DSTMODE { 9294630Sobrien DSTon, DSToff, DSTmaybe 9394630Sobrien} DSTMODE; 9494630Sobrien 9594630Sobrien/* 9694630Sobrien** Meridian: am, pm, or 24-hour style. 9794630Sobrien*/ 9894630Sobrientypedef enum _MERIDIAN { 9994630Sobrien MERam, MERpm, MER24 10094630Sobrien} MERIDIAN; 10194630Sobrien 10294630Sobrien 10394630Sobrien/* 10494630Sobrien** Global variables. We could get rid of most of these by using a good 10594630Sobrien** union as the yacc stack. (This routine was originally written before 10694630Sobrien** yacc had the %union construct.) Maybe someday; right now we only use 10794630Sobrien** the %union very rarely. 10894630Sobrien*/ 10994630Sobrienstatic char *yyInput; 11094630Sobrienstatic DSTMODE yyDSTmode; 11194630Sobrienstatic time_t yyDayOrdinal; 11294630Sobrienstatic time_t yyDayNumber; 11394630Sobrienstatic int yyHaveDate; 11494630Sobrienstatic int yyHaveDay; 11594630Sobrienstatic int yyHaveRel; 11694630Sobrienstatic int yyHaveTime; 11794630Sobrienstatic int yyHaveZone; 11894630Sobrienstatic time_t yyTimezone; 11994630Sobrienstatic time_t yyDay; 12094630Sobrienstatic time_t yyHour; 12194630Sobrienstatic time_t yyMinutes; 12294630Sobrienstatic time_t yyMonth; 12394630Sobrienstatic time_t yySeconds; 12494630Sobrienstatic time_t yyYear; 12594630Sobrienstatic MERIDIAN yyMeridian; 12694630Sobrienstatic time_t yyRelMonth; 12794630Sobrienstatic time_t yyRelSeconds; 12894630Sobrien 12994630Sobrien%} 13094630Sobrien 13194630Sobrien%union { 13294630Sobrien time_t Number; 13394630Sobrien enum _MERIDIAN Meridian; 13494630Sobrien} 13594630Sobrien 13694630Sobrien%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT 13794630Sobrien%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST 13894630Sobrien 13994630Sobrien%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT 14094630Sobrien%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE 14194630Sobrien%type <Meridian> tMERIDIAN o_merid 14294630Sobrien 14394630Sobrien%% 14494630Sobrien 14594630Sobrienspec : /* NULL */ 14694630Sobrien | spec item 14794630Sobrien ; 14894630Sobrien 14994630Sobrienitem : time { 15094630Sobrien yyHaveTime++; 15194630Sobrien } 15294630Sobrien | zone { 15394630Sobrien yyHaveZone++; 15494630Sobrien } 15594630Sobrien | date { 15694630Sobrien yyHaveDate++; 15794630Sobrien } 15894630Sobrien | day { 15994630Sobrien yyHaveDay++; 16094630Sobrien } 16194630Sobrien | rel { 16294630Sobrien yyHaveRel++; 16394630Sobrien } 16494630Sobrien | number 16594630Sobrien ; 16694630Sobrien 16794630Sobrientime : tUNUMBER tMERIDIAN { 16894630Sobrien yyHour = $1; 16994630Sobrien yyMinutes = 0; 17094630Sobrien yySeconds = 0; 17194630Sobrien yyMeridian = $2; 17294630Sobrien } 17394630Sobrien | tUNUMBER ':' tUNUMBER o_merid { 17494630Sobrien yyHour = $1; 17594630Sobrien yyMinutes = $3; 17694630Sobrien yySeconds = 0; 17794630Sobrien yyMeridian = $4; 17894630Sobrien } 17994630Sobrien | tUNUMBER ':' tUNUMBER tSNUMBER { 18094630Sobrien yyHour = $1; 18194630Sobrien yyMinutes = $3; 18294630Sobrien yyMeridian = MER24; 18394630Sobrien yyDSTmode = DSToff; 18494630Sobrien yyTimezone = - ($4 % 100 + ($4 / 100) * 60); 18594630Sobrien } 18694630Sobrien | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { 18794630Sobrien yyHour = $1; 18894630Sobrien yyMinutes = $3; 18994630Sobrien yySeconds = $5; 19094630Sobrien yyMeridian = $6; 19194630Sobrien } 19294630Sobrien | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { 19394630Sobrien yyHour = $1; 19494630Sobrien yyMinutes = $3; 19594630Sobrien yySeconds = $5; 19694630Sobrien yyMeridian = MER24; 19794630Sobrien yyDSTmode = DSToff; 19894630Sobrien yyTimezone = - ($6 % 100 + ($6 / 100) * 60); 19994630Sobrien } 20094630Sobrien ; 20194630Sobrien 20294630Sobrienzone : tZONE { 20394630Sobrien yyTimezone = $1; 20494630Sobrien yyDSTmode = DSToff; 20594630Sobrien } 20694630Sobrien | tDAYZONE { 20794630Sobrien yyTimezone = $1; 20894630Sobrien yyDSTmode = DSTon; 20994630Sobrien } 21094630Sobrien | 21194630Sobrien tZONE tDST { 21294630Sobrien yyTimezone = $1; 21394630Sobrien yyDSTmode = DSTon; 21494630Sobrien } 21594630Sobrien ; 21694630Sobrien 21794630Sobrienday : tDAY { 21894630Sobrien yyDayOrdinal = 1; 21994630Sobrien yyDayNumber = $1; 22094630Sobrien } 22194630Sobrien | tDAY ',' { 22294630Sobrien yyDayOrdinal = 1; 22394630Sobrien yyDayNumber = $1; 22494630Sobrien } 22594630Sobrien | tUNUMBER tDAY { 22694630Sobrien yyDayOrdinal = $1; 22794630Sobrien yyDayNumber = $2; 22894630Sobrien } 22994630Sobrien ; 23094630Sobrien 23194630Sobriendate : tUNUMBER '/' tUNUMBER { 23294630Sobrien yyMonth = $1; 23394630Sobrien yyDay = $3; 23494630Sobrien } 23594630Sobrien | tUNUMBER '/' tUNUMBER '/' tUNUMBER { 23694630Sobrien if ($1 >= 100) { 23794630Sobrien yyYear = $1; 23894630Sobrien yyMonth = $3; 23994630Sobrien yyDay = $5; 24094630Sobrien } else { 24194630Sobrien yyMonth = $1; 24294630Sobrien yyDay = $3; 24394630Sobrien yyYear = $5; 24494630Sobrien } 24594630Sobrien } 24694630Sobrien | tUNUMBER tSNUMBER tSNUMBER { 24794630Sobrien /* ISO 8601 format. yyyy-mm-dd. */ 24894630Sobrien yyYear = $1; 24994630Sobrien yyMonth = -$2; 25094630Sobrien yyDay = -$3; 25194630Sobrien } 25294630Sobrien | tUNUMBER tMONTH tSNUMBER { 25394630Sobrien /* e.g. 17-JUN-1992. */ 25494630Sobrien yyDay = $1; 25594630Sobrien yyMonth = $2; 25694630Sobrien yyYear = -$3; 25794630Sobrien } 25894630Sobrien | tMONTH tUNUMBER { 25994630Sobrien yyMonth = $1; 26094630Sobrien yyDay = $2; 26194630Sobrien } 26294630Sobrien | tMONTH tUNUMBER ',' tUNUMBER { 26394630Sobrien yyMonth = $1; 26494630Sobrien yyDay = $2; 26594630Sobrien yyYear = $4; 26694630Sobrien } 26794630Sobrien | tUNUMBER tMONTH { 26894630Sobrien yyMonth = $2; 26994630Sobrien yyDay = $1; 27094630Sobrien } 27194630Sobrien | tUNUMBER tMONTH tUNUMBER { 27294630Sobrien yyMonth = $2; 27394630Sobrien yyDay = $1; 27494630Sobrien yyYear = $3; 27594630Sobrien } 27694630Sobrien ; 27794630Sobrien 27894630Sobrienrel : relunit tAGO { 27994630Sobrien yyRelSeconds = -yyRelSeconds; 28094630Sobrien yyRelMonth = -yyRelMonth; 28194630Sobrien } 28294630Sobrien | relunit 28394630Sobrien ; 28494630Sobrien 28594630Sobrienrelunit : tUNUMBER tMINUTE_UNIT { 28694630Sobrien yyRelSeconds += $1 * $2 * 60L; 28794630Sobrien } 28894630Sobrien | tSNUMBER tMINUTE_UNIT { 28994630Sobrien yyRelSeconds += $1 * $2 * 60L; 29094630Sobrien } 29194630Sobrien | tMINUTE_UNIT { 29294630Sobrien yyRelSeconds += $1 * 60L; 29394630Sobrien } 29494630Sobrien | tSNUMBER tSEC_UNIT { 29594630Sobrien yyRelSeconds += $1; 29694630Sobrien } 29794630Sobrien | tUNUMBER tSEC_UNIT { 29894630Sobrien yyRelSeconds += $1; 29994630Sobrien } 30094630Sobrien | tSEC_UNIT { 30194630Sobrien yyRelSeconds++; 30294630Sobrien } 30394630Sobrien | tSNUMBER tMONTH_UNIT { 30494630Sobrien yyRelMonth += $1 * $2; 30594630Sobrien } 30694630Sobrien | tUNUMBER tMONTH_UNIT { 30794630Sobrien yyRelMonth += $1 * $2; 30894630Sobrien } 30994630Sobrien | tMONTH_UNIT { 31094630Sobrien yyRelMonth += $1; 31194630Sobrien } 31294630Sobrien ; 31394630Sobrien 31494630Sobriennumber : tUNUMBER { 31594630Sobrien if (yyHaveTime && yyHaveDate && !yyHaveRel) 31694630Sobrien yyYear = $1; 31794630Sobrien else { 31894630Sobrien if($1>10000) { 31994630Sobrien yyHaveDate++; 32094630Sobrien yyDay= ($1)%100; 32194630Sobrien yyMonth= ($1/100)%100; 32294630Sobrien yyYear = $1/10000; 32394630Sobrien } 32494630Sobrien else { 32594630Sobrien yyHaveTime++; 32694630Sobrien if ($1 < 100) { 32794630Sobrien yyHour = $1; 32894630Sobrien yyMinutes = 0; 32994630Sobrien } 33094630Sobrien else { 33194630Sobrien yyHour = $1 / 100; 33294630Sobrien yyMinutes = $1 % 100; 33394630Sobrien } 33494630Sobrien yySeconds = 0; 33594630Sobrien yyMeridian = MER24; 33694630Sobrien } 33794630Sobrien } 33894630Sobrien } 33994630Sobrien ; 34094630Sobrien 34194630Sobrieno_merid : /* NULL */ { 34294630Sobrien $$ = MER24; 34394630Sobrien } 34494630Sobrien | tMERIDIAN { 34594630Sobrien $$ = $1; 34694630Sobrien } 34794630Sobrien ; 34894630Sobrien 34994630Sobrien%% 35094630Sobrien 35194630Sobrien/* Month and day table. */ 35294630Sobrienstatic TABLE const MonthDayTable[] = { 35394630Sobrien { "january", tMONTH, 1 }, 35494630Sobrien { "february", tMONTH, 2 }, 35594630Sobrien { "march", tMONTH, 3 }, 35694630Sobrien { "april", tMONTH, 4 }, 35794630Sobrien { "may", tMONTH, 5 }, 35894630Sobrien { "june", tMONTH, 6 }, 35994630Sobrien { "july", tMONTH, 7 }, 36094630Sobrien { "august", tMONTH, 8 }, 36194630Sobrien { "september", tMONTH, 9 }, 36294630Sobrien { "sept", tMONTH, 9 }, 36394630Sobrien { "october", tMONTH, 10 }, 36494630Sobrien { "november", tMONTH, 11 }, 36594630Sobrien { "december", tMONTH, 12 }, 36694630Sobrien { "sunday", tDAY, 0 }, 36794630Sobrien { "monday", tDAY, 1 }, 36894630Sobrien { "tuesday", tDAY, 2 }, 36994630Sobrien { "tues", tDAY, 2 }, 37094630Sobrien { "wednesday", tDAY, 3 }, 37194630Sobrien { "wednes", tDAY, 3 }, 37294630Sobrien { "thursday", tDAY, 4 }, 37394630Sobrien { "thur", tDAY, 4 }, 37494630Sobrien { "thurs", tDAY, 4 }, 37594630Sobrien { "friday", tDAY, 5 }, 37694630Sobrien { "saturday", tDAY, 6 }, 377116333Smarkm { NULL, 0, 0 } 37894630Sobrien}; 37994630Sobrien 38094630Sobrien/* Time units table. */ 38194630Sobrienstatic TABLE const UnitsTable[] = { 38294630Sobrien { "year", tMONTH_UNIT, 12 }, 38394630Sobrien { "month", tMONTH_UNIT, 1 }, 38494630Sobrien { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, 38594630Sobrien { "week", tMINUTE_UNIT, 7 * 24 * 60 }, 38694630Sobrien { "day", tMINUTE_UNIT, 1 * 24 * 60 }, 38794630Sobrien { "hour", tMINUTE_UNIT, 60 }, 38894630Sobrien { "minute", tMINUTE_UNIT, 1 }, 38994630Sobrien { "min", tMINUTE_UNIT, 1 }, 39094630Sobrien { "second", tSEC_UNIT, 1 }, 39194630Sobrien { "sec", tSEC_UNIT, 1 }, 392116333Smarkm { NULL, 0, 0 } 39394630Sobrien}; 39494630Sobrien 39594630Sobrien/* Assorted relative-time words. */ 39694630Sobrienstatic TABLE const OtherTable[] = { 39794630Sobrien { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, 39894630Sobrien { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, 39994630Sobrien { "today", tMINUTE_UNIT, 0 }, 40094630Sobrien { "now", tMINUTE_UNIT, 0 }, 40194630Sobrien { "last", tUNUMBER, -1 }, 40294630Sobrien { "this", tMINUTE_UNIT, 0 }, 40394630Sobrien { "next", tUNUMBER, 2 }, 40494630Sobrien { "first", tUNUMBER, 1 }, 40594630Sobrien/* { "second", tUNUMBER, 2 }, */ 40694630Sobrien { "third", tUNUMBER, 3 }, 40794630Sobrien { "fourth", tUNUMBER, 4 }, 40894630Sobrien { "fifth", tUNUMBER, 5 }, 40994630Sobrien { "sixth", tUNUMBER, 6 }, 41094630Sobrien { "seventh", tUNUMBER, 7 }, 41194630Sobrien { "eighth", tUNUMBER, 8 }, 41294630Sobrien { "ninth", tUNUMBER, 9 }, 41394630Sobrien { "tenth", tUNUMBER, 10 }, 41494630Sobrien { "eleventh", tUNUMBER, 11 }, 41594630Sobrien { "twelfth", tUNUMBER, 12 }, 416116333Smarkm { "ago", tAGO, 1 }, 417116333Smarkm { NULL, 0, 0 } 41894630Sobrien}; 41994630Sobrien 42094630Sobrien/* The timezone table. */ 42194630Sobrien/* Some of these are commented out because a time_t can't store a float. */ 42294630Sobrienstatic TABLE const TimezoneTable[] = { 42394630Sobrien { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ 42494630Sobrien { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ 42594630Sobrien { "utc", tZONE, HOUR( 0) }, 42694630Sobrien { "wet", tZONE, HOUR( 0) }, /* Western European */ 42794630Sobrien { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ 42894630Sobrien { "wat", tZONE, HOUR( 1) }, /* West Africa */ 42994630Sobrien { "at", tZONE, HOUR( 2) }, /* Azores */ 43094630Sobrien#if 0 43194630Sobrien /* For completeness. BST is also British Summer, and GST is 43294630Sobrien * also Guam Standard. */ 43394630Sobrien { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ 43494630Sobrien { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ 43594630Sobrien#endif 43694630Sobrien#if 0 43794630Sobrien { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ 43894630Sobrien { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ 43994630Sobrien { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ 44094630Sobrien#endif 44194630Sobrien { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ 44294630Sobrien { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ 44394630Sobrien { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ 44494630Sobrien { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ 44594630Sobrien { "cst", tZONE, HOUR( 6) }, /* Central Standard */ 44694630Sobrien { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ 44794630Sobrien { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ 44894630Sobrien { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ 44994630Sobrien { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ 45094630Sobrien { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ 45194630Sobrien { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ 45294630Sobrien { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ 45394630Sobrien { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ 45494630Sobrien { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ 45594630Sobrien { "cat", tZONE, HOUR(10) }, /* Central Alaska */ 45694630Sobrien { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ 45794630Sobrien { "nt", tZONE, HOUR(11) }, /* Nome */ 45894630Sobrien { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ 45994630Sobrien { "cet", tZONE, -HOUR(1) }, /* Central European */ 46094630Sobrien { "met", tZONE, -HOUR(1) }, /* Middle European */ 46194630Sobrien { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ 46294630Sobrien { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ 46394630Sobrien { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ 46494630Sobrien { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ 46594630Sobrien { "fwt", tZONE, -HOUR(1) }, /* French Winter */ 46694630Sobrien { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ 46794630Sobrien { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ 46894630Sobrien { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ 46994630Sobrien#if 0 47094630Sobrien { "it", tZONE, -HOUR(3.5) },/* Iran */ 47194630Sobrien#endif 47294630Sobrien { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ 47394630Sobrien { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ 47494630Sobrien#if 0 47594630Sobrien { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ 47694630Sobrien#endif 47794630Sobrien { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ 47894630Sobrien#if 0 47994630Sobrien /* For completeness. NST is also Newfoundland Stanard, and SST is 48094630Sobrien * also Swedish Summer. */ 48194630Sobrien { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ 48294630Sobrien { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ 48394630Sobrien#endif /* 0 */ 48494630Sobrien { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ 48594630Sobrien { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ 48694630Sobrien#if 0 48794630Sobrien { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ 48894630Sobrien#endif 48994630Sobrien { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ 49094630Sobrien { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ 49194630Sobrien#if 0 49294630Sobrien { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ 49394630Sobrien { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ 49494630Sobrien#endif 49594630Sobrien { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ 49694630Sobrien { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ 49794630Sobrien { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ 49894630Sobrien { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ 49994630Sobrien { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ 50094630Sobrien { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ 50194630Sobrien { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ 502116333Smarkm { NULL, 0, 0 } 50394630Sobrien}; 50494630Sobrien 50594630Sobrien/* Military timezone table. */ 50694630Sobrienstatic TABLE const MilitaryTable[] = { 50794630Sobrien { "a", tZONE, HOUR( 1) }, 50894630Sobrien { "b", tZONE, HOUR( 2) }, 50994630Sobrien { "c", tZONE, HOUR( 3) }, 51094630Sobrien { "d", tZONE, HOUR( 4) }, 51194630Sobrien { "e", tZONE, HOUR( 5) }, 51294630Sobrien { "f", tZONE, HOUR( 6) }, 51394630Sobrien { "g", tZONE, HOUR( 7) }, 51494630Sobrien { "h", tZONE, HOUR( 8) }, 51594630Sobrien { "i", tZONE, HOUR( 9) }, 51694630Sobrien { "k", tZONE, HOUR( 10) }, 51794630Sobrien { "l", tZONE, HOUR( 11) }, 51894630Sobrien { "m", tZONE, HOUR( 12) }, 51994630Sobrien { "n", tZONE, HOUR(- 1) }, 52094630Sobrien { "o", tZONE, HOUR(- 2) }, 52194630Sobrien { "p", tZONE, HOUR(- 3) }, 52294630Sobrien { "q", tZONE, HOUR(- 4) }, 52394630Sobrien { "r", tZONE, HOUR(- 5) }, 52494630Sobrien { "s", tZONE, HOUR(- 6) }, 52594630Sobrien { "t", tZONE, HOUR(- 7) }, 52694630Sobrien { "u", tZONE, HOUR(- 8) }, 52794630Sobrien { "v", tZONE, HOUR(- 9) }, 52894630Sobrien { "w", tZONE, HOUR(-10) }, 52994630Sobrien { "x", tZONE, HOUR(-11) }, 53094630Sobrien { "y", tZONE, HOUR(-12) }, 53194630Sobrien { "z", tZONE, HOUR( 0) }, 532116333Smarkm { NULL, 0, 0 } 53394630Sobrien}; 53494630Sobrien 53594630Sobrien 53694630Sobrien 53794630Sobrien 53894630Sobrien/* ARGSUSED */ 53994630Sobrienstatic int 540116333Smarkmyyerror(const char *s __unused) 54194630Sobrien{ 54294630Sobrien return 0; 54394630Sobrien} 54494630Sobrien 54594630Sobrien 54694630Sobrienstatic time_t 547116333SmarkmToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) 54894630Sobrien{ 54994630Sobrien if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) 55094630Sobrien return -1; 55194630Sobrien switch (Meridian) { 55294630Sobrien case MER24: 55394630Sobrien if (Hours < 0 || Hours > 23) 55494630Sobrien return -1; 55594630Sobrien return (Hours * 60L + Minutes) * 60L + Seconds; 55694630Sobrien case MERam: 55794630Sobrien if (Hours < 1 || Hours > 12) 55894630Sobrien return -1; 55994630Sobrien if (Hours == 12) 56094630Sobrien Hours = 0; 56194630Sobrien return (Hours * 60L + Minutes) * 60L + Seconds; 56294630Sobrien case MERpm: 56394630Sobrien if (Hours < 1 || Hours > 12) 56494630Sobrien return -1; 56594630Sobrien if (Hours == 12) 56694630Sobrien Hours = 0; 56794630Sobrien return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; 56894630Sobrien default: 56994630Sobrien abort (); 57094630Sobrien } 57194630Sobrien /* NOTREACHED */ 57294630Sobrien} 57394630Sobrien 57494630Sobrien 57594630Sobrien/* Year is either 57694630Sobrien * A negative number, which means to use its absolute value (why?) 57794630Sobrien * A number from 0 to 99, which means a year from 1900 to 1999, or 57894630Sobrien * The actual year (>=100). */ 57994630Sobrienstatic time_t 580116333SmarkmConvert(time_t Month, time_t Day, time_t Year, 581116333Smarkm time_t Hours, time_t Minutes, time_t Seconds, 582116333Smarkm MERIDIAN Meridian, DSTMODE DSTmode) 58394630Sobrien{ 58494630Sobrien static int DaysInMonth[12] = { 58594630Sobrien 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 58694630Sobrien }; 58794630Sobrien time_t tod; 58894630Sobrien time_t Julian; 58994630Sobrien int i; 59094630Sobrien 59194630Sobrien if (Year < 0) 59294630Sobrien Year = -Year; 59394630Sobrien if (Year < 69) 59494630Sobrien Year += 2000; 59594630Sobrien else if (Year < 100) 59694630Sobrien Year += 1900; 59794630Sobrien DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) 59894630Sobrien ? 29 : 28; 59994630Sobrien /* Checking for 2038 bogusly assumes that time_t is 32 bits. But 60094630Sobrien I'm too lazy to try to check for time_t overflow in another way. */ 60194630Sobrien if (Year < EPOCH || Year > 2038 60294630Sobrien || Month < 1 || Month > 12 60394630Sobrien /* Lint fluff: "conversion from long may lose accuracy" */ 60494630Sobrien || Day < 1 || Day > DaysInMonth[(int)--Month]) 60594630Sobrien return -1; 60694630Sobrien 60794630Sobrien for (Julian = Day - 1, i = 0; i < Month; i++) 60894630Sobrien Julian += DaysInMonth[i]; 60994630Sobrien for (i = EPOCH; i < Year; i++) 61094630Sobrien Julian += 365 + (i % 4 == 0); 61194630Sobrien Julian *= SECSPERDAY; 61294630Sobrien Julian += yyTimezone * 60L; 61394630Sobrien if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) 61494630Sobrien return -1; 61594630Sobrien Julian += tod; 61694630Sobrien if (DSTmode == DSTon 61794630Sobrien || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) 61894630Sobrien Julian -= 60 * 60; 61994630Sobrien return Julian; 62094630Sobrien} 62194630Sobrien 62294630Sobrien 62394630Sobrienstatic time_t 624116333SmarkmDSTcorrect(time_t Start, time_t Future) 62594630Sobrien{ 62694630Sobrien time_t StartDay; 62794630Sobrien time_t FutureDay; 62894630Sobrien 62994630Sobrien StartDay = (localtime(&Start)->tm_hour + 1) % 24; 63094630Sobrien FutureDay = (localtime(&Future)->tm_hour + 1) % 24; 63194630Sobrien return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; 63294630Sobrien} 63394630Sobrien 63494630Sobrien 63594630Sobrienstatic time_t 636116333SmarkmRelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber) 63794630Sobrien{ 63894630Sobrien struct tm *tm; 63994630Sobrien time_t now; 64094630Sobrien 64194630Sobrien now = Start; 64294630Sobrien tm = localtime(&now); 64394630Sobrien now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); 64494630Sobrien now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); 64594630Sobrien return DSTcorrect(Start, now); 64694630Sobrien} 64794630Sobrien 64894630Sobrien 64994630Sobrienstatic time_t 650116333SmarkmRelativeMonth(time_t Start, time_t RelMonth) 65194630Sobrien{ 65294630Sobrien struct tm *tm; 65394630Sobrien time_t Month; 65494630Sobrien time_t Year; 65594630Sobrien 65694630Sobrien if (RelMonth == 0) 65794630Sobrien return 0; 65894630Sobrien tm = localtime(&Start); 65994630Sobrien Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; 66094630Sobrien Year = Month / 12; 66194630Sobrien Month = Month % 12 + 1; 66294630Sobrien return DSTcorrect(Start, 66394630Sobrien Convert(Month, (time_t)tm->tm_mday, Year, 66494630Sobrien (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, 66594630Sobrien MER24, DSTmaybe)); 66694630Sobrien} 66794630Sobrien 66894630Sobrien 66994630Sobrienstatic int 670116333SmarkmLookupWord(char *buff) 67194630Sobrien{ 672116333Smarkm char *p; 673116333Smarkm char *q; 674116333Smarkm const TABLE *tp; 675116333Smarkm int i; 676116333Smarkm int abbrev; 67794630Sobrien 67894630Sobrien /* Make it lowercase. */ 67994630Sobrien for (p = buff; *p; p++) 68094630Sobrien if (isupper(*p)) 68194630Sobrien *p = tolower(*p); 68294630Sobrien 68394630Sobrien if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { 68494630Sobrien yylval.Meridian = MERam; 68594630Sobrien return tMERIDIAN; 68694630Sobrien } 68794630Sobrien if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { 68894630Sobrien yylval.Meridian = MERpm; 68994630Sobrien return tMERIDIAN; 69094630Sobrien } 69194630Sobrien 69294630Sobrien /* See if we have an abbreviation for a month. */ 69394630Sobrien if (strlen(buff) == 3) 69494630Sobrien abbrev = 1; 69594630Sobrien else if (strlen(buff) == 4 && buff[3] == '.') { 69694630Sobrien abbrev = 1; 69794630Sobrien buff[3] = '\0'; 69894630Sobrien } 69994630Sobrien else 70094630Sobrien abbrev = 0; 70194630Sobrien 70294630Sobrien for (tp = MonthDayTable; tp->name; tp++) { 70394630Sobrien if (abbrev) { 70494630Sobrien if (strncmp(buff, tp->name, 3) == 0) { 70594630Sobrien yylval.Number = tp->value; 70694630Sobrien return tp->type; 70794630Sobrien } 70894630Sobrien } 70994630Sobrien else if (strcmp(buff, tp->name) == 0) { 71094630Sobrien yylval.Number = tp->value; 71194630Sobrien return tp->type; 71294630Sobrien } 71394630Sobrien } 71494630Sobrien 71594630Sobrien for (tp = TimezoneTable; tp->name; tp++) 71694630Sobrien if (strcmp(buff, tp->name) == 0) { 71794630Sobrien yylval.Number = tp->value; 71894630Sobrien return tp->type; 71994630Sobrien } 72094630Sobrien 72194630Sobrien if (strcmp(buff, "dst") == 0) 72294630Sobrien return tDST; 72394630Sobrien 72494630Sobrien for (tp = UnitsTable; tp->name; tp++) 72594630Sobrien if (strcmp(buff, tp->name) == 0) { 72694630Sobrien yylval.Number = tp->value; 72794630Sobrien return tp->type; 72894630Sobrien } 72994630Sobrien 73094630Sobrien /* Strip off any plural and try the units table again. */ 73194630Sobrien i = strlen(buff) - 1; 73294630Sobrien if (buff[i] == 's') { 73394630Sobrien buff[i] = '\0'; 73494630Sobrien for (tp = UnitsTable; tp->name; tp++) 73594630Sobrien if (strcmp(buff, tp->name) == 0) { 73694630Sobrien yylval.Number = tp->value; 73794630Sobrien return tp->type; 73894630Sobrien } 73994630Sobrien buff[i] = 's'; /* Put back for "this" in OtherTable. */ 74094630Sobrien } 74194630Sobrien 74294630Sobrien for (tp = OtherTable; tp->name; tp++) 74394630Sobrien if (strcmp(buff, tp->name) == 0) { 74494630Sobrien yylval.Number = tp->value; 74594630Sobrien return tp->type; 74694630Sobrien } 74794630Sobrien 74894630Sobrien /* Military timezones. */ 74994630Sobrien if (buff[1] == '\0' && isalpha(*buff)) { 75094630Sobrien for (tp = MilitaryTable; tp->name; tp++) 75194630Sobrien if (strcmp(buff, tp->name) == 0) { 75294630Sobrien yylval.Number = tp->value; 75394630Sobrien return tp->type; 75494630Sobrien } 75594630Sobrien } 75694630Sobrien 75794630Sobrien /* Drop out any periods and try the timezone table again. */ 75894630Sobrien for (i = 0, p = q = buff; *q; q++) 75994630Sobrien if (*q != '.') 76094630Sobrien *p++ = *q; 76194630Sobrien else 76294630Sobrien i++; 76394630Sobrien *p = '\0'; 76494630Sobrien if (i) 76594630Sobrien for (tp = TimezoneTable; tp->name; tp++) 76694630Sobrien if (strcmp(buff, tp->name) == 0) { 76794630Sobrien yylval.Number = tp->value; 76894630Sobrien return tp->type; 76994630Sobrien } 77094630Sobrien 77194630Sobrien return tID; 77294630Sobrien} 77394630Sobrien 77494630Sobrien 77594630Sobrienstatic int 776116333Smarkmyylex(void) 77794630Sobrien{ 778116333Smarkm char c; 779116333Smarkm char *p; 780116333Smarkm char buff[20]; 781116333Smarkm int Count; 782116333Smarkm int sign; 78394630Sobrien 78494630Sobrien for ( ; ; ) { 78594630Sobrien while (isspace(*yyInput)) 78694630Sobrien yyInput++; 78794630Sobrien 78894630Sobrien if (isdigit(c = *yyInput) || c == '-' || c == '+') { 78994630Sobrien if (c == '-' || c == '+') { 79094630Sobrien sign = c == '-' ? -1 : 1; 79194630Sobrien if (!isdigit(*++yyInput)) 79294630Sobrien /* skip the '-' sign */ 79394630Sobrien continue; 79494630Sobrien } 79594630Sobrien else 79694630Sobrien sign = 0; 79794630Sobrien for (yylval.Number = 0; isdigit(c = *yyInput++); ) 79894630Sobrien yylval.Number = 10 * yylval.Number + c - '0'; 79994630Sobrien yyInput--; 80094630Sobrien if (sign < 0) 80194630Sobrien yylval.Number = -yylval.Number; 80294630Sobrien return sign ? tSNUMBER : tUNUMBER; 80394630Sobrien } 80494630Sobrien if (isalpha(c)) { 80594630Sobrien for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) 80694630Sobrien if (p < &buff[sizeof buff - 1]) 80794630Sobrien *p++ = c; 80894630Sobrien *p = '\0'; 80994630Sobrien yyInput--; 81094630Sobrien return LookupWord(buff); 81194630Sobrien } 81294630Sobrien if (c != '(') 81394630Sobrien return *yyInput++; 81494630Sobrien Count = 0; 81594630Sobrien do { 81694630Sobrien c = *yyInput++; 81794630Sobrien if (c == '\0') 81894630Sobrien return c; 81994630Sobrien if (c == '(') 82094630Sobrien Count++; 82194630Sobrien else if (c == ')') 82294630Sobrien Count--; 82394630Sobrien } while (Count > 0); 82494630Sobrien } 82594630Sobrien} 82694630Sobrien 82794630Sobrien#define TM_YEAR_ORIGIN 1900 82894630Sobrien 82994630Sobrien/* Yield A - B, measured in seconds. */ 83094630Sobrienstatic long 831116333Smarkmdifftm (struct tm *a, struct tm *b) 83294630Sobrien{ 83394630Sobrien int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); 83494630Sobrien int by = b->tm_year + (TM_YEAR_ORIGIN - 1); 83594630Sobrien int days = ( 83694630Sobrien /* difference in day of year */ 83794630Sobrien a->tm_yday - b->tm_yday 83894630Sobrien /* + intervening leap days */ 83994630Sobrien + ((ay >> 2) - (by >> 2)) 84094630Sobrien - (ay/100 - by/100) 84194630Sobrien + ((ay/100 >> 2) - (by/100 >> 2)) 84294630Sobrien /* + difference in years * 365 */ 84394630Sobrien + (long)(ay-by) * 365 84494630Sobrien ); 84594630Sobrien return (60*(60*(24*days + (a->tm_hour - b->tm_hour)) 84694630Sobrien + (a->tm_min - b->tm_min)) 84794630Sobrien + (a->tm_sec - b->tm_sec)); 84894630Sobrien} 84994630Sobrien 85094630Sobrientime_t 851203723Sedget_date(char *p) 85294630Sobrien{ 853203723Sed struct tm *tm, *gmt_ptr, gmt; 854203723Sed int tzoff; 85594630Sobrien time_t Start; 85694630Sobrien time_t tod; 85794630Sobrien time_t nowtime; 85894630Sobrien 859149453Sroberto bzero (&gmt, sizeof(struct tm)); 86094630Sobrien yyInput = p; 86194630Sobrien 862203723Sed (void)time (&nowtime); 86394630Sobrien 864203723Sed gmt_ptr = gmtime (&nowtime); 865203723Sed if (gmt_ptr != NULL) 866203723Sed { 867203723Sed /* Make a copy, in case localtime modifies *tm (I think 868203723Sed that comment now applies to *gmt_ptr, but I am too 869203723Sed lazy to dig into how gmtime and locatime allocate the 870203723Sed structures they return pointers to). */ 871203723Sed gmt = *gmt_ptr; 872203723Sed } 87394630Sobrien 874203723Sed if (! (tm = localtime (&nowtime))) 875203723Sed return -1; 87694630Sobrien 877203723Sed if (gmt_ptr != NULL) 878203723Sed tzoff = difftm (&gmt, tm) / 60; 87994630Sobrien else 880203723Sed /* We are on a system like VMS, where the system clock is 881203723Sed in local time and the system has no concept of timezones. 882203723Sed Hopefully we can fake this out (for the case in which the 883203723Sed user specifies no timezone) by just saying the timezone 884203723Sed is zero. */ 885203723Sed tzoff = 0; 88694630Sobrien 887203723Sed if(tm->tm_isdst) 888203723Sed tzoff += 60; 889203723Sed 89094630Sobrien tm = localtime(&nowtime); 89194630Sobrien yyYear = tm->tm_year + 1900; 89294630Sobrien yyMonth = tm->tm_mon + 1; 89394630Sobrien yyDay = tm->tm_mday; 894203723Sed yyTimezone = tzoff; 89594630Sobrien yyDSTmode = DSTmaybe; 89694630Sobrien yyHour = 0; 89794630Sobrien yyMinutes = 0; 89894630Sobrien yySeconds = 0; 89994630Sobrien yyMeridian = MER24; 90094630Sobrien yyRelSeconds = 0; 90194630Sobrien yyRelMonth = 0; 90294630Sobrien yyHaveDate = 0; 90394630Sobrien yyHaveDay = 0; 90494630Sobrien yyHaveRel = 0; 90594630Sobrien yyHaveTime = 0; 90694630Sobrien yyHaveZone = 0; 90794630Sobrien 90894630Sobrien if (yyparse() 90994630Sobrien || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) 91094630Sobrien return -1; 91194630Sobrien 91294630Sobrien if (yyHaveDate || yyHaveTime || yyHaveDay) { 91394630Sobrien Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, 91494630Sobrien yyMeridian, yyDSTmode); 91594630Sobrien if (Start < 0) 91694630Sobrien return -1; 91794630Sobrien } 91894630Sobrien else { 91994630Sobrien Start = nowtime; 92094630Sobrien if (!yyHaveRel) 92194630Sobrien Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; 92294630Sobrien } 92394630Sobrien 92494630Sobrien Start += yyRelSeconds; 92594630Sobrien Start += RelativeMonth(Start, yyRelMonth); 92694630Sobrien 92794630Sobrien if (yyHaveDay && !yyHaveDate) { 92894630Sobrien tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); 92994630Sobrien Start += tod; 93094630Sobrien } 93194630Sobrien 93294630Sobrien /* Have to do *something* with a legitimate -1 so it's distinguishable 93394630Sobrien * from the error return value. (Alternately could set errno on error.) */ 93494630Sobrien return Start == -1 ? 0 : Start; 93594630Sobrien} 93694630Sobrien 93794630Sobrien 93894630Sobrien#if defined(TEST) 93994630Sobrien 94094630Sobrien/* ARGSUSED */ 94194630Sobrienint 942116333Smarkmmain(int ac, char *av[]) 94394630Sobrien{ 94494630Sobrien char buff[128]; 94594630Sobrien time_t d; 94694630Sobrien 94794630Sobrien (void)printf("Enter date, or blank line to exit.\n\t> "); 94894630Sobrien (void)fflush(stdout); 94994630Sobrien while (gets(buff) && buff[0]) { 950203723Sed d = get_date(buff); 95194630Sobrien if (d == -1) 95294630Sobrien (void)printf("Bad format - couldn't convert.\n"); 95394630Sobrien else 95494630Sobrien (void)printf("%s", ctime(&d)); 95594630Sobrien (void)printf("\t> "); 95694630Sobrien (void)fflush(stdout); 95794630Sobrien } 95894630Sobrien exit(0); 95994630Sobrien /* NOTREACHED */ 96094630Sobrien} 96194630Sobrien#endif /* defined(TEST) */ 962