Annotation of src/lib/libutil/parsedate.y, Revision 1.16.6.4
1.1 christos 1: %{
2: /*
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;
7: **
8: ** This grammar has 10 shift/reduce conflicts.
9: **
10: ** This code is in the public domain and has no copyright.
11: */
12: /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13: /* SUPPRESS 288 on yyerrlab *//* Label unused */
14:
1.15 yamt 15: #include <sys/cdefs.h>
16: #ifdef __RCSID
1.16.6.4! snj 17: __RCSID("$NetBSD: parsedate.y,v 1.16.6.3 2014/10/11 16:51:23 snj Exp $");
1.15 yamt 18: #endif
19:
1.1 christos 20: #include <stdio.h>
21: #include <ctype.h>
1.14 apb 22: #include <errno.h>
1.1 christos 23: #include <string.h>
24: #include <time.h>
25: #include <util.h>
1.3 drochner 26: #include <stdlib.h>
1.1 christos 27:
28: /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
29: releases):
30:
31: We don't want to mess with all the portability hassles of alloca.
32: In particular, most (all?) versions of bison will use alloca in
33: their parser. If bison works on your system (e.g. it should work
34: with gcc), then go ahead and use it, but the more general solution
35: is to use byacc instead of bison, which should generate a portable
36: parser. I played with adding "#define alloca dont_use_alloca", to
37: give an error if the parser generator uses alloca (and thus detect
38: unportable parsedate.c's), but that seems to cause as many problems
39: as it solves. */
40:
41: #define EPOCH 1970
42: #define HOUR(x) ((time_t)(x) * 60)
43: #define SECSPERDAY (24L * 60L * 60L)
44:
1.16.6.4! snj 45: #define USE_LOCAL_TIME 99999 /* special case for Convert() and yyTimezone */
1.1 christos 46:
47: /*
48: ** An entry in the lexical lookup table.
49: */
50: typedef struct _TABLE {
51: const char *name;
52: int type;
53: time_t value;
54: } TABLE;
55:
56:
57: /*
58: ** Daylight-savings mode: on, off, or not yet known.
59: */
60: typedef enum _DSTMODE {
61: DSTon, DSToff, DSTmaybe
62: } DSTMODE;
63:
64: /*
65: ** Meridian: am, pm, or 24-hour style.
66: */
67: typedef enum _MERIDIAN {
68: MERam, MERpm, MER24
69: } MERIDIAN;
70:
71:
1.9 christos 72: struct dateinfo {
1.16.6.1 snj 73: DSTMODE yyDSTmode; /* DST on/off/maybe */
1.9 christos 74: time_t yyDayOrdinal;
75: time_t yyDayNumber;
76: int yyHaveDate;
1.16.6.1 snj 77: int yyHaveFullYear; /* if true, year is not abbreviated. */
78: /* if false, need to call AdjustYear(). */
1.9 christos 79: int yyHaveDay;
80: int yyHaveRel;
81: int yyHaveTime;
82: int yyHaveZone;
1.16.6.1 snj 83: time_t yyTimezone; /* Timezone as minutes ahead/east of UTC */
84: time_t yyDay; /* Day of month [1-31] */
85: time_t yyHour; /* Hour of day [0-24] or [1-12] */
86: time_t yyMinutes; /* Minute of hour [0-59] */
87: time_t yyMonth; /* Month of year [1-12] */
88: time_t yySeconds; /* Second of minute [0-60] */
89: time_t yyYear; /* Year, see also yyHaveFullYear */
90: MERIDIAN yyMeridian; /* Interpret yyHour as AM/PM/24 hour clock */
1.9 christos 91: time_t yyRelMonth;
92: time_t yyRelSeconds;
93: };
1.1 christos 94: %}
95:
96: %union {
97: time_t Number;
98: enum _MERIDIAN Meridian;
99: }
100:
101: %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
1.5 tron 102: %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
1.1 christos 103:
104: %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
105: %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
106: %type <Meridian> tMERIDIAN o_merid
107:
1.9 christos 108: %parse-param { struct dateinfo *param }
109: %parse-param { const char **yyInput }
110: %lex-param { const char **yyInput }
111: %pure-parser
112:
1.1 christos 113: %%
114:
115: spec : /* NULL */
116: | spec item
117: ;
118:
119: item : time {
1.9 christos 120: param->yyHaveTime++;
1.1 christos 121: }
1.16.6.3 snj 122: | time_numericzone {
123: param->yyHaveTime++;
124: param->yyHaveZone++;
125: }
1.1 christos 126: | zone {
1.9 christos 127: param->yyHaveZone++;
1.1 christos 128: }
129: | date {
1.9 christos 130: param->yyHaveDate++;
1.1 christos 131: }
132: | day {
1.9 christos 133: param->yyHaveDay++;
1.1 christos 134: }
135: | rel {
1.9 christos 136: param->yyHaveRel++;
1.1 christos 137: }
138: | cvsstamp {
1.9 christos 139: param->yyHaveTime++;
140: param->yyHaveDate++;
141: param->yyHaveZone++;
1.1 christos 142: }
1.5 tron 143: | epochdate {
1.9 christos 144: param->yyHaveTime++;
145: param->yyHaveDate++;
146: param->yyHaveZone++;
1.5 tron 147: }
1.1 christos 148: | number
149: ;
150:
151: cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
1.9 christos 152: param->yyYear = $1;
153: if (param->yyYear < 100) param->yyYear += 1900;
1.16.6.1 snj 154: param->yyHaveFullYear = 1;
1.9 christos 155: param->yyMonth = $3;
156: param->yyDay = $5;
157: param->yyHour = $7;
158: param->yyMinutes = $9;
159: param->yySeconds = $11;
160: param->yyDSTmode = DSToff;
161: param->yyTimezone = 0;
1.1 christos 162: }
163: ;
164:
1.14 apb 165: epochdate: AT_SIGN at_number {
166: time_t when = $<Number>2;
1.5 tron 167: struct tm tmbuf;
168: if (gmtime_r(&when, &tmbuf) != NULL) {
1.9 christos 169: param->yyYear = tmbuf.tm_year + 1900;
170: param->yyMonth = tmbuf.tm_mon + 1;
171: param->yyDay = tmbuf.tm_mday;
172:
173: param->yyHour = tmbuf.tm_hour;
174: param->yyMinutes = tmbuf.tm_min;
175: param->yySeconds = tmbuf.tm_sec;
1.5 tron 176: } else {
1.9 christos 177: param->yyYear = EPOCH;
178: param->yyMonth = 1;
179: param->yyDay = 1;
180:
181: param->yyHour = 0;
182: param->yyMinutes = 0;
183: param->yySeconds = 0;
1.5 tron 184: }
1.16.6.1 snj 185: param->yyHaveFullYear = 1;
1.9 christos 186: param->yyDSTmode = DSToff;
187: param->yyTimezone = 0;
1.5 tron 188: }
189: ;
190:
1.14 apb 191: at_number : tUNUMBER | tSNUMBER ;
192:
1.1 christos 193: time : tUNUMBER tMERIDIAN {
1.9 christos 194: param->yyHour = $1;
195: param->yyMinutes = 0;
196: param->yySeconds = 0;
197: param->yyMeridian = $2;
1.1 christos 198: }
199: | tUNUMBER ':' tUNUMBER o_merid {
1.9 christos 200: param->yyHour = $1;
201: param->yyMinutes = $3;
202: param->yySeconds = 0;
203: param->yyMeridian = $4;
1.1 christos 204: }
205: | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
1.9 christos 206: param->yyHour = $1;
207: param->yyMinutes = $3;
208: param->yySeconds = $5;
209: param->yyMeridian = $6;
1.1 christos 210: }
1.16.6.3 snj 211: | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
1.9 christos 212: param->yyHour = $1;
213: param->yyMinutes = $3;
214: param->yySeconds = $5;
215: param->yyMeridian = MER24;
1.16.6.3 snj 216: /* XXX: Do nothing with millis */
217: }
218: ;
219:
220: time_numericzone : tUNUMBER ':' tUNUMBER tSNUMBER {
221: param->yyHour = $1;
222: param->yyMinutes = $3;
223: param->yyMeridian = MER24;
1.9 christos 224: param->yyDSTmode = DSToff;
1.16.6.3 snj 225: param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
1.1 christos 226: }
1.16.6.3 snj 227: | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
1.9 christos 228: param->yyHour = $1;
229: param->yyMinutes = $3;
230: param->yySeconds = $5;
231: param->yyMeridian = MER24;
1.16.6.3 snj 232: param->yyDSTmode = DSToff;
233: param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
1.7 christos 234: }
1.1 christos 235: ;
236:
237: zone : tZONE {
1.9 christos 238: param->yyTimezone = $1;
239: param->yyDSTmode = DSToff;
1.1 christos 240: }
241: | tDAYZONE {
1.9 christos 242: param->yyTimezone = $1;
243: param->yyDSTmode = DSTon;
1.1 christos 244: }
245: |
246: tZONE tDST {
1.9 christos 247: param->yyTimezone = $1;
248: param->yyDSTmode = DSTon;
1.1 christos 249: }
250: ;
251:
252: day : tDAY {
1.9 christos 253: param->yyDayOrdinal = 1;
254: param->yyDayNumber = $1;
1.1 christos 255: }
256: | tDAY ',' {
1.9 christos 257: param->yyDayOrdinal = 1;
258: param->yyDayNumber = $1;
1.1 christos 259: }
260: | tUNUMBER tDAY {
1.9 christos 261: param->yyDayOrdinal = $1;
262: param->yyDayNumber = $2;
1.1 christos 263: }
264: ;
265:
266: date : tUNUMBER '/' tUNUMBER {
1.9 christos 267: param->yyMonth = $1;
268: param->yyDay = $3;
1.1 christos 269: }
270: | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
271: if ($1 >= 100) {
1.9 christos 272: param->yyYear = $1;
273: param->yyMonth = $3;
274: param->yyDay = $5;
1.1 christos 275: } else {
1.9 christos 276: param->yyMonth = $1;
277: param->yyDay = $3;
278: param->yyYear = $5;
1.1 christos 279: }
280: }
281: | tUNUMBER tSNUMBER tSNUMBER {
282: /* ISO 8601 format. yyyy-mm-dd. */
1.9 christos 283: param->yyYear = $1;
1.16.6.1 snj 284: param->yyHaveFullYear = 1;
1.9 christos 285: param->yyMonth = -$2;
286: param->yyDay = -$3;
1.1 christos 287: }
288: | tUNUMBER tMONTH tSNUMBER {
289: /* e.g. 17-JUN-1992. */
1.9 christos 290: param->yyDay = $1;
291: param->yyMonth = $2;
292: param->yyYear = -$3;
1.1 christos 293: }
294: | tMONTH tUNUMBER {
1.9 christos 295: param->yyMonth = $1;
296: param->yyDay = $2;
1.1 christos 297: }
298: | tMONTH tUNUMBER ',' tUNUMBER {
1.9 christos 299: param->yyMonth = $1;
300: param->yyDay = $2;
301: param->yyYear = $4;
1.1 christos 302: }
303: | tUNUMBER tMONTH {
1.9 christos 304: param->yyMonth = $2;
305: param->yyDay = $1;
1.1 christos 306: }
307: | tUNUMBER tMONTH tUNUMBER {
1.9 christos 308: param->yyMonth = $2;
309: param->yyDay = $1;
310: param->yyYear = $3;
1.1 christos 311: }
312: ;
313:
314: rel : relunit tAGO {
1.9 christos 315: param->yyRelSeconds = -param->yyRelSeconds;
316: param->yyRelMonth = -param->yyRelMonth;
1.1 christos 317: }
318: | relunit
319: ;
320:
321: relunit : tUNUMBER tMINUTE_UNIT {
1.9 christos 322: param->yyRelSeconds += $1 * $2 * 60L;
1.1 christos 323: }
324: | tSNUMBER tMINUTE_UNIT {
1.9 christos 325: param->yyRelSeconds += $1 * $2 * 60L;
1.1 christos 326: }
327: | tMINUTE_UNIT {
1.9 christos 328: param->yyRelSeconds += $1 * 60L;
1.1 christos 329: }
330: | tSNUMBER tSEC_UNIT {
1.9 christos 331: param->yyRelSeconds += $1;
1.1 christos 332: }
333: | tUNUMBER tSEC_UNIT {
1.9 christos 334: param->yyRelSeconds += $1;
1.1 christos 335: }
336: | tSEC_UNIT {
1.9 christos 337: param->yyRelSeconds++;
1.1 christos 338: }
339: | tSNUMBER tMONTH_UNIT {
1.9 christos 340: param->yyRelMonth += $1 * $2;
1.1 christos 341: }
342: | tUNUMBER tMONTH_UNIT {
1.9 christos 343: param->yyRelMonth += $1 * $2;
1.1 christos 344: }
345: | tMONTH_UNIT {
1.9 christos 346: param->yyRelMonth += $1;
1.1 christos 347: }
348: ;
349:
350: number : tUNUMBER {
1.9 christos 351: if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
352: param->yyYear = $1;
1.1 christos 353: else {
354: if($1>10000) {
1.9 christos 355: param->yyHaveDate++;
356: param->yyDay= ($1)%100;
357: param->yyMonth= ($1/100)%100;
358: param->yyYear = $1/10000;
1.1 christos 359: }
360: else {
1.9 christos 361: param->yyHaveTime++;
1.1 christos 362: if ($1 < 100) {
1.9 christos 363: param->yyHour = $1;
364: param->yyMinutes = 0;
1.1 christos 365: }
366: else {
1.9 christos 367: param->yyHour = $1 / 100;
368: param->yyMinutes = $1 % 100;
1.1 christos 369: }
1.9 christos 370: param->yySeconds = 0;
371: param->yyMeridian = MER24;
1.1 christos 372: }
373: }
374: }
375: ;
376:
377: o_merid : /* NULL */ {
378: $$ = MER24;
379: }
380: | tMERIDIAN {
381: $$ = $1;
382: }
383: ;
384:
385: %%
386:
387: /* Month and day table. */
1.12 joerg 388: static const TABLE MonthDayTable[] = {
1.1 christos 389: { "january", tMONTH, 1 },
390: { "february", tMONTH, 2 },
391: { "march", tMONTH, 3 },
392: { "april", tMONTH, 4 },
393: { "may", tMONTH, 5 },
394: { "june", tMONTH, 6 },
395: { "july", tMONTH, 7 },
396: { "august", tMONTH, 8 },
397: { "september", tMONTH, 9 },
398: { "sept", tMONTH, 9 },
399: { "october", tMONTH, 10 },
400: { "november", tMONTH, 11 },
401: { "december", tMONTH, 12 },
402: { "sunday", tDAY, 0 },
403: { "monday", tDAY, 1 },
404: { "tuesday", tDAY, 2 },
405: { "tues", tDAY, 2 },
406: { "wednesday", tDAY, 3 },
407: { "wednes", tDAY, 3 },
408: { "thursday", tDAY, 4 },
409: { "thur", tDAY, 4 },
410: { "thurs", tDAY, 4 },
411: { "friday", tDAY, 5 },
412: { "saturday", tDAY, 6 },
413: { NULL, 0, 0 }
414: };
415:
416: /* Time units table. */
1.12 joerg 417: static const TABLE UnitsTable[] = {
1.1 christos 418: { "year", tMONTH_UNIT, 12 },
419: { "month", tMONTH_UNIT, 1 },
420: { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
421: { "week", tMINUTE_UNIT, 7 * 24 * 60 },
422: { "day", tMINUTE_UNIT, 1 * 24 * 60 },
423: { "hour", tMINUTE_UNIT, 60 },
424: { "minute", tMINUTE_UNIT, 1 },
425: { "min", tMINUTE_UNIT, 1 },
426: { "second", tSEC_UNIT, 1 },
427: { "sec", tSEC_UNIT, 1 },
428: { NULL, 0, 0 }
429: };
430:
431: /* Assorted relative-time words. */
1.12 joerg 432: static const TABLE OtherTable[] = {
1.1 christos 433: { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
434: { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
435: { "today", tMINUTE_UNIT, 0 },
436: { "now", tMINUTE_UNIT, 0 },
437: { "last", tUNUMBER, -1 },
438: { "this", tMINUTE_UNIT, 0 },
439: { "next", tUNUMBER, 2 },
440: { "first", tUNUMBER, 1 },
1.7 christos 441: { "one", tUNUMBER, 1 },
1.1 christos 442: /* { "second", tUNUMBER, 2 }, */
1.7 christos 443: { "two", tUNUMBER, 2 },
1.1 christos 444: { "third", tUNUMBER, 3 },
1.7 christos 445: { "three", tUNUMBER, 3 },
1.1 christos 446: { "fourth", tUNUMBER, 4 },
1.7 christos 447: { "four", tUNUMBER, 4 },
1.1 christos 448: { "fifth", tUNUMBER, 5 },
1.7 christos 449: { "five", tUNUMBER, 5 },
1.1 christos 450: { "sixth", tUNUMBER, 6 },
1.7 christos 451: { "six", tUNUMBER, 6 },
1.1 christos 452: { "seventh", tUNUMBER, 7 },
1.7 christos 453: { "seven", tUNUMBER, 7 },
1.1 christos 454: { "eighth", tUNUMBER, 8 },
1.7 christos 455: { "eight", tUNUMBER, 8 },
1.1 christos 456: { "ninth", tUNUMBER, 9 },
1.7 christos 457: { "nine", tUNUMBER, 9 },
1.1 christos 458: { "tenth", tUNUMBER, 10 },
1.7 christos 459: { "ten", tUNUMBER, 10 },
1.1 christos 460: { "eleventh", tUNUMBER, 11 },
1.7 christos 461: { "eleven", tUNUMBER, 11 },
1.1 christos 462: { "twelfth", tUNUMBER, 12 },
1.7 christos 463: { "twelve", tUNUMBER, 12 },
1.1 christos 464: { "ago", tAGO, 1 },
465: { NULL, 0, 0 }
466: };
467:
468: /* The timezone table. */
469: /* Some of these are commented out because a time_t can't store a float. */
1.12 joerg 470: static const TABLE TimezoneTable[] = {
1.1 christos 471: { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
472: { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
473: { "utc", tZONE, HOUR( 0) },
474: { "wet", tZONE, HOUR( 0) }, /* Western European */
475: { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
476: { "wat", tZONE, HOUR( 1) }, /* West Africa */
477: { "at", tZONE, HOUR( 2) }, /* Azores */
478: #if 0
479: /* For completeness. BST is also British Summer, and GST is
480: * also Guam Standard. */
481: { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
482: { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
483: #endif
484: #if 0
485: { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
486: { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
487: { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
488: #endif
489: { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
490: { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
491: { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
492: { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
493: { "cst", tZONE, HOUR( 6) }, /* Central Standard */
494: { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
495: { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
496: { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
497: { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
498: { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
499: { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
500: { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
501: { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
502: { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
503: { "cat", tZONE, HOUR(10) }, /* Central Alaska */
504: { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
505: { "nt", tZONE, HOUR(11) }, /* Nome */
506: { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
507: { "cet", tZONE, -HOUR(1) }, /* Central European */
508: { "met", tZONE, -HOUR(1) }, /* Middle European */
509: { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
510: { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
511: { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
512: { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
513: { "fwt", tZONE, -HOUR(1) }, /* French Winter */
514: { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
515: { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
516: { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
517: #if 0
518: { "it", tZONE, -HOUR(3.5) },/* Iran */
519: #endif
520: { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
521: { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
522: #if 0
523: { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
524: #endif
525: { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
526: #if 0
527: /* For completeness. NST is also Newfoundland Stanard, and SST is
528: * also Swedish Summer. */
529: { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
530: { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
531: #endif /* 0 */
532: { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
533: { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
534: #if 0
535: { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
536: #endif
537: { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
538: { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
539: #if 0
540: { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
541: { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
542: #endif
543: { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
544: { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
545: { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
546: { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
547: { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
548: { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
549: { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
550: { NULL, 0, 0 }
551: };
552:
553: /* Military timezone table. */
1.12 joerg 554: static const TABLE MilitaryTable[] = {
1.1 christos 555: { "a", tZONE, HOUR( 1) },
556: { "b", tZONE, HOUR( 2) },
557: { "c", tZONE, HOUR( 3) },
558: { "d", tZONE, HOUR( 4) },
559: { "e", tZONE, HOUR( 5) },
560: { "f", tZONE, HOUR( 6) },
561: { "g", tZONE, HOUR( 7) },
562: { "h", tZONE, HOUR( 8) },
563: { "i", tZONE, HOUR( 9) },
564: { "k", tZONE, HOUR( 10) },
565: { "l", tZONE, HOUR( 11) },
566: { "m", tZONE, HOUR( 12) },
567: { "n", tZONE, HOUR(- 1) },
568: { "o", tZONE, HOUR(- 2) },
569: { "p", tZONE, HOUR(- 3) },
570: { "q", tZONE, HOUR(- 4) },
571: { "r", tZONE, HOUR(- 5) },
572: { "s", tZONE, HOUR(- 6) },
573: { "t", tZONE, HOUR(- 7) },
574: { "u", tZONE, HOUR(- 8) },
575: { "v", tZONE, HOUR(- 9) },
576: { "w", tZONE, HOUR(-10) },
577: { "x", tZONE, HOUR(-11) },
578: { "y", tZONE, HOUR(-12) },
579: { "z", tZONE, HOUR( 0) },
580: { NULL, 0, 0 }
581: };
582:
583:
584:
585:
586: /* ARGSUSED */
587: static int
1.9 christos 588: yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
1.1 christos 589: {
590: return 0;
591: }
592:
593:
1.16.6.1 snj 594: /* Adjust year from a value that might be abbreviated, to a full value.
595: * e.g. convert 70 to 1970.
596: * Input Year is either:
597: * - A negative number, which means to use its absolute value (why?)
598: * - A number from 0 to 99, which means a year from 1900 to 1999, or
599: * - The actual year (>=100).
600: * Returns the full year. */
601: static time_t
602: AdjustYear(time_t Year)
603: {
604: /* XXX Y2K */
605: if (Year < 0)
606: Year = -Year;
607: if (Year < 70)
608: Year += 2000;
609: else if (Year < 100)
610: Year += 1900;
611: return Year;
612: }
613:
1.1 christos 614: static time_t
615: Convert(
1.11 apb 616: time_t Month, /* month of year [1-12] */
617: time_t Day, /* day of month [1-31] */
1.16.6.1 snj 618: time_t Year, /* year, not abbreviated in any way */
1.11 apb 619: time_t Hours, /* Hour of day [0-24] */
620: time_t Minutes, /* Minute of hour [0-59] */
621: time_t Seconds, /* Second of minute [0-60] */
1.16.6.4! snj 622: time_t Timezone, /* Timezone as minutes east of UTC,
! 623: * or USE_LOCAL_TIME special case */
1.11 apb 624: MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */
625: DSTMODE DSTmode /* DST on/off/maybe */
1.1 christos 626: )
627: {
1.13 apb 628: struct tm tm = {.tm_sec = 0};
629: time_t result;
1.1 christos 630:
1.11 apb 631: tm.tm_sec = Seconds;
632: tm.tm_min = Minutes;
633: tm.tm_hour = Hours + (Meridian == MERpm ? 12 : 0);
634: tm.tm_mday = Day;
635: tm.tm_mon = Month - 1;
636: tm.tm_year = Year - 1900;
637: switch (DSTmode) {
638: case DSTon: tm.tm_isdst = 1; break;
639: case DSToff: tm.tm_isdst = 0; break;
640: default: tm.tm_isdst = -1; break;
1.6 christos 641: }
642:
1.16.6.4! snj 643: if (Timezone == USE_LOCAL_TIME) {
! 644: result = mktime(&tm);
! 645: } else {
! 646: /* We rely on mktime_z(NULL, ...) working in UTC */
! 647: result = mktime_z(NULL, &tm);
! 648: result += Timezone * 60;
! 649: }
! 650:
! 651: #if PARSEDATE_DEBUG
! 652: fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
! 653: " mer=%d DST=%d)",
! 654: __func__,
! 655: (intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
! 656: (intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
! 657: (intmax_t)Timezone, (int)Meridian, (int)DSTmode);
! 658: fprintf(stderr, " -> %jd", (intmax_t)result);
! 659: fprintf(stderr, " %s", ctime(&result));
! 660: #endif
! 661:
1.13 apb 662: return result;
1.1 christos 663: }
664:
665:
666: static time_t
667: DSTcorrect(
668: time_t Start,
669: time_t Future
670: )
671: {
672: time_t StartDay;
673: time_t FutureDay;
1.6 christos 674: struct tm *tm;
675:
676: if ((tm = localtime(&Start)) == NULL)
677: return -1;
678: StartDay = (tm->tm_hour + 1) % 24;
679:
680: if ((tm = localtime(&Future)) == NULL)
681: return -1;
682: FutureDay = (tm->tm_hour + 1) % 24;
1.1 christos 683:
684: return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
685: }
686:
687:
688: static time_t
689: RelativeDate(
690: time_t Start,
691: time_t DayOrdinal,
692: time_t DayNumber
693: )
694: {
695: struct tm *tm;
696: time_t now;
697:
698: now = Start;
699: tm = localtime(&now);
700: now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
701: now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
702: return DSTcorrect(Start, now);
703: }
704:
705:
706: static time_t
707: RelativeMonth(
708: time_t Start,
1.9 christos 709: time_t RelMonth,
710: time_t Timezone
1.1 christos 711: )
712: {
713: struct tm *tm;
714: time_t Month;
715: time_t Year;
716:
717: if (RelMonth == 0)
718: return 0;
719: tm = localtime(&Start);
1.6 christos 720: if (tm == NULL)
721: return -1;
1.1 christos 722: Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
723: Year = Month / 12;
724: Month = Month % 12 + 1;
725: return DSTcorrect(Start,
726: Convert(Month, (time_t)tm->tm_mday, Year,
727: (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
1.9 christos 728: Timezone, MER24, DSTmaybe));
1.1 christos 729: }
730:
731:
732: static int
1.9 christos 733: LookupWord(YYSTYPE *yylval, char *buff)
1.1 christos 734: {
735: register char *p;
736: register char *q;
737: register const TABLE *tp;
738: int i;
739: int abbrev;
740:
741: /* Make it lowercase. */
742: for (p = buff; *p; p++)
743: if (isupper((unsigned char)*p))
744: *p = tolower((unsigned char)*p);
745:
746: if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
1.9 christos 747: yylval->Meridian = MERam;
1.1 christos 748: return tMERIDIAN;
749: }
750: if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
1.9 christos 751: yylval->Meridian = MERpm;
1.1 christos 752: return tMERIDIAN;
753: }
754:
755: /* See if we have an abbreviation for a month. */
756: if (strlen(buff) == 3)
757: abbrev = 1;
758: else if (strlen(buff) == 4 && buff[3] == '.') {
759: abbrev = 1;
760: buff[3] = '\0';
761: }
762: else
763: abbrev = 0;
764:
765: for (tp = MonthDayTable; tp->name; tp++) {
766: if (abbrev) {
767: if (strncmp(buff, tp->name, 3) == 0) {
1.9 christos 768: yylval->Number = tp->value;
1.1 christos 769: return tp->type;
770: }
771: }
772: else if (strcmp(buff, tp->name) == 0) {
1.9 christos 773: yylval->Number = tp->value;
1.1 christos 774: return tp->type;
775: }
776: }
777:
778: for (tp = TimezoneTable; tp->name; tp++)
779: if (strcmp(buff, tp->name) == 0) {
1.9 christos 780: yylval->Number = tp->value;
1.1 christos 781: return tp->type;
782: }
783:
784: if (strcmp(buff, "dst") == 0)
785: return tDST;
786:
787: for (tp = UnitsTable; tp->name; tp++)
788: if (strcmp(buff, tp->name) == 0) {
1.9 christos 789: yylval->Number = tp->value;
1.1 christos 790: return tp->type;
791: }
792:
793: /* Strip off any plural and try the units table again. */
794: i = strlen(buff) - 1;
795: if (buff[i] == 's') {
796: buff[i] = '\0';
797: for (tp = UnitsTable; tp->name; tp++)
798: if (strcmp(buff, tp->name) == 0) {
1.9 christos 799: yylval->Number = tp->value;
1.1 christos 800: return tp->type;
801: }
802: buff[i] = 's'; /* Put back for "this" in OtherTable. */
803: }
804:
805: for (tp = OtherTable; tp->name; tp++)
806: if (strcmp(buff, tp->name) == 0) {
1.9 christos 807: yylval->Number = tp->value;
1.1 christos 808: return tp->type;
809: }
810:
811: /* Military timezones. */
812: if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
813: for (tp = MilitaryTable; tp->name; tp++)
814: if (strcmp(buff, tp->name) == 0) {
1.9 christos 815: yylval->Number = tp->value;
1.1 christos 816: return tp->type;
817: }
818: }
819:
820: /* Drop out any periods and try the timezone table again. */
821: for (i = 0, p = q = buff; *q; q++)
822: if (*q != '.')
823: *p++ = *q;
824: else
825: i++;
826: *p = '\0';
827: if (i)
828: for (tp = TimezoneTable; tp->name; tp++)
829: if (strcmp(buff, tp->name) == 0) {
1.9 christos 830: yylval->Number = tp->value;
1.1 christos 831: return tp->type;
832: }
833:
834: return tID;
835: }
836:
837:
838: static int
1.9 christos 839: yylex(YYSTYPE *yylval, const char **yyInput)
1.1 christos 840: {
841: register char c;
842: register char *p;
843: char buff[20];
844: int Count;
845: int sign;
1.9 christos 846: const char *inp = *yyInput;
1.1 christos 847:
848: for ( ; ; ) {
1.9 christos 849: while (isspace((unsigned char)*inp))
850: inp++;
1.1 christos 851:
1.9 christos 852: if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
1.1 christos 853: if (c == '-' || c == '+') {
854: sign = c == '-' ? -1 : 1;
1.9 christos 855: if (!isdigit((unsigned char)*++inp))
1.1 christos 856: /* skip the '-' sign */
857: continue;
858: }
859: else
860: sign = 0;
1.9 christos 861: for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
862: yylval->Number = 10 * yylval->Number + c - '0';
1.1 christos 863: if (sign < 0)
1.9 christos 864: yylval->Number = -yylval->Number;
865: *yyInput = --inp;
1.1 christos 866: return sign ? tSNUMBER : tUNUMBER;
867: }
868: if (isalpha((unsigned char)c)) {
1.9 christos 869: for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
1.1 christos 870: if (p < &buff[sizeof buff - 1])
871: *p++ = c;
872: *p = '\0';
1.9 christos 873: *yyInput = --inp;
874: return LookupWord(yylval, buff);
1.1 christos 875: }
1.5 tron 876: if (c == '@') {
1.9 christos 877: *yyInput = ++inp;
1.5 tron 878: return AT_SIGN;
879: }
1.9 christos 880: if (c != '(') {
881: *yyInput = ++inp;
882: return c;
883: }
1.1 christos 884: Count = 0;
885: do {
1.9 christos 886: c = *inp++;
1.1 christos 887: if (c == '\0')
888: return c;
889: if (c == '(')
890: Count++;
891: else if (c == ')')
892: Count--;
893: } while (Count > 0);
894: }
895: }
896:
897: #define TM_YEAR_ORIGIN 1900
898:
899: time_t
900: parsedate(const char *p, const time_t *now, const int *zone)
901: {
1.16.6.4! snj 902: struct tm local, *tm;
1.1 christos 903: time_t nowt;
904: int zonet;
905: time_t Start;
1.6 christos 906: time_t tod, rm;
1.9 christos 907: struct dateinfo param;
1.14 apb 908: int saved_errno;
909:
910: saved_errno = errno;
911: errno = 0;
1.1 christos 912:
1.16.6.4! snj 913: if (now == NULL) {
1.1 christos 914: now = &nowt;
915: (void)time(&nowt);
1.16.6.4! snj 916: }
! 917: if (zone == NULL) {
! 918: zone = &zonet;
! 919: zonet = USE_LOCAL_TIME;
1.1 christos 920: if ((tm = localtime_r(now, &local)) == NULL)
921: return -1;
922: } else {
1.16.6.4! snj 923: /*
! 924: * Should use the specified zone, not localtime.
! 925: * Fake it using gmtime and arithmetic.
! 926: * This is good enough because we use only the year/month/day,
! 927: * not other fields of struct tm.
! 928: */
! 929: time_t fake = *now + (*zone * 60);
! 930: if ((tm = gmtime_r(&fake, &local)) == NULL)
1.1 christos 931: return -1;
932: }
1.9 christos 933: param.yyYear = tm->tm_year + 1900;
934: param.yyMonth = tm->tm_mon + 1;
935: param.yyDay = tm->tm_mday;
936: param.yyTimezone = *zone;
937: param.yyDSTmode = DSTmaybe;
938: param.yyHour = 0;
939: param.yyMinutes = 0;
940: param.yySeconds = 0;
941: param.yyMeridian = MER24;
942: param.yyRelSeconds = 0;
943: param.yyRelMonth = 0;
944: param.yyHaveDate = 0;
1.16.6.1 snj 945: param.yyHaveFullYear = 0;
1.9 christos 946: param.yyHaveDay = 0;
947: param.yyHaveRel = 0;
948: param.yyHaveTime = 0;
949: param.yyHaveZone = 0;
950:
951: if (yyparse(¶m, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
1.14 apb 952: param.yyHaveDate > 1 || param.yyHaveDay > 1) {
953: errno = EINVAL;
1.9 christos 954: return -1;
1.14 apb 955: }
1.9 christos 956:
957: if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
1.16.6.1 snj 958: if (! param.yyHaveFullYear) {
959: param.yyYear = AdjustYear(param.yyYear);
960: param.yyHaveFullYear = 1;
961: }
1.9 christos 962: Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
963: param.yyMinutes, param.yySeconds, param.yyTimezone,
964: param.yyMeridian, param.yyDSTmode);
1.14 apb 965: if (Start == -1 && errno != 0)
1.1 christos 966: return -1;
967: }
968: else {
969: Start = *now;
1.9 christos 970: if (!param.yyHaveRel)
1.1 christos 971: Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
972: }
973:
1.9 christos 974: Start += param.yyRelSeconds;
975: rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
1.14 apb 976: if (rm == -1 && errno != 0)
1.6 christos 977: return -1;
978: Start += rm;
1.1 christos 979:
1.9 christos 980: if (param.yyHaveDay && !param.yyHaveDate) {
981: tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
1.1 christos 982: Start += tod;
983: }
984:
1.14 apb 985: if (errno == 0)
986: errno = saved_errno;
1.6 christos 987: return Start;
1.1 christos 988: }
989:
990:
991: #if defined(TEST)
992:
993: /* ARGSUSED */
994: int
1.14 apb 995: main(int ac, char *av[])
1.1 christos 996: {
997: char buff[128];
998: time_t d;
999:
1000: (void)printf("Enter date, or blank line to exit.\n\t> ");
1001: (void)fflush(stdout);
1.14 apb 1002: while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
1003: errno = 0;
1.1 christos 1004: d = parsedate(buff, NULL, NULL);
1.14 apb 1005: if (d == -1 && errno != 0)
1006: (void)printf("Bad format - couldn't convert: %s\n",
1007: strerror(errno));
1.1 christos 1008: else
1.14 apb 1009: (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
1.1 christos 1010: (void)printf("\t> ");
1011: (void)fflush(stdout);
1012: }
1013: exit(0);
1014: /* NOTREACHED */
1015: }
1016: #endif /* defined(TEST) */
CVSweb <webmaster@jp.NetBSD.org>