3
** Originally written by Steven M. Bellovin <smb@research.att.com> while
4
** at the University of North Carolina at Chapel Hill. Later tweaked by
5
** a couple of people on Usenet. Completely overhauled by Rich $alz
6
** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
8
** This grammar has 10 shift/reduce conflicts.
10
** This code is in the public domain and has no copyright.
12
/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13
/* SUPPRESS 288 on yyerrlab *//* Label unused */
17
#include <sys/types.h>
27
#endif /* STDC_HEADERS */
29
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
33
#endif /* HAVE_STRING_H */
36
#endif /* HAVE_STRINGS_H */
37
#if TIME_WITH_SYS_TIME
46
#define HOUR(x) ((time_t)(x) * 60)
47
#define SECSPERDAY (24L * 60L * 60L)
51
** An entry in the lexical lookup table.
53
typedef struct _TABLE {
61
** Daylight-savings mode: on, off, or not yet known.
63
typedef enum _DSTMODE {
64
DSTon, DSToff, DSTmaybe
68
** Meridian: am, pm, or 24-hour style.
70
typedef enum _MERIDIAN {
76
** Global variables. We could get rid of most of these by using a good
77
** union as the yacc stack. (This routine was originally written before
78
** yacc had the %union construct.) Maybe someday; right now we only use
79
** the %union very rarely.
82
static DSTMODE yyDSTmode;
83
static time_t yyDayOrdinal;
84
static time_t yyDayNumber;
85
static int yyHaveDate;
88
static int yyHaveTime;
89
static int yyHaveZone;
90
static time_t yyTimezone;
93
static time_t yyMinutes;
94
static time_t yyMonth;
95
static time_t yySeconds;
97
static MERIDIAN yyMeridian;
98
static time_t yyRelMonth;
99
static time_t yyRelSeconds;
101
static int yyerror(char *s);
102
static int yylex(void);
109
enum _MERIDIAN Meridian;
112
%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
113
%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
115
%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
116
%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
117
%type <Meridian> tMERIDIAN o_merid
143
time : tUNUMBER tMERIDIAN {
149
| tUNUMBER ':' tUNUMBER o_merid {
155
| tUNUMBER ':' tUNUMBER tSNUMBER {
160
yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
162
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
168
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
174
yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
207
date : tUNUMBER '/' tUNUMBER {
211
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
222
| tUNUMBER tSNUMBER tSNUMBER {
223
/* ISO 8601 format. yyyy-mm-dd. */
228
| tUNUMBER tMONTH tSNUMBER {
229
/* e.g. 17-JUN-1992. */
238
| tMONTH tUNUMBER ',' tUNUMBER {
247
| tUNUMBER tMONTH tUNUMBER {
255
yyRelSeconds = -yyRelSeconds;
256
yyRelMonth = -yyRelMonth;
261
relunit : tUNUMBER tMINUTE_UNIT {
262
yyRelSeconds += $1 * $2 * 60L;
264
| tSNUMBER tMINUTE_UNIT {
265
yyRelSeconds += $1 * $2 * 60L;
268
yyRelSeconds += $1 * 60L;
270
| tSNUMBER tSEC_UNIT {
273
| tUNUMBER tSEC_UNIT {
279
| tSNUMBER tMONTH_UNIT {
280
yyRelMonth += $1 * $2;
282
| tUNUMBER tMONTH_UNIT {
283
yyRelMonth += $1 * $2;
291
if (yyHaveTime && yyHaveDate && !yyHaveRel)
297
yyMonth= ($1/100)%100;
308
yyMinutes = $1 % 100;
317
o_merid : /* NULL */ {
327
/* Month and day table. */
328
static TABLE const MonthDayTable[] = {
329
{ "january", tMONTH, 1 },
330
{ "february", tMONTH, 2 },
331
{ "march", tMONTH, 3 },
332
{ "april", tMONTH, 4 },
333
{ "may", tMONTH, 5 },
334
{ "june", tMONTH, 6 },
335
{ "july", tMONTH, 7 },
336
{ "august", tMONTH, 8 },
337
{ "september", tMONTH, 9 },
338
{ "sept", tMONTH, 9 },
339
{ "october", tMONTH, 10 },
340
{ "november", tMONTH, 11 },
341
{ "december", tMONTH, 12 },
342
{ "sunday", tDAY, 0 },
343
{ "monday", tDAY, 1 },
344
{ "tuesday", tDAY, 2 },
346
{ "wednesday", tDAY, 3 },
347
{ "wednes", tDAY, 3 },
348
{ "thursday", tDAY, 4 },
350
{ "thurs", tDAY, 4 },
351
{ "friday", tDAY, 5 },
352
{ "saturday", tDAY, 6 },
356
/* Time units table. */
357
static TABLE const UnitsTable[] = {
358
{ "year", tMONTH_UNIT, 12 },
359
{ "month", tMONTH_UNIT, 1 },
360
{ "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
361
{ "week", tMINUTE_UNIT, 7 * 24 * 60 },
362
{ "day", tMINUTE_UNIT, 1 * 24 * 60 },
363
{ "hour", tMINUTE_UNIT, 60 },
364
{ "minute", tMINUTE_UNIT, 1 },
365
{ "min", tMINUTE_UNIT, 1 },
366
{ "second", tSEC_UNIT, 1 },
367
{ "sec", tSEC_UNIT, 1 },
371
/* Assorted relative-time words. */
372
static TABLE const OtherTable[] = {
373
{ "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
374
{ "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
375
{ "today", tMINUTE_UNIT, 0 },
376
{ "now", tMINUTE_UNIT, 0 },
377
{ "last", tUNUMBER, -1 },
378
{ "this", tMINUTE_UNIT, 0 },
379
{ "next", tUNUMBER, 2 },
380
{ "first", tUNUMBER, 1 },
381
/* { "second", tUNUMBER, 2 }, */
382
{ "third", tUNUMBER, 3 },
383
{ "fourth", tUNUMBER, 4 },
384
{ "fifth", tUNUMBER, 5 },
385
{ "sixth", tUNUMBER, 6 },
386
{ "seventh", tUNUMBER, 7 },
387
{ "eighth", tUNUMBER, 8 },
388
{ "ninth", tUNUMBER, 9 },
389
{ "tenth", tUNUMBER, 10 },
390
{ "eleventh", tUNUMBER, 11 },
391
{ "twelfth", tUNUMBER, 12 },
396
/* The timezone table. */
397
/* Some of these are commented out because a time_t can't store a float. */
398
static TABLE const TimezoneTable[] = {
399
{ "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
400
{ "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
401
{ "utc", tZONE, HOUR( 0) },
402
{ "wet", tZONE, HOUR( 0) }, /* Western European */
403
{ "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
404
{ "wat", tZONE, HOUR( 1) }, /* West Africa */
405
{ "at", tZONE, HOUR( 2) }, /* Azores */
407
/* For completeness. BST is also British Summer, and GST is
408
* also Guam Standard. */
409
{ "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
410
{ "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
413
{ "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
414
{ "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
415
{ "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
417
{ "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
418
{ "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
419
{ "est", tZONE, HOUR( 5) }, /* Eastern Standard */
420
{ "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
421
{ "cst", tZONE, HOUR( 6) }, /* Central Standard */
422
{ "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
423
{ "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
424
{ "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
425
{ "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
426
{ "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
427
{ "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
428
{ "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
429
{ "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
430
{ "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
431
{ "cat", tZONE, HOUR(10) }, /* Central Alaska */
432
{ "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
433
{ "nt", tZONE, HOUR(11) }, /* Nome */
434
{ "idlw", tZONE, HOUR(12) }, /* International Date Line West */
435
{ "cet", tZONE, -HOUR(1) }, /* Central European */
436
{ "met", tZONE, -HOUR(1) }, /* Middle European */
437
{ "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
438
{ "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
439
{ "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
440
{ "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
441
{ "fwt", tZONE, -HOUR(1) }, /* French Winter */
442
{ "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
443
{ "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
444
{ "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
446
{ "it", tZONE, -HOUR(3.5) },/* Iran */
448
{ "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
449
{ "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
451
{ "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
453
{ "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
455
/* For completeness. NST is also Newfoundland Stanard, and SST is
456
* also Swedish Summer. */
457
{ "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
458
{ "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
460
{ "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
461
{ "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
463
{ "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
465
{ "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
466
{ "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
468
{ "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
469
{ "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
471
{ "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
472
{ "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
473
{ "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
474
{ "nzt", tZONE, -HOUR(12) }, /* New Zealand */
475
{ "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
476
{ "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
477
{ "idle", tZONE, -HOUR(12) }, /* International Date Line East */
481
/* Military timezone table. */
482
static TABLE const MilitaryTable[] = {
483
{ "a", tZONE, HOUR( 1) },
484
{ "b", tZONE, HOUR( 2) },
485
{ "c", tZONE, HOUR( 3) },
486
{ "d", tZONE, HOUR( 4) },
487
{ "e", tZONE, HOUR( 5) },
488
{ "f", tZONE, HOUR( 6) },
489
{ "g", tZONE, HOUR( 7) },
490
{ "h", tZONE, HOUR( 8) },
491
{ "i", tZONE, HOUR( 9) },
492
{ "k", tZONE, HOUR( 10) },
493
{ "l", tZONE, HOUR( 11) },
494
{ "m", tZONE, HOUR( 12) },
495
{ "n", tZONE, HOUR(- 1) },
496
{ "o", tZONE, HOUR(- 2) },
497
{ "p", tZONE, HOUR(- 3) },
498
{ "q", tZONE, HOUR(- 4) },
499
{ "r", tZONE, HOUR(- 5) },
500
{ "s", tZONE, HOUR(- 6) },
501
{ "t", tZONE, HOUR(- 7) },
502
{ "u", tZONE, HOUR(- 8) },
503
{ "v", tZONE, HOUR(- 9) },
504
{ "w", tZONE, HOUR(-10) },
505
{ "x", tZONE, HOUR(-11) },
506
{ "y", tZONE, HOUR(-12) },
507
{ "z", tZONE, HOUR( 0) },
524
ToSeconds(Hours, Minutes, Seconds, Meridian)
530
if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
534
if (Hours < 0 || Hours > 23)
536
return (Hours * 60L + Minutes) * 60L + Seconds;
538
if (Hours < 1 || Hours > 12)
542
return (Hours * 60L + Minutes) * 60L + Seconds;
544
if (Hours < 1 || Hours > 12)
548
return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
557
* A negative number, which means to use its absolute value (why?)
558
* A number from 0 to 99, which means a year from 1900 to 1999, or
559
* The actual year (>=100). */
561
Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
571
static int DaysInMonth[12] = {
572
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
582
else if (Year < 100) {
587
DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
589
/* Checking for 2038 bogusly assumes that time_t is 32 bits. But
590
I'm too lazy to try to check for time_t overflow in another way. */
591
if (Year < EPOCH || Year > 2038
592
|| Month < 1 || Month > 12
593
/* Lint fluff: "conversion from long may lose accuracy" */
594
|| Day < 1 || Day > DaysInMonth[(int)--Month])
597
for (Julian = Day - 1, i = 0; i < Month; i++)
598
Julian += DaysInMonth[i];
599
for (i = EPOCH; i < Year; i++)
600
Julian += 365 + (i % 4 == 0);
601
Julian *= SECSPERDAY;
602
Julian += yyTimezone * 60L;
603
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
607
|| (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
614
DSTcorrect(Start, Future)
621
StartDay = (localtime(&Start)->tm_hour + 1) % 24;
622
FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
623
return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
628
RelativeDate(Start, DayOrdinal, DayNumber)
637
tm = localtime(&now);
638
now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
639
now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
640
return DSTcorrect(Start, now);
645
RelativeMonth(Start, RelMonth)
655
tm = localtime(&Start);
656
Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
658
Month = Month % 12 + 1;
659
return DSTcorrect(Start,
660
Convert(Month, (time_t)tm->tm_mday, Year,
661
(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
676
/* Make it lowercase. */
677
for (p = buff; *p; p++)
678
if (isupper((unsigned char)*p))
679
*p = tolower((unsigned char)*p);
681
if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
682
yylval.Meridian = MERam;
685
if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
686
yylval.Meridian = MERpm;
690
/* See if we have an abbreviation for a month. */
691
if (strlen(buff) == 3)
693
else if (strlen(buff) == 4 && buff[3] == '.') {
700
for (tp = MonthDayTable; tp->name; tp++) {
702
if (strncmp(buff, tp->name, 3) == 0) {
703
yylval.Number = tp->value;
707
else if (strcmp(buff, tp->name) == 0) {
708
yylval.Number = tp->value;
713
for (tp = TimezoneTable; tp->name; tp++)
714
if (strcmp(buff, tp->name) == 0) {
715
yylval.Number = tp->value;
719
if (strcmp(buff, "dst") == 0)
722
for (tp = UnitsTable; tp->name; tp++)
723
if (strcmp(buff, tp->name) == 0) {
724
yylval.Number = tp->value;
728
/* Strip off any plural and try the units table again. */
729
i = strlen(buff) - 1;
730
if (buff[i] == 's') {
732
for (tp = UnitsTable; tp->name; tp++)
733
if (strcmp(buff, tp->name) == 0) {
734
yylval.Number = tp->value;
737
buff[i] = 's'; /* Put back for "this" in OtherTable. */
740
for (tp = OtherTable; tp->name; tp++)
741
if (strcmp(buff, tp->name) == 0) {
742
yylval.Number = tp->value;
746
/* Military timezones. */
747
if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
748
for (tp = MilitaryTable; tp->name; tp++)
749
if (strcmp(buff, tp->name) == 0) {
750
yylval.Number = tp->value;
755
/* Drop out any periods and try the timezone table again. */
756
for (i = 0, p = q = buff; *q; q++)
763
for (tp = TimezoneTable; tp->name; tp++)
764
if (strcmp(buff, tp->name) == 0) {
765
yylval.Number = tp->value;
783
while (isspace((unsigned char)*yyInput))
786
if (isdigit((unsigned char)(c = *yyInput)) || c == '-' || c == '+') {
787
if (c == '-' || c == '+') {
788
sign = c == '-' ? -1 : 1;
789
if (!isdigit((unsigned char)*++yyInput))
790
/* skip the '-' sign */
795
for (yylval.Number = 0; isdigit((unsigned char)(c = *yyInput++)); )
796
yylval.Number = 10 * yylval.Number + c - '0';
799
yylval.Number = -yylval.Number;
800
return sign ? tSNUMBER : tUNUMBER;
802
if (isalpha((unsigned char)c)) {
803
for (p = buff; isalpha((unsigned char)(c = *yyInput++)) || c == '.'; )
804
if (p < &buff[sizeof buff - 1])
808
return LookupWord(buff);
825
#define TM_YEAR_ORIGIN 1900
827
/* Yield A - B, measured in seconds. */
832
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
833
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
835
/* difference in day of year */
836
a->tm_yday - b->tm_yday
837
/* + intervening leap days */
838
+ ((ay >> 2) - (by >> 2))
840
+ ((ay/100 >> 2) - (by/100 >> 2))
841
/* + difference in years * 365 */
842
+ (long)(ay-by) * 365
844
return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
845
+ (a->tm_min - b->tm_min))
846
+ (a->tm_sec - b->tm_sec));
853
struct tm *tm, *gmt, gmtbuf;
865
/* Make a copy, in case localtime modifies *tm (I think
866
that comment now applies to *gmt, but I am too
867
lazy to dig into how gmtime and locatime allocate the
868
structures they return pointers to). */
873
if (! (tm = localtime (&now)))
877
timezone = difftm (gmt, tm) / 60;
879
/* We are on a system like VMS, where the system clock is
880
in local time and the system has no concept of timezones.
881
Hopefully we can fake this out (for the case in which the
882
user specifies no timezone) by just saying the timezone
889
tm = localtime(&now);
890
yyYear = tm->tm_year + 1900;
891
yyMonth = tm->tm_mon + 1;
893
yyTimezone = timezone;
894
yyDSTmode = DSTmaybe;
908
|| yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
911
if (yyHaveDate || yyHaveTime || yyHaveDay) {
912
Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
913
yyMeridian, yyDSTmode);
920
Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
923
Start += yyRelSeconds;
924
Start += RelativeMonth(Start, yyRelMonth);
926
if (yyHaveDay && !yyHaveDate) {
927
tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
931
/* Have to do *something* with a legitimate -1 so it's distinguishable
932
* from the error return value. (Alternately could set errno on error.) */
933
return Start == -1 ? 0 : Start;
948
(void)printf("Enter date, or blank line to exit.\n\t> ");
949
(void)fflush(stdout);
950
while (gets(buff) && buff[0]) {
953
(void)printf("Bad format - couldn't convert.\n");
955
(void)printf("%s", ctime(&d));
956
(void)printf("\t> ");
957
(void)fflush(stdout);
962
#endif /* defined(TEST) */