A new version of getdate.y
Rich Salz
rsalz at bbn.com
Thu Sep 20 02:45:33 AEST 1990
This is a new version of getdate.y. It has several timezone fixes,
documentation on what else needs to be done, int/long problems fixed, and
so on. Slightly earlier versions of this have been sent to Steve
Bellovin, the original author, and the maintainers of C and B news.
I don't know if/when I'll have a chance to do make the other changes
Steve recommends, but I look forward to comments at any rate.
/rich $alz
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents: README.getdate getdate.y
# Wrapped by rsalz at litchi.bbn.com on Wed Sep 19 12:42:42 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive."'
if test -f 'README.getdate' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README.getdate'\"
else
echo shar: Extracting \"'README.getdate'\" \(2104 characters\)
sed "s/^X//" >'README.getdate' <<'END_OF_FILE'
X
XWe've been using Steve Bellovin's getdate() routine in the Cronus project
Xfor over a year. This past summer Jim Berets and I cleaned up all the
Xtimezones, cleaned up some int/long problems that others have found,
Xreformatted the code and cleaned it up, and contacted Steve about what
Xshould else needs to be done with it.
X
XUnfortunately, we didn't have the time to do anything much with Steve's
Xcomments, other then document them in the code, and here (thanks to Steve
Xfor letting me quote some email):
X Two things need to be done. First, the purpose of getdate should be
X defined; many of its features were for a rather different purpose that
X it's used for today. Specifically, I was implementing 'at' before
X there was such a thing, and I wanted the ability to specify relative
X intervals. Thus, getdate accepts things like 'two weeks'. I don't
X think that code is tremendously useful. If it is, it could be
X strengthened by writing a better grammar, i.e., ``two weeks after
X monday'' -- currently, the word ``after'' isn't accept, and the
X relationship between the day of the week and the interval is
X peculiar. And that in turn goes back to the central implementation
X failure: it is restricted to a simple 'int' stack, because that's all
X that v6 yacc allowed. There's also a lot of reliance on global
X variables, for the same reason. Using a union as the stack type, one
X could store much better information, clean up the code, and make the
X semantics much clearer. (As a trivial change, I suspect that the
X military time zones are wrong, as I took them from RFC822 (or maybe
X even 733), and I've since learned that those are incorrect. I'd drop
X that entirely.) It might also be worth some effort to make the time
X zone stuff more compatible with the 4.3tahoe and the SysVR3.2 stuff,
X especially as regards the names of the zones.
X
X It was also intended to let utter novices set the date on a UNIX
X machine after rebooting it and thus it accepted almost any rational
X form.
X
XHope you find this useful.
X /rich $alz
END_OF_FILE
if test 2104 -ne `wc -c <'README.getdate'`; then
echo shar: \"'README.getdate'\" unpacked with wrong size!
fi
# end of 'README.getdate'
fi
if test -f 'getdate.y' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getdate.y'\"
else
echo shar: Extracting \"'getdate.y'\" \(20830 characters\)
sed "s/^X//" >'getdate.y' <<'END_OF_FILE'
X%{
X/* $Revision: 2.1 $
X**
X** Originally written by Steven M. Bellovin <smb at research.att.com> while
X** at the University of North Carolina at Chapel Hill. Later tweaked by
X** a couple of people on Usenet. Completely overhauled by Rich $alz
X** <rsalz at bbn.com> and Jim Berets <jberets at bbn.com> in August, 1990;
X** send any email to Rich.
X**
X** This grammar has eight shift/reduce conflicts.
X**
X** This code is in the public domain and has no copyright.
X*/
X/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
X/* SUPPRESS 288 on yyerrlab *//* Label unused */
X#include "defs.h"
X#include <stdio.h>
X#include <ctype.h>
X
X#if defined(vms)
X#include <types.h>
X#include <time.h>
X#else
X#include <sys/types.h>
X#if defined(USG)
X/*
X** Uncomment the next line if you need to do a tzset() call to set the
X** timezone, and don't have ftime(). Some SystemV releases, I think.
X*/
X/*#define NEED_TZSET */
Xstruct timeb {
X time_t time; /* Seconds since the epoch */
X unsigned short millitm; /* Field not used */
X short timezone;
X short dstflag; /* Field not used */
X};
X#else
X#include <sys/timeb.h>
X#endif /* defined(USG) */
X#if defined(BSD4_2)
X#include <sys/time.h>
X#else
X#include <time.h>
X#endif /* defined(BSD4_2) */
X#endif /* defined(vms) */
X
X#if !defined(lint) && !defined(SABER)
Xstatic char RCS[] =
X "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
X#endif /* !defined(lint) && !defined(SABER) */
X
X
X#define EPOCH 1970
X#define HOUR(x) (x * 60)
X#define SECSPERDAY (24L * 60L * 60L)
X
X
X/*
X** An entry in the lexical lookup table.
X*/
Xtypedef struct _TABLE {
X char *name;
X int type;
X time_t value;
X} TABLE;
X
X
X/*
X** Daylight-savings mode: on, off, or not yet known.
X*/
Xtypedef enum _DSTMODE {
X DSTon, DSToff, DSTmaybe
X} DSTMODE;
X
X/*
X** Meridian: am, pm, or 24-hour style.
X*/
Xtypedef enum _MERIDIAN {
X MERam, MERpm, MER24
X} MERIDIAN;
X
X
X/*
X** Global variables. We could get rid of most of these by using a good
X** union as the yacc stack. (This routine was originally written before
X** yacc had the %union construct.) Maybe someday; right now we only use
X** the %union very rarely.
X*/
Xstatic char *yyInput;
Xstatic DSTMODE yyDSTmode;
Xstatic time_t yyDayOrdinal;
Xstatic time_t yyDayNumber;
Xstatic int yyHaveDate;
Xstatic int yyHaveDay;
Xstatic int yyHaveRel;
Xstatic int yyHaveTime;
Xstatic int yyHaveZone;
Xstatic time_t yyTimezone;
Xstatic time_t yyDay;
Xstatic time_t yyHour;
Xstatic time_t yyMinutes;
Xstatic time_t yyMonth;
Xstatic time_t yySeconds;
Xstatic time_t yyYear;
Xstatic MERIDIAN yyMeridian;
Xstatic time_t yyRelMonth;
Xstatic time_t yyRelSeconds;
X
X
Xextern struct tm *localtime();
X%}
X
X%union {
X time_t Number;
X enum _MERIDIAN Meridian;
X}
X
X%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
X%token tSEC_UNIT tSNUMBER tUNUMBER tZONE
X
X%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
X%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
X%type <Meridian> tMERIDIAN o_merid
X
X%%
X
Xspec : /* NULL */
X | spec item
X ;
X
Xitem : time {
X yyHaveTime++;
X }
X | zone {
X yyHaveZone++;
X }
X | date {
X yyHaveDate++;
X }
X | day {
X yyHaveDay++;
X }
X | rel {
X yyHaveRel++;
X }
X | number
X ;
X
Xtime : tUNUMBER tMERIDIAN {
X yyHour = $1;
X yyMinutes = 0;
X yySeconds = 0;
X yyMeridian = $2;
X }
X | tUNUMBER ':' tUNUMBER o_merid {
X yyHour = $1;
X yyMinutes = $3;
X yySeconds = 0;
X yyMeridian = $4;
X }
X | tUNUMBER ':' tUNUMBER tSNUMBER {
X yyHour = $1;
X yyMinutes = $3;
X yyMeridian = MER24;
X yyDSTmode = DSToff;
X yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
X }
X | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
X yyHour = $1;
X yyMinutes = $3;
X yySeconds = $5;
X yyMeridian = $6;
X }
X | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
X yyHour = $1;
X yyMinutes = $3;
X yySeconds = $5;
X yyMeridian = MER24;
X yyDSTmode = DSToff;
X yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
X }
X ;
X
Xzone : tZONE {
X yyTimezone = $1;
X yyDSTmode = DSToff;
X }
X | tDAYZONE {
X yyTimezone = $1;
X yyDSTmode = DSTon;
X }
X ;
X
Xday : tDAY {
X yyDayOrdinal = 1;
X yyDayNumber = $1;
X }
X | tDAY ',' {
X yyDayOrdinal = 1;
X yyDayNumber = $1;
X }
X | tUNUMBER tDAY {
X yyDayOrdinal = $1;
X yyDayNumber = $2;
X }
X ;
X
Xdate : tUNUMBER '/' tUNUMBER {
X yyMonth = $1;
X yyDay = $3;
X }
X | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
X yyMonth = $1;
X yyDay = $3;
X yyYear = $5;
X }
X | tMONTH tUNUMBER {
X yyMonth = $1;
X yyDay = $2;
X }
X | tMONTH tUNUMBER ',' tUNUMBER {
X yyMonth = $1;
X yyDay = $2;
X yyYear = $4;
X }
X | tUNUMBER tMONTH {
X yyMonth = $2;
X yyDay = $1;
X }
X | tUNUMBER tMONTH tUNUMBER {
X yyMonth = $2;
X yyDay = $1;
X yyYear = $3;
X }
X ;
X
Xrel : relunit tAGO {
X yyRelSeconds = -yyRelSeconds;
X yyRelMonth = -yyRelMonth;
X }
X | relunit
X ;
X
Xrelunit : tUNUMBER tMINUTE_UNIT {
X yyRelSeconds += $1 * $2 * 60L;
X }
X | tSNUMBER tMINUTE_UNIT {
X yyRelSeconds += $1 * $2 * 60L;
X }
X | tMINUTE_UNIT {
X yyRelSeconds += $1 * 60L;
X }
X | tSNUMBER tSEC_UNIT {
X yyRelSeconds += $1;
X }
X | tUNUMBER tSEC_UNIT {
X yyRelSeconds += $1;
X }
X | tSEC_UNIT {
X yyRelSeconds++;
X }
X | tSNUMBER tMONTH_UNIT {
X yyRelMonth += $1 * $2;
X }
X | tUNUMBER tMONTH_UNIT {
X yyRelMonth += $1 * $2;
X }
X | tMONTH_UNIT {
X yyRelMonth += $1;
X }
X ;
X
Xnumber : tUNUMBER {
X if (yyHaveTime && yyHaveDate && !yyHaveRel)
X yyYear = $1;
X else {
X yyHaveTime++;
X if ($1 < 100) {
X yyHour = $1;
X yyMinutes = 0;
X }
X else {
X yyHour = $1 / 100;
X yyMinutes = $1 % 100;
X }
X yySeconds = 0;
X yyMeridian = MER24;
X }
X }
X ;
X
Xo_merid : /* NULL */ {
X $$ = MER24;
X }
X | tMERIDIAN {
X $$ = $1;
X }
X ;
X
X%%
X
X/* Month and day table. */
Xstatic TABLE MonthDayTable[] = {
X { "january", tMONTH, 1 },
X { "february", tMONTH, 2 },
X { "march", tMONTH, 3 },
X { "april", tMONTH, 4 },
X { "may", tMONTH, 5 },
X { "june", tMONTH, 6 },
X { "july", tMONTH, 7 },
X { "august", tMONTH, 8 },
X { "september", tMONTH, 9 },
X { "sept", tMONTH, 9 },
X { "october", tMONTH, 10 },
X { "november", tMONTH, 11 },
X { "december", tMONTH, 12 },
X { "sunday", tDAY, 0 },
X { "monday", tDAY, 1 },
X { "tuesday", tDAY, 2 },
X { "tues", tDAY, 2 },
X { "wednesday", tDAY, 3 },
X { "wednes", tDAY, 3 },
X { "thursday", tDAY, 4 },
X { "thur", tDAY, 4 },
X { "thurs", tDAY, 4 },
X { "friday", tDAY, 5 },
X { "saturday", tDAY, 6 },
X { NULL }
X};
X
X/* Time units table. */
Xstatic TABLE UnitsTable[] = {
X { "year", tMONTH_UNIT, 12 },
X { "month", tMONTH_UNIT, 1 },
X { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
X { "week", tMINUTE_UNIT, 7 * 24 * 60 },
X { "day", tMINUTE_UNIT, 1 * 24 * 60 },
X { "hour", tMINUTE_UNIT, 60 },
X { "minute", tMINUTE_UNIT, 1 },
X { "min", tMINUTE_UNIT, 1 },
X { "second", tSEC_UNIT, 1 },
X { "sec", tSEC_UNIT, 1 },
X { NULL }
X};
X
X/* Assorted relative-time words. */
Xstatic TABLE OtherTable[] = {
X { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
X { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
X { "today", tMINUTE_UNIT, 0 },
X { "now", tMINUTE_UNIT, 0 },
X { "last", tUNUMBER, -1 },
X { "this", tMINUTE_UNIT, 0 },
X { "next", tUNUMBER, 2 },
X { "first", tUNUMBER, 1 },
X/* { "second", tUNUMBER, 2 }, */
X { "third", tUNUMBER, 3 },
X { "fourth", tUNUMBER, 4 },
X { "fifth", tUNUMBER, 5 },
X { "sixth", tUNUMBER, 6 },
X { "seventh", tUNUMBER, 7 },
X { "eighth", tUNUMBER, 8 },
X { "ninth", tUNUMBER, 9 },
X { "tenth", tUNUMBER, 10 },
X { "eleventh", tUNUMBER, 11 },
X { "twelfth", tUNUMBER, 12 },
X { "ago", tAGO, 1 },
X { NULL }
X};
X
X/* The timezone table. */
Xstatic TABLE TimezoneTable[] = {
X { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
X { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
X { "utc", tZONE, HOUR( 0) },
X { "wet", tZONE, HOUR( 0) }, /* Western European */
X { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
X { "wat", tZONE, HOUR( 1) }, /* West Africa */
X { "at", tZONE, HOUR( 2) }, /* Azores */
X#if 0
X /* For completeness. BST is also British Summer, and GST is
X * also Guam Standard. */
X { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
X { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
X#endif
X { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
X { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
X { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
X { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
X { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
X { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
X { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
X { "cst", tZONE, HOUR( 6) }, /* Central Standard */
X { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
X { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
X { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
X { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
X { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
X { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
X { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
X { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
X { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
X { "cat", tZONE, HOUR(10) }, /* Central Alaska */
X { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
X { "nt", tZONE, HOUR(11) }, /* Nome */
X { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
X { "cet", tZONE, -HOUR(1) }, /* Central European */
X { "met", tZONE, -HOUR(1) }, /* Middle European */
X { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
X { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
X { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
X { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
X { "fwt", tZONE, -HOUR(1) }, /* French Winter */
X { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
X { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
X { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
X { "it", tZONE, -HOUR(3.5) },/* Iran */
X { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
X { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
X { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
X { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
X#if 0
X /* For completeness. NST is also Newfoundland Stanard, nad SST is
X * also Swedish Summer. */
X { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
X { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
X#endif /* 0 */
X { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
X { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
X { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
X { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
X { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
X { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
X { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
X { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
X { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
X { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
X { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
X { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
X { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
X { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
X { NULL }
X};
X
X/* Military timezone table. */
Xstatic TABLE MilitaryTable[] = {
X { "a", tZONE, HOUR( 1) },
X { "b", tZONE, HOUR( 2) },
X { "c", tZONE, HOUR( 3) },
X { "d", tZONE, HOUR( 4) },
X { "e", tZONE, HOUR( 5) },
X { "f", tZONE, HOUR( 6) },
X { "g", tZONE, HOUR( 7) },
X { "h", tZONE, HOUR( 8) },
X { "i", tZONE, HOUR( 9) },
X { "k", tZONE, HOUR( 10) },
X { "l", tZONE, HOUR( 11) },
X { "m", tZONE, HOUR( 12) },
X { "n", tZONE, HOUR(- 1) },
X { "o", tZONE, HOUR(- 2) },
X { "p", tZONE, HOUR(- 3) },
X { "q", tZONE, HOUR(- 4) },
X { "r", tZONE, HOUR(- 5) },
X { "s", tZONE, HOUR(- 6) },
X { "t", tZONE, HOUR(- 7) },
X { "u", tZONE, HOUR(- 8) },
X { "v", tZONE, HOUR(- 9) },
X { "w", tZONE, HOUR(-10) },
X { "x", tZONE, HOUR(-11) },
X { "y", tZONE, HOUR(-12) },
X { "z", tZONE, HOUR( 0) },
X { NULL }
X};
X
X
X
X
X/* ARGSUSED */
Xstatic
Xyyerror(s)
X char *s;
X{
X}
X
X
Xstatic time_t
XToSeconds(Hours, Minutes, Seconds, Meridian)
X time_t Hours;
X time_t Minutes;
X time_t Seconds;
X MERIDIAN Meridian;
X{
X if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
X return -1;
X switch (Meridian) {
X case MER24:
X if (Hours < 0 || Hours > 23)
X return -1;
X return (Hours * 60L + Minutes) * 60L + Seconds;
X case MERam:
X if (Hours < 1 || Hours > 12)
X return -1;
X return (Hours * 60L + Minutes) * 60L + Seconds;
X case MERpm:
X if (Hours < 1 || Hours > 12)
X return -1;
X return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
X }
X /* NOTREACHED */
X}
X
X
Xstatic time_t
XConvert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
X time_t Month;
X time_t Day;
X time_t Year;
X time_t Hours;
X time_t Minutes;
X time_t Seconds;
X MERIDIAN Meridian;
X DSTMODE DSTmode;
X{
X static int DaysInMonth[12] = {
X 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X };
X time_t tod;
X time_t Julian;
X int i;
X
X if (Year < 0)
X Year = -Year;
X if (Year < 100)
X Year += 1900;
X DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
X ? 29 : 28;
X if (Year < EPOCH || Year > 1999
X || Month < 1 || Month > 12
X /* Lint fluff: "conversion from long may lose accuracy" */
X || Day < 1 || Day > DaysInMonth[(int)--Month])
X return -1;
X
X for (Julian = Day - 1, i = 0; i < Month; i++)
X Julian += DaysInMonth[i];
X for (i = EPOCH; i < Year; i++)
X Julian += 365 + (i % 4 == 0);
X Julian *= SECSPERDAY;
X Julian += yyTimezone * 60L;
X if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
X return -1;
X Julian += tod;
X if (DSTmode == DSTon
X || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
X Julian -= 60 * 60;
X return Julian;
X}
X
X
Xstatic time_t
XDSTcorrect(Start, Future)
X time_t Start;
X time_t Future;
X{
X time_t StartDay;
X time_t FutureDay;
X
X StartDay = (localtime(&Start)->tm_hour + 1) % 24;
X FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
X return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
X}
X
X
Xstatic time_t
XRelativeDate(Start, DayOrdinal, DayNumber)
X time_t Start;
X time_t DayOrdinal;
X time_t DayNumber;
X{
X struct tm *tm;
X time_t now;
X
X now = Start;
X tm = localtime(&now);
X now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
X now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
X return DSTcorrect(Start, now);
X}
X
X
Xstatic time_t
XRelativeMonth(Start, RelMonth)
X time_t Start;
X time_t RelMonth;
X{
X struct tm *tm;
X time_t Month;
X time_t Year;
X
X if (RelMonth == 0)
X return 0;
X tm = localtime(&Start);
X Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
X Year = Month / 12;
X Month = Month % 12 + 1;
X return DSTcorrect(Start,
X Convert(Month, (time_t)tm->tm_mday, Year,
X (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
X MER24, DSTmaybe));
X}
X
X
Xstatic int
XLookupWord(buff)
X char *buff;
X{
X register char *p;
X register char *q;
X register TABLE *tp;
X int i;
X int abbrev;
X
X /* Make it lowercase. */
X for (p = buff; *p; p++)
X if (isupper(*p))
X *p = tolower(*p);
X
X if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
X yylval.Meridian = MERam;
X return tMERIDIAN;
X }
X if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
X yylval.Meridian = MERpm;
X return tMERIDIAN;
X }
X
X /* See if we have an abbreviation for a month. */
X if (strlen(buff) == 3)
X abbrev = 1;
X else if (strlen(buff) == 4 && buff[3] == '.') {
X abbrev = 1;
X buff[3] = '\0';
X }
X else
X abbrev = 0;
X
X for (tp = MonthDayTable; tp->name; tp++) {
X if (abbrev) {
X if (strncmp(buff, tp->name, 3) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X else if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X for (tp = TimezoneTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X for (tp = UnitsTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X /* Strip off any plural and try the units table again. */
X i = strlen(buff) - 1;
X if (buff[i] == 's') {
X buff[i] = '\0';
X for (tp = UnitsTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X for (tp = OtherTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X /* Military timezones. */
X if (buff[1] == '\0' && isalpha(*buff)) {
X for (tp = MilitaryTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X /* Drop out any periods and try the timezone table again. */
X for (i = 0, p = q = buff; *q; q++)
X if (*q != '.')
X *p++ = *q;
X else
X i++;
X *p = '\0';
X if (i)
X for (tp = TimezoneTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X return tID;
X}
X
X
Xstatic int
Xyylex()
X{
X register char c;
X register char *p;
X char buff[20];
X int Count;
X int sign;
X
X for ( ; ; ) {
X while (isspace(*yyInput))
X yyInput++;
X
X if (isdigit(c = *yyInput) || c == '-' || c == '+') {
X if (c == '-' || c == '+') {
X sign = c == '-' ? -1 : 1;
X if (!isdigit(*++yyInput))
X /* skip the '-' sign */
X continue;
X }
X else
X sign = 0;
X for (yylval.Number = 0; isdigit(c = *yyInput++); )
X yylval.Number = 10 * yylval.Number + c - '0';
X yyInput--;
X if (sign < 0)
X yylval.Number = -yylval.Number;
X return sign ? tSNUMBER : tUNUMBER;
X }
X if (isalpha(c)) {
X for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
X if (p < &buff[sizeof buff - 1])
X *p++ = c;
X *p = '\0';
X yyInput--;
X return LookupWord(buff);
X }
X if (c != '(')
X return *yyInput++;
X Count = 0;
X do {
X c = *yyInput++;
X if (c == '\0')
X return c;
X if (c == '(')
X Count++;
X else if (c == ')')
X Count--;
X } while (Count > 0);
X }
X}
X
X
Xstatic time_t
Xgetdate(p, now)
X char *p;
X struct timeb *now;
X{
X struct tm *tm;
X struct timeb ftz;
X time_t Start;
X time_t tod;
X
X yyInput = p;
X if (now == NULL) {
X now = &ftz;
X#if defined(NEED_TZSET)
X (void)time(&ftz.time);
X /* Set the timezone global. */
X tzset();
X ftz.timezone = (int) timezone / 60;
X#else
X (void)ftime(&ftz);
X#endif /* defined(NEED_TZSET) */
X }
X
X tm = localtime(&now->time);
X yyYear = tm->tm_year;
X yyMonth = tm->tm_mon + 1;
X yyDay = tm->tm_mday;
X yyTimezone = now->timezone;
X yyDSTmode = DSTmaybe;
X yyHour = 0;
X yyMinutes = 0;
X yySeconds = 0;
X yyMeridian = MER24;
X yyRelSeconds = 0;
X yyRelMonth = 0;
X yyHaveDate = 0;
X yyHaveDay = 0;
X yyHaveRel = 0;
X yyHaveTime = 0;
X yyHaveZone = 0;
X
X if (yyparse()
X || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
X return -1;
X
X if (yyHaveDate || yyHaveTime || yyHaveDay) {
X Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
X yyMeridian, yyDSTmode);
X if (Start < 0)
X return -1;
X }
X else {
X Start = now->time;
X if (!yyHaveRel)
X Start -= ((tm->tm_hour * 60L) + tm->tm_min * 60L) + tm->tm_sec;
X }
X
X Start += yyRelSeconds;
X Start += RelativeMonth(Start, yyRelMonth);
X
X if (yyHaveDay && !yyHaveDate) {
X tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
X Start += tod;
X }
X
X /* Have to do *something* with a legitimate -1 so it's distinguishable
X * from the error return value. (Alternately could set errno on error.) */
X return Start == -1 ? 0 : Start;
X}
X
X
X#if defined(TEST)
X
X/* ARGSUSED */
Xmain(ac, av)
X int ac;
X char *av[];
X{
X char buff[128];
X time_t d;
X
X (void)printf("Enter date, or blank line to exit.\n\t> ");
X (void)fflush(stdout);
X while (gets(buff) && buff[0]) {
X d = getdate(buff, (struct timeb *)NULL);
X if (d == -1)
X (void)printf("Bad format - couldn't convert.\n");
X else
X (void)printf("%s", ctime(&d));
X (void)printf("\t> ");
X (void)fflush(stdout);
X }
X exit(0);
X /* NOTREACHED */
X}
X#endif /* defined(TEST) */
END_OF_FILE
if test 20830 -ne `wc -c <'getdate.y'`; then
echo shar: \"'getdate.y'\" unpacked with wrong size!
fi
# end of 'getdate.y'
fi
echo shar: End of archive.
exit 0
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Alt.sources
mailing list