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