parsetime.c revision 17221
110154Sache/*
27767Sache *  parsetime.c - parse time for at(1)
37767Sache *  Copyright (C) 1993, 1994  Thomas Koenig
4941Snate *
57767Sache *  modifications for english-language times
67767Sache *  Copyright (C) 1993  David Parsons
7941Snate *
8941Snate * Redistribution and use in source and binary forms, with or without
9941Snate * modification, are permitted provided that the following conditions
10941Snate * are met:
11941Snate * 1. Redistributions of source code must retain the above copyright
12941Snate *    notice, this list of conditions and the following disclaimer.
13941Snate * 2. The name of the author(s) may not be used to endorse or promote
14941Snate *    products derived from this software without specific prior written
15941Snate *    permission.
16941Snate *
17941Snate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18941Snate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19941Snate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2010154Sache * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21941Snate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22941Snate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23941Snate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24941Snate * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25941Snate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26941Snate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27941Snate *
28941Snate *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
29941Snate *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
30941Snate *     |NOON                       | |[TOMORROW]                          |
317767Sache *     |MIDNIGHT                   | |[DAY OF WEEK]                       |
327767Sache *     \TEATIME                    / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
337767Sache *                                   \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
34941Snate */
35941Snate
36941Snate/* System Headers */
37941Snate
387767Sache
39941Snate#include <sys/types.h>
40941Snate#include <errno.h>
41941Snate#include <stdio.h>
42941Snate#include <stdlib.h>
43941Snate#include <string.h>
44941Snate#include <time.h>
45941Snate#include <unistd.h>
46941Snate#include <ctype.h>
477767Sache#ifndef __FreeBSD__
487767Sache#include <getopt.h>
497767Sache#endif
50941Snate
51941Snate/* Local headers */
52941Snate
53941Snate#include "at.h"
54941Snate#include "panic.h"
55941Snate
56941Snate
57941Snate/* Structures and unions */
58941Snate
5910154Sacheenum {	/* symbols */
60941Snate    MIDNIGHT, NOON, TEATIME,
61941Snate    PM, AM, TOMORROW, TODAY, NOW,
62941Snate    MINUTES, HOURS, DAYS, WEEKS,
63941Snate    NUMBER, PLUS, DOT, SLASH, ID, JUNK,
64941Snate    JAN, FEB, MAR, APR, MAY, JUN,
657767Sache    JUL, AUG, SEP, OCT, NOV, DEC,
667767Sache    SUN, MON, TUE, WED, THU, FRI, SAT
677767Sache    };
68941Snate
697767Sache/* parse translation table - table driven parsers can be your FRIEND!
70941Snate */
71941Snatestruct {
7210154Sache    char *name;	/* token name */
7310154Sache    int value;	/* token id */
747767Sache    int plural;	/* is this plural? */
75941Snate} Specials[] = {
767767Sache    { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
777767Sache    { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
787767Sache    { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
797767Sache    { "am", AM,0 },		/* morning times for 0-12 clock */
807767Sache    { "pm", PM,0 },		/* evening times for 0-12 clock */
817767Sache    { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
827767Sache    { "today", TODAY, 0 },	/* execute today - don't advance time */
837767Sache    { "now", NOW,0 },		/* opt prefix for PLUS */
84941Snate
857767Sache    { "minute", MINUTES,0 },	/* minutes multiplier */
867767Sache    { "minutes", MINUTES,1 },	/* (pluralized) */
877767Sache    { "hour", HOURS,0 },	/* hours ... */
887767Sache    { "hours", HOURS,1 },	/* (pluralized) */
897767Sache    { "day", DAYS,0 },		/* days ... */
907767Sache    { "days", DAYS,1 },		/* (pluralized) */
917767Sache    { "week", WEEKS,0 },	/* week ... */
927767Sache    { "weeks", WEEKS,1 },	/* (pluralized) */
937767Sache    { "jan", JAN,0 },
947767Sache    { "feb", FEB,0 },
957767Sache    { "mar", MAR,0 },
967767Sache    { "apr", APR,0 },
977767Sache    { "may", MAY,0 },
987767Sache    { "jun", JUN,0 },
997767Sache    { "jul", JUL,0 },
1007767Sache    { "aug", AUG,0 },
1017767Sache    { "sep", SEP,0 },
1027767Sache    { "oct", OCT,0 },
1037767Sache    { "nov", NOV,0 },
1047767Sache    { "dec", DEC,0 },
1057767Sache    { "sunday", SUN, 0 },
1067767Sache    { "sun", SUN, 0 },
1077767Sache    { "monday", MON, 0 },
1087767Sache    { "mon", MON, 0 },
1097767Sache    { "tuesday", TUE, 0 },
1107767Sache    { "tue", TUE, 0 },
1117767Sache    { "wednesday", WED, 0 },
1127767Sache    { "wed", WED, 0 },
1137767Sache    { "thursday", THU, 0 },
1147767Sache    { "thu", THU, 0 },
1157767Sache    { "friday", FRI, 0 },
1167767Sache    { "fri", FRI, 0 },
1177767Sache    { "saturday", SAT, 0 },
1187767Sache    { "sat", SAT, 0 },
119941Snate} ;
120941Snate
121941Snate/* File scope variables */
122941Snate
123941Snatestatic char **scp;	/* scanner - pointer at arglist */
124941Snatestatic char scc;	/* scanner - count of remaining arguments */
125941Snatestatic char *sct;	/* scanner - next char pointer in current argument */
126941Snatestatic int need;	/* scanner - need to advance to next argument */
127941Snate
128941Snatestatic char *sc_token;	/* scanner - token buffer */
129941Snatestatic size_t sc_len;   /* scanner - lenght of token buffer */
13010154Sachestatic int sc_tokid;	/* scanner - token id */
1317767Sachestatic int sc_tokplur;	/* scanner - is token plural? */
132941Snate
13317221Sjdpstatic char rcsid[] = "$Id: parsetime.c,v 1.6 1995/08/21 12:32:50 ache Exp $";
134941Snate
135941Snate/* Local functions */
136941Snate
137941Snate/*
138941Snate * parse a token, checking if it's something special to us
139941Snate */
14010154Sachestatic int
14110154Sacheparse_token(char *arg)
142941Snate{
143941Snate    int i;
144941Snate
145941Snate    for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
146941Snate	if (strcasecmp(Specials[i].name, arg) == 0) {
1477767Sache	    sc_tokplur = Specials[i].plural;
148941Snate	    return sc_tokid = Specials[i].value;
149941Snate	}
150941Snate
151941Snate    /* not special - must be some random id */
152941Snate    return ID;
153941Snate} /* parse_token */
154941Snate
155941Snate
156941Snate/*
157941Snate * init_scanner() sets up the scanner to eat arguments
158941Snate */
159941Snatestatic void
1607767Sacheinit_scanner(int argc, char **argv)
161941Snate{
162941Snate    scp = argv;
163941Snate    scc = argc;
164941Snate    need = 1;
165941Snate    sc_len = 1;
16610154Sache    while (argc-- > 0)
16710154Sache	sc_len += strlen(*argv++);
168941Snate
1697767Sache    sc_token = (char *) mymalloc(sc_len);
170941Snate} /* init_scanner */
171941Snate
172941Snate/*
173941Snate * token() fetches a token from the input stream
174941Snate */
17510154Sachestatic int
176941Snatetoken()
177941Snate{
178941Snate    int idx;
179941Snate
180941Snate    while (1) {
181941Snate	memset(sc_token, 0, sc_len);
182941Snate	sc_tokid = EOF;
1837767Sache	sc_tokplur = 0;
184941Snate	idx = 0;
185941Snate
1867767Sache	/* if we need to read another argument, walk along the argument list;
187941Snate	 * when we fall off the arglist, we'll just return EOF forever
188941Snate	 */
189941Snate	if (need) {
190941Snate	    if (scc < 1)
191941Snate		return sc_tokid;
192941Snate	    sct = *scp;
193941Snate	    scp++;
194941Snate	    scc--;
195941Snate	    need = 0;
196941Snate	}
1977767Sache	/* eat whitespace now - if we walk off the end of the argument,
198941Snate	 * we'll continue, which puts us up at the top of the while loop
199941Snate	 * to fetch the next argument in
200941Snate	 */
201941Snate	while (isspace(*sct))
202941Snate	    ++sct;
203941Snate	if (!*sct) {
204941Snate	    need = 1;
205941Snate	    continue;
206941Snate	}
207941Snate
2087767Sache	/* preserve the first character of the new token
209941Snate	 */
210941Snate	sc_token[0] = *sct++;
211941Snate
2127767Sache	/* then see what it is
213941Snate	 */
214941Snate	if (isdigit(sc_token[0])) {
215941Snate	    while (isdigit(*sct))
216941Snate		sc_token[++idx] = *sct++;
217941Snate	    sc_token[++idx] = 0;
218941Snate	    return sc_tokid = NUMBER;
2197767Sache	}
2207767Sache	else if (isalpha(sc_token[0])) {
221941Snate	    while (isalpha(*sct))
222941Snate		sc_token[++idx] = *sct++;
223941Snate	    sc_token[++idx] = 0;
224941Snate	    return parse_token(sc_token);
225941Snate	}
226941Snate	else if (sc_token[0] == ':' || sc_token[0] == '.')
227941Snate	    return sc_tokid = DOT;
228941Snate	else if (sc_token[0] == '+')
229941Snate	    return sc_tokid = PLUS;
2307767Sache	else if (sc_token[0] == '/')
231941Snate	    return sc_tokid = SLASH;
232941Snate	else
233941Snate	    return sc_tokid = JUNK;
234941Snate    } /* while (1) */
235941Snate} /* token */
236941Snate
237941Snate
238941Snate/*
239941Snate * plonk() gives an appropriate error message if a token is incorrect
240941Snate */
241941Snatestatic void
2427767Sacheplonk(int tok)
243941Snate{
244941Snate    panic((tok == EOF) ? "incomplete time"
245941Snate		       : "garbled time");
246941Snate} /* plonk */
247941Snate
248941Snate
24910154Sache/*
250941Snate * expect() gets a token and dies most horribly if it's not the token we want
251941Snate */
252941Snatestatic void
25310154Sacheexpect(int desired)
254941Snate{
255941Snate    if (token() != desired)
256941Snate	plonk(sc_tokid);	/* and we die here... */
257941Snate} /* expect */
258941Snate
259941Snate
260941Snate/*
261941Snate * dateadd() adds a number of minutes to a date.  It is extraordinarily
262941Snate * stupid regarding day-of-month overflow, and will most likely not
263941Snate * work properly
264941Snate */
265941Snatestatic void
2667767Sachedateadd(int minutes, struct tm *tm)
267941Snate{
268941Snate    /* increment days */
269941Snate
270941Snate    while (minutes > 24*60) {
271941Snate	minutes -= 24*60;
272941Snate	tm->tm_mday++;
273941Snate    }
274941Snate
275941Snate    /* increment hours */
276941Snate    while (minutes > 60) {
277941Snate	minutes -= 60;
278941Snate	tm->tm_hour++;
279941Snate	if (tm->tm_hour > 23) {
280941Snate	    tm->tm_mday++;
281941Snate	    tm->tm_hour = 0;
282941Snate	}
283941Snate    }
284941Snate
285941Snate    /* increment minutes */
286941Snate    tm->tm_min += minutes;
287941Snate
288941Snate    if (tm->tm_min > 59) {
289941Snate	tm->tm_hour++;
290941Snate	tm->tm_min -= 60;
291941Snate
292941Snate	if (tm->tm_hour > 23) {
293941Snate	    tm->tm_mday++;
294941Snate	    tm->tm_hour = 0;
295941Snate	}
296941Snate    }
297941Snate} /* dateadd */
298941Snate
299941Snate
300941Snate/*
301941Snate * plus() parses a now + time
302941Snate *
303941Snate *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
304941Snate *
305941Snate */
306941Snatestatic void
3077767Sacheplus(struct tm *tm)
308941Snate{
309941Snate    int delay;
3107767Sache    int expectplur;
311941Snate
312941Snate    expect(NUMBER);
313941Snate
314941Snate    delay = atoi(sc_token);
3157767Sache    expectplur = (delay != 1) ? 1 : 0;
316941Snate
317941Snate    switch (token()) {
318941Snate    case WEEKS:
319941Snate	    delay *= 7;
320941Snate    case DAYS:
321941Snate	    delay *= 24;
322941Snate    case HOURS:
323941Snate	    delay *= 60;
324941Snate    case MINUTES:
3257767Sache	    if (expectplur != sc_tokplur)
3267767Sache		fprintf(stderr, "at: pluralization is wrong\n");
327941Snate	    dateadd(delay, tm);
328941Snate	    return;
329941Snate    }
330941Snate    plonk(sc_tokid);
331941Snate} /* plus */
332941Snate
333941Snate
334941Snate/*
335941Snate * tod() computes the time of day
336941Snate *     [NUMBER [DOT NUMBER] [AM|PM]]
337941Snate */
338941Snatestatic void
3397767Sachetod(struct tm *tm)
340941Snate{
341941Snate    int hour, minute = 0;
342941Snate    int tlen;
343941Snate
344941Snate    hour = atoi(sc_token);
345941Snate    tlen = strlen(sc_token);
346941Snate
3477767Sache    /* first pick out the time of day - if it's 4 digits, we assume
348941Snate     * a HHMM time, otherwise it's HH DOT MM time
349941Snate     */
350941Snate    if (token() == DOT) {
351941Snate	expect(NUMBER);
352941Snate	minute = atoi(sc_token);
353941Snate	if (minute > 59)
354941Snate	    panic("garbled time");
355941Snate	token();
3567767Sache    }
3577767Sache    else if (tlen == 4) {
358941Snate	minute = hour%100;
359941Snate	if (minute > 59)
360941Snate	    panic("garbeld time");
361941Snate	hour = hour/100;
362941Snate    }
363941Snate
3647767Sache    /* check if an AM or PM specifier was given
365941Snate     */
366941Snate    if (sc_tokid == AM || sc_tokid == PM) {
367941Snate	if (hour > 12)
368941Snate	    panic("garbled time");
369941Snate
37017221Sjdp	if (sc_tokid == PM) {
37117221Sjdp	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
37217221Sjdp		hour += 12;
37317221Sjdp	} else {
37417221Sjdp	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
37517221Sjdp		hour -= 12;
37617221Sjdp	}
377941Snate	token();
3787767Sache    }
3797767Sache    else if (hour > 23)
380941Snate	panic("garbled time");
381941Snate
3827767Sache    /* if we specify an absolute time, we don't want to bump the day even
383941Snate     * if we've gone past that time - but if we're specifying a time plus
384941Snate     * a relative offset, it's okay to bump things
385941Snate     */
3867767Sache    if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
387941Snate	tm->tm_mday++;
3887767Sache	tm->tm_wday++;
3897767Sache    }
390941Snate
391941Snate    tm->tm_hour = hour;
392941Snate    tm->tm_min = minute;
393941Snate    if (tm->tm_hour == 24) {
394941Snate	tm->tm_hour = 0;
395941Snate	tm->tm_mday++;
396941Snate    }
397941Snate} /* tod */
398941Snate
399941Snate
400941Snate/*
401941Snate * assign_date() assigns a date, wrapping to next year if needed
402941Snate */
403941Snatestatic void
4047767Sacheassign_date(struct tm *tm, long mday, long mon, long year)
405941Snate{
406941Snate    if (year > 99) {
407941Snate	if (year > 1899)
408941Snate	    year -= 1900;
409941Snate	else
410941Snate	    panic("garbled time");
411941Snate    }
412941Snate
413941Snate    if (year < 0 &&
414941Snate	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
415941Snate	year = tm->tm_year + 1;
416941Snate
417941Snate    tm->tm_mday = mday;
418941Snate    tm->tm_mon = mon;
419941Snate
420941Snate    if (year >= 0)
421941Snate	tm->tm_year = year;
422941Snate} /* assign_date */
423941Snate
424941Snate
42510154Sache/*
426941Snate * month() picks apart a month specification
427941Snate *
428941Snate *  /[<month> NUMBER [NUMBER]]           \
429941Snate *  |[TOMORROW]                          |
4307767Sache *  |[DAY OF WEEK]                       |
431941Snate *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
432941Snate *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
433941Snate */
434941Snatestatic void
4357767Sachemonth(struct tm *tm)
436941Snate{
437941Snate    long year= (-1);
4387767Sache    long mday, wday, mon;
439941Snate    int tlen;
440941Snate
441941Snate    switch (sc_tokid) {
442941Snate    case PLUS:
443941Snate	    plus(tm);
444941Snate	    break;
445941Snate
446941Snate    case TOMORROW:
447941Snate	    /* do something tomorrow */
448941Snate	    tm->tm_mday ++;
4497767Sache	    tm->tm_wday ++;
450941Snate    case TODAY:	/* force ourselves to stay in today - no further processing */
451941Snate	    token();
452941Snate	    break;
453941Snate
454941Snate    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
455941Snate    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
4567767Sache	    /* do month mday [year]
457941Snate	     */
458941Snate	    mon = (sc_tokid-JAN);
459941Snate	    expect(NUMBER);
4606079Sbde	    mday = atol(sc_token);
461941Snate	    if (token() == NUMBER) {
462941Snate		year = atol(sc_token);
463941Snate		token();
464941Snate	    }
465941Snate	    assign_date(tm, mday, mon, year);
466941Snate	    break;
467941Snate
4687767Sache    case SUN: case MON: case TUE:
4697767Sache    case WED: case THU: case FRI:
4707767Sache    case SAT:
4717767Sache	    /* do a particular day of the week
4727767Sache	     */
4737767Sache	    wday = (sc_tokid-SUN);
4747767Sache
4757767Sache	    mday = tm->tm_mday;
4767767Sache
4777767Sache	    /* if this day is < today, then roll to next week
4787767Sache	     */
4797767Sache	    if (wday < tm->tm_wday)
4807767Sache		mday += 7 - (tm->tm_wday - wday);
4817767Sache	    else
4827767Sache		mday += (wday - tm->tm_wday);
4837767Sache
4847767Sache	    tm->tm_wday = wday;
4857767Sache
4867767Sache	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
4877767Sache	    break;
4887767Sache
489941Snate    case NUMBER:
4907767Sache	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
491941Snate	     */
492941Snate	    tlen = strlen(sc_token);
493941Snate	    mon = atol(sc_token);
494941Snate	    token();
495941Snate
496941Snate	    if (sc_tokid == SLASH || sc_tokid == DOT) {
49710154Sache		int sep;
498941Snate
499941Snate		sep = sc_tokid;
500941Snate		expect(NUMBER);
501941Snate		mday = atol(sc_token);
502941Snate		if (token() == sep) {
503941Snate		    expect(NUMBER);
504941Snate		    year = atol(sc_token);
505941Snate		    token();
506941Snate		}
507941Snate
5087767Sache		/* flip months and days for european timing
509941Snate		 */
510941Snate		if (sep == DOT) {
511941Snate		    int x = mday;
512941Snate		    mday = mon;
513941Snate		    mon = x;
514941Snate		}
5157767Sache	    }
5167767Sache	    else if (tlen == 6 || tlen == 8) {
517941Snate		if (tlen == 8) {
518941Snate		    year = (mon % 10000) - 1900;
519941Snate		    mon /= 10000;
5207767Sache		}
5217767Sache		else {
522941Snate		    year = mon % 100;
523941Snate		    mon /= 100;
524941Snate		}
525941Snate		mday = mon % 100;
526941Snate		mon /= 100;
5277767Sache	    }
5287767Sache	    else
529941Snate		panic("garbled time");
530941Snate
531941Snate	    mon--;
532941Snate	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
533941Snate		panic("garbled time");
534941Snate
535941Snate	    assign_date(tm, mday, mon, year);
536941Snate	    break;
537941Snate    } /* case */
538941Snate} /* month */
539941Snate
540941Snate
541941Snate/* Global functions */
542941Snate
543941Snatetime_t
5447767Sacheparsetime(int argc, char **argv)
545941Snate{
5467767Sache/* Do the argument parsing, die if necessary, and return the time the job
547941Snate * should be run.
548941Snate */
549941Snate    time_t nowtimer, runtimer;
550941Snate    struct tm nowtime, runtime;
551941Snate    int hr = 0;
552941Snate    /* this MUST be initialized to zero for midnight/noon/teatime */
553941Snate
554941Snate    nowtimer = time(NULL);
555941Snate    nowtime = *localtime(&nowtimer);
556941Snate
557941Snate    runtime = nowtime;
558941Snate    runtime.tm_sec = 0;
559941Snate    runtime.tm_isdst = 0;
560941Snate
561941Snate    if (argc <= optind)
562941Snate	usage();
563941Snate
564941Snate    init_scanner(argc-optind, argv+optind);
565941Snate
566941Snate    switch (token()) {
567941Snate    case NOW:	/* now is optional prefix for PLUS tree */
568941Snate	    expect(PLUS);
569941Snate    case PLUS:
570941Snate	    plus(&runtime);
571941Snate	    break;
572941Snate
573941Snate    case NUMBER:
574941Snate	    tod(&runtime);
575941Snate	    month(&runtime);
576941Snate	    break;
577941Snate
5787767Sache	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
579941Snate	     * hr to zero up above, then fall into this case in such a
580941Snate	     * way so we add +12 +4 hours to it for teatime, +12 hours
581941Snate	     * to it for noon, and nothing at all for midnight, then
582941Snate	     * set our runtime to that hour before leaping into the
583941Snate	     * month scanner
584941Snate	     */
585941Snate    case TEATIME:
586941Snate	    hr += 4;
587941Snate    case NOON:
588941Snate	    hr += 12;
589941Snate    case MIDNIGHT:
5907767Sache	    if (runtime.tm_hour >= hr) {
591941Snate		runtime.tm_mday++;
5927767Sache		runtime.tm_wday++;
5937767Sache	    }
594941Snate	    runtime.tm_hour = hr;
595941Snate	    runtime.tm_min = 0;
596941Snate	    token();
597941Snate	    /* fall through to month setting */
598941Snate    default:
599941Snate	    month(&runtime);
600941Snate	    break;
601941Snate    } /* ugly case statement */
602941Snate    expect(EOF);
603941Snate
6047767Sache    /* adjust for daylight savings time
605941Snate     */
606941Snate    runtime.tm_isdst = -1;
607941Snate    runtimer = mktime(&runtime);
608941Snate    if (runtime.tm_isdst > 0) {
609941Snate	runtimer -= 3600;
610941Snate	runtimer = mktime(&runtime);
611941Snate    }
612941Snate
613941Snate    if (runtimer < 0)
614941Snate	panic("garbled time");
615941Snate
616941Snate    if (nowtimer > runtimer)
617941Snate	panic("Trying to travel back in time");
618941Snate
619941Snate    return runtimer;
620941Snate} /* parsetime */
621