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