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