parsedate.y revision 1.9 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.9 christos struct dateinfo {
66 1.9 christos DSTMODE yyDSTmode;
67 1.9 christos time_t yyDayOrdinal;
68 1.9 christos time_t yyDayNumber;
69 1.9 christos int yyHaveDate;
70 1.9 christos int yyHaveDay;
71 1.9 christos int yyHaveRel;
72 1.9 christos int yyHaveTime;
73 1.9 christos int yyHaveZone;
74 1.9 christos time_t yyTimezone;
75 1.9 christos time_t yyDay;
76 1.9 christos time_t yyHour;
77 1.9 christos time_t yyMinutes;
78 1.9 christos time_t yyMonth;
79 1.9 christos time_t yySeconds;
80 1.9 christos time_t yyYear;
81 1.9 christos MERIDIAN yyMeridian;
82 1.9 christos time_t yyRelMonth;
83 1.9 christos time_t yyRelSeconds;
84 1.9 christos };
85 1.1 christos %}
86 1.1 christos
87 1.1 christos %union {
88 1.1 christos time_t Number;
89 1.1 christos enum _MERIDIAN Meridian;
90 1.1 christos }
91 1.1 christos
92 1.1 christos %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
93 1.5 tron %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
94 1.1 christos
95 1.1 christos %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
96 1.1 christos %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
97 1.1 christos %type <Meridian> tMERIDIAN o_merid
98 1.1 christos
99 1.9 christos %parse-param { struct dateinfo *param }
100 1.9 christos %parse-param { const char **yyInput }
101 1.9 christos %lex-param { const char **yyInput }
102 1.9 christos %pure-parser
103 1.9 christos
104 1.1 christos %%
105 1.1 christos
106 1.1 christos spec : /* NULL */
107 1.1 christos | spec item
108 1.1 christos ;
109 1.1 christos
110 1.1 christos item : time {
111 1.9 christos param->yyHaveTime++;
112 1.1 christos }
113 1.1 christos | zone {
114 1.9 christos param->yyHaveZone++;
115 1.1 christos }
116 1.1 christos | date {
117 1.9 christos param->yyHaveDate++;
118 1.1 christos }
119 1.1 christos | day {
120 1.9 christos param->yyHaveDay++;
121 1.1 christos }
122 1.1 christos | rel {
123 1.9 christos param->yyHaveRel++;
124 1.1 christos }
125 1.1 christos | cvsstamp {
126 1.9 christos param->yyHaveTime++;
127 1.9 christos param->yyHaveDate++;
128 1.9 christos param->yyHaveZone++;
129 1.1 christos }
130 1.5 tron | epochdate {
131 1.9 christos param->yyHaveTime++;
132 1.9 christos param->yyHaveDate++;
133 1.9 christos param->yyHaveZone++;
134 1.5 tron }
135 1.1 christos | number
136 1.1 christos ;
137 1.1 christos
138 1.1 christos cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
139 1.9 christos param->yyYear = $1;
140 1.9 christos if (param->yyYear < 100) param->yyYear += 1900;
141 1.9 christos param->yyMonth = $3;
142 1.9 christos param->yyDay = $5;
143 1.9 christos param->yyHour = $7;
144 1.9 christos param->yyMinutes = $9;
145 1.9 christos param->yySeconds = $11;
146 1.9 christos param->yyDSTmode = DSToff;
147 1.9 christos param->yyTimezone = 0;
148 1.1 christos }
149 1.1 christos ;
150 1.1 christos
151 1.5 tron epochdate: AT_SIGN tUNUMBER {
152 1.5 tron time_t when = $2;
153 1.5 tron struct tm tmbuf;
154 1.5 tron if (gmtime_r(&when, &tmbuf) != NULL) {
155 1.9 christos param->yyYear = tmbuf.tm_year + 1900;
156 1.9 christos param->yyMonth = tmbuf.tm_mon + 1;
157 1.9 christos param->yyDay = tmbuf.tm_mday;
158 1.9 christos
159 1.9 christos param->yyHour = tmbuf.tm_hour;
160 1.9 christos param->yyMinutes = tmbuf.tm_min;
161 1.9 christos param->yySeconds = tmbuf.tm_sec;
162 1.5 tron } else {
163 1.9 christos param->yyYear = EPOCH;
164 1.9 christos param->yyMonth = 1;
165 1.9 christos param->yyDay = 1;
166 1.9 christos
167 1.9 christos param->yyHour = 0;
168 1.9 christos param->yyMinutes = 0;
169 1.9 christos param->yySeconds = 0;
170 1.5 tron }
171 1.9 christos param->yyDSTmode = DSToff;
172 1.9 christos param->yyTimezone = 0;
173 1.5 tron }
174 1.5 tron ;
175 1.5 tron
176 1.1 christos time : tUNUMBER tMERIDIAN {
177 1.9 christos param->yyHour = $1;
178 1.9 christos param->yyMinutes = 0;
179 1.9 christos param->yySeconds = 0;
180 1.9 christos param->yyMeridian = $2;
181 1.1 christos }
182 1.1 christos | tUNUMBER ':' tUNUMBER o_merid {
183 1.9 christos param->yyHour = $1;
184 1.9 christos param->yyMinutes = $3;
185 1.9 christos param->yySeconds = 0;
186 1.9 christos param->yyMeridian = $4;
187 1.1 christos }
188 1.1 christos | tUNUMBER ':' tUNUMBER tSNUMBER {
189 1.9 christos param->yyHour = $1;
190 1.9 christos param->yyMinutes = $3;
191 1.9 christos param->yyMeridian = MER24;
192 1.9 christos param->yyDSTmode = DSToff;
193 1.9 christos param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
194 1.1 christos }
195 1.1 christos | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
196 1.9 christos param->yyHour = $1;
197 1.9 christos param->yyMinutes = $3;
198 1.9 christos param->yySeconds = $5;
199 1.9 christos param->yyMeridian = $6;
200 1.1 christos }
201 1.1 christos | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
202 1.9 christos param->yyHour = $1;
203 1.9 christos param->yyMinutes = $3;
204 1.9 christos param->yySeconds = $5;
205 1.9 christos param->yyMeridian = MER24;
206 1.9 christos param->yyDSTmode = DSToff;
207 1.9 christos param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
208 1.1 christos }
209 1.7 christos | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
210 1.9 christos param->yyHour = $1;
211 1.9 christos param->yyMinutes = $3;
212 1.9 christos param->yySeconds = $5;
213 1.9 christos param->yyMeridian = MER24;
214 1.9 christos param->yyDSTmode = DSToff;
215 1.7 christos /* XXX: Do nothing with millis */
216 1.9 christos /* param->yyTimezone = ($7 % 100 + ($7 / 100) * 60); */
217 1.7 christos }
218 1.1 christos ;
219 1.1 christos
220 1.1 christos zone : tZONE {
221 1.9 christos param->yyTimezone = $1;
222 1.9 christos param->yyDSTmode = DSToff;
223 1.1 christos }
224 1.1 christos | tDAYZONE {
225 1.9 christos param->yyTimezone = $1;
226 1.9 christos param->yyDSTmode = DSTon;
227 1.1 christos }
228 1.1 christos |
229 1.1 christos tZONE tDST {
230 1.9 christos param->yyTimezone = $1;
231 1.9 christos param->yyDSTmode = DSTon;
232 1.1 christos }
233 1.1 christos ;
234 1.1 christos
235 1.1 christos day : tDAY {
236 1.9 christos param->yyDayOrdinal = 1;
237 1.9 christos param->yyDayNumber = $1;
238 1.1 christos }
239 1.1 christos | tDAY ',' {
240 1.9 christos param->yyDayOrdinal = 1;
241 1.9 christos param->yyDayNumber = $1;
242 1.1 christos }
243 1.1 christos | tUNUMBER tDAY {
244 1.9 christos param->yyDayOrdinal = $1;
245 1.9 christos param->yyDayNumber = $2;
246 1.1 christos }
247 1.1 christos ;
248 1.1 christos
249 1.1 christos date : tUNUMBER '/' tUNUMBER {
250 1.9 christos param->yyMonth = $1;
251 1.9 christos param->yyDay = $3;
252 1.1 christos }
253 1.1 christos | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
254 1.1 christos if ($1 >= 100) {
255 1.9 christos param->yyYear = $1;
256 1.9 christos param->yyMonth = $3;
257 1.9 christos param->yyDay = $5;
258 1.1 christos } else {
259 1.9 christos param->yyMonth = $1;
260 1.9 christos param->yyDay = $3;
261 1.9 christos param->yyYear = $5;
262 1.1 christos }
263 1.1 christos }
264 1.1 christos | tUNUMBER tSNUMBER tSNUMBER {
265 1.1 christos /* ISO 8601 format. yyyy-mm-dd. */
266 1.9 christos param->yyYear = $1;
267 1.9 christos param->yyMonth = -$2;
268 1.9 christos param->yyDay = -$3;
269 1.1 christos }
270 1.1 christos | tUNUMBER tMONTH tSNUMBER {
271 1.1 christos /* e.g. 17-JUN-1992. */
272 1.9 christos param->yyDay = $1;
273 1.9 christos param->yyMonth = $2;
274 1.9 christos param->yyYear = -$3;
275 1.1 christos }
276 1.1 christos | tMONTH tUNUMBER {
277 1.9 christos param->yyMonth = $1;
278 1.9 christos param->yyDay = $2;
279 1.1 christos }
280 1.1 christos | tMONTH tUNUMBER ',' tUNUMBER {
281 1.9 christos param->yyMonth = $1;
282 1.9 christos param->yyDay = $2;
283 1.9 christos param->yyYear = $4;
284 1.1 christos }
285 1.1 christos | tUNUMBER tMONTH {
286 1.9 christos param->yyMonth = $2;
287 1.9 christos param->yyDay = $1;
288 1.1 christos }
289 1.1 christos | tUNUMBER tMONTH tUNUMBER {
290 1.9 christos param->yyMonth = $2;
291 1.9 christos param->yyDay = $1;
292 1.9 christos param->yyYear = $3;
293 1.1 christos }
294 1.1 christos ;
295 1.1 christos
296 1.1 christos rel : relunit tAGO {
297 1.9 christos param->yyRelSeconds = -param->yyRelSeconds;
298 1.9 christos param->yyRelMonth = -param->yyRelMonth;
299 1.1 christos }
300 1.1 christos | relunit
301 1.1 christos ;
302 1.1 christos
303 1.1 christos relunit : tUNUMBER tMINUTE_UNIT {
304 1.9 christos param->yyRelSeconds += $1 * $2 * 60L;
305 1.1 christos }
306 1.1 christos | tSNUMBER tMINUTE_UNIT {
307 1.9 christos param->yyRelSeconds += $1 * $2 * 60L;
308 1.1 christos }
309 1.1 christos | tMINUTE_UNIT {
310 1.9 christos param->yyRelSeconds += $1 * 60L;
311 1.1 christos }
312 1.1 christos | tSNUMBER tSEC_UNIT {
313 1.9 christos param->yyRelSeconds += $1;
314 1.1 christos }
315 1.1 christos | tUNUMBER tSEC_UNIT {
316 1.9 christos param->yyRelSeconds += $1;
317 1.1 christos }
318 1.1 christos | tSEC_UNIT {
319 1.9 christos param->yyRelSeconds++;
320 1.1 christos }
321 1.1 christos | tSNUMBER tMONTH_UNIT {
322 1.9 christos param->yyRelMonth += $1 * $2;
323 1.1 christos }
324 1.1 christos | tUNUMBER tMONTH_UNIT {
325 1.9 christos param->yyRelMonth += $1 * $2;
326 1.1 christos }
327 1.1 christos | tMONTH_UNIT {
328 1.9 christos param->yyRelMonth += $1;
329 1.1 christos }
330 1.1 christos ;
331 1.1 christos
332 1.1 christos number : tUNUMBER {
333 1.9 christos if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
334 1.9 christos param->yyYear = $1;
335 1.1 christos else {
336 1.1 christos if($1>10000) {
337 1.9 christos param->yyHaveDate++;
338 1.9 christos param->yyDay= ($1)%100;
339 1.9 christos param->yyMonth= ($1/100)%100;
340 1.9 christos param->yyYear = $1/10000;
341 1.1 christos }
342 1.1 christos else {
343 1.9 christos param->yyHaveTime++;
344 1.1 christos if ($1 < 100) {
345 1.9 christos param->yyHour = $1;
346 1.9 christos param->yyMinutes = 0;
347 1.1 christos }
348 1.1 christos else {
349 1.9 christos param->yyHour = $1 / 100;
350 1.9 christos param->yyMinutes = $1 % 100;
351 1.1 christos }
352 1.9 christos param->yySeconds = 0;
353 1.9 christos param->yyMeridian = MER24;
354 1.1 christos }
355 1.1 christos }
356 1.1 christos }
357 1.1 christos ;
358 1.1 christos
359 1.1 christos o_merid : /* NULL */ {
360 1.1 christos $$ = MER24;
361 1.1 christos }
362 1.1 christos | tMERIDIAN {
363 1.1 christos $$ = $1;
364 1.1 christos }
365 1.1 christos ;
366 1.1 christos
367 1.1 christos %%
368 1.1 christos
369 1.1 christos /* Month and day table. */
370 1.9 christos static const TABLE const MonthDayTable[] = {
371 1.1 christos { "january", tMONTH, 1 },
372 1.1 christos { "february", tMONTH, 2 },
373 1.1 christos { "march", tMONTH, 3 },
374 1.1 christos { "april", tMONTH, 4 },
375 1.1 christos { "may", tMONTH, 5 },
376 1.1 christos { "june", tMONTH, 6 },
377 1.1 christos { "july", tMONTH, 7 },
378 1.1 christos { "august", tMONTH, 8 },
379 1.1 christos { "september", tMONTH, 9 },
380 1.1 christos { "sept", tMONTH, 9 },
381 1.1 christos { "october", tMONTH, 10 },
382 1.1 christos { "november", tMONTH, 11 },
383 1.1 christos { "december", tMONTH, 12 },
384 1.1 christos { "sunday", tDAY, 0 },
385 1.1 christos { "monday", tDAY, 1 },
386 1.1 christos { "tuesday", tDAY, 2 },
387 1.1 christos { "tues", tDAY, 2 },
388 1.1 christos { "wednesday", tDAY, 3 },
389 1.1 christos { "wednes", tDAY, 3 },
390 1.1 christos { "thursday", tDAY, 4 },
391 1.1 christos { "thur", tDAY, 4 },
392 1.1 christos { "thurs", tDAY, 4 },
393 1.1 christos { "friday", tDAY, 5 },
394 1.1 christos { "saturday", tDAY, 6 },
395 1.1 christos { NULL, 0, 0 }
396 1.1 christos };
397 1.1 christos
398 1.1 christos /* Time units table. */
399 1.9 christos static const TABLE const UnitsTable[] = {
400 1.1 christos { "year", tMONTH_UNIT, 12 },
401 1.1 christos { "month", tMONTH_UNIT, 1 },
402 1.1 christos { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
403 1.1 christos { "week", tMINUTE_UNIT, 7 * 24 * 60 },
404 1.1 christos { "day", tMINUTE_UNIT, 1 * 24 * 60 },
405 1.1 christos { "hour", tMINUTE_UNIT, 60 },
406 1.1 christos { "minute", tMINUTE_UNIT, 1 },
407 1.1 christos { "min", tMINUTE_UNIT, 1 },
408 1.1 christos { "second", tSEC_UNIT, 1 },
409 1.1 christos { "sec", tSEC_UNIT, 1 },
410 1.1 christos { NULL, 0, 0 }
411 1.1 christos };
412 1.1 christos
413 1.1 christos /* Assorted relative-time words. */
414 1.9 christos static const TABLE const OtherTable[] = {
415 1.1 christos { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
416 1.1 christos { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
417 1.1 christos { "today", tMINUTE_UNIT, 0 },
418 1.1 christos { "now", tMINUTE_UNIT, 0 },
419 1.1 christos { "last", tUNUMBER, -1 },
420 1.1 christos { "this", tMINUTE_UNIT, 0 },
421 1.1 christos { "next", tUNUMBER, 2 },
422 1.1 christos { "first", tUNUMBER, 1 },
423 1.7 christos { "one", tUNUMBER, 1 },
424 1.1 christos /* { "second", tUNUMBER, 2 }, */
425 1.7 christos { "two", tUNUMBER, 2 },
426 1.1 christos { "third", tUNUMBER, 3 },
427 1.7 christos { "three", tUNUMBER, 3 },
428 1.1 christos { "fourth", tUNUMBER, 4 },
429 1.7 christos { "four", tUNUMBER, 4 },
430 1.1 christos { "fifth", tUNUMBER, 5 },
431 1.7 christos { "five", tUNUMBER, 5 },
432 1.1 christos { "sixth", tUNUMBER, 6 },
433 1.7 christos { "six", tUNUMBER, 6 },
434 1.1 christos { "seventh", tUNUMBER, 7 },
435 1.7 christos { "seven", tUNUMBER, 7 },
436 1.1 christos { "eighth", tUNUMBER, 8 },
437 1.7 christos { "eight", tUNUMBER, 8 },
438 1.1 christos { "ninth", tUNUMBER, 9 },
439 1.7 christos { "nine", tUNUMBER, 9 },
440 1.1 christos { "tenth", tUNUMBER, 10 },
441 1.7 christos { "ten", tUNUMBER, 10 },
442 1.1 christos { "eleventh", tUNUMBER, 11 },
443 1.7 christos { "eleven", tUNUMBER, 11 },
444 1.1 christos { "twelfth", tUNUMBER, 12 },
445 1.7 christos { "twelve", tUNUMBER, 12 },
446 1.1 christos { "ago", tAGO, 1 },
447 1.1 christos { NULL, 0, 0 }
448 1.1 christos };
449 1.1 christos
450 1.1 christos /* The timezone table. */
451 1.1 christos /* Some of these are commented out because a time_t can't store a float. */
452 1.9 christos static const TABLE const TimezoneTable[] = {
453 1.1 christos { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
454 1.1 christos { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
455 1.1 christos { "utc", tZONE, HOUR( 0) },
456 1.1 christos { "wet", tZONE, HOUR( 0) }, /* Western European */
457 1.1 christos { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
458 1.1 christos { "wat", tZONE, HOUR( 1) }, /* West Africa */
459 1.1 christos { "at", tZONE, HOUR( 2) }, /* Azores */
460 1.1 christos #if 0
461 1.1 christos /* For completeness. BST is also British Summer, and GST is
462 1.1 christos * also Guam Standard. */
463 1.1 christos { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
464 1.1 christos { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
465 1.1 christos #endif
466 1.1 christos #if 0
467 1.1 christos { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
468 1.1 christos { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
469 1.1 christos { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
470 1.1 christos #endif
471 1.1 christos { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
472 1.1 christos { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
473 1.1 christos { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
474 1.1 christos { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
475 1.1 christos { "cst", tZONE, HOUR( 6) }, /* Central Standard */
476 1.1 christos { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
477 1.1 christos { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
478 1.1 christos { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
479 1.1 christos { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
480 1.1 christos { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
481 1.1 christos { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
482 1.1 christos { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
483 1.1 christos { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
484 1.1 christos { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
485 1.1 christos { "cat", tZONE, HOUR(10) }, /* Central Alaska */
486 1.1 christos { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
487 1.1 christos { "nt", tZONE, HOUR(11) }, /* Nome */
488 1.1 christos { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
489 1.1 christos { "cet", tZONE, -HOUR(1) }, /* Central European */
490 1.1 christos { "met", tZONE, -HOUR(1) }, /* Middle European */
491 1.1 christos { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
492 1.1 christos { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
493 1.1 christos { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
494 1.1 christos { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
495 1.1 christos { "fwt", tZONE, -HOUR(1) }, /* French Winter */
496 1.1 christos { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
497 1.1 christos { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
498 1.1 christos { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
499 1.1 christos #if 0
500 1.1 christos { "it", tZONE, -HOUR(3.5) },/* Iran */
501 1.1 christos #endif
502 1.1 christos { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
503 1.1 christos { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
504 1.1 christos #if 0
505 1.1 christos { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
506 1.1 christos #endif
507 1.1 christos { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
508 1.1 christos #if 0
509 1.1 christos /* For completeness. NST is also Newfoundland Stanard, and SST is
510 1.1 christos * also Swedish Summer. */
511 1.1 christos { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
512 1.1 christos { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
513 1.1 christos #endif /* 0 */
514 1.1 christos { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
515 1.1 christos { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
516 1.1 christos #if 0
517 1.1 christos { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
518 1.1 christos #endif
519 1.1 christos { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
520 1.1 christos { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
521 1.1 christos #if 0
522 1.1 christos { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
523 1.1 christos { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
524 1.1 christos #endif
525 1.1 christos { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
526 1.1 christos { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
527 1.1 christos { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
528 1.1 christos { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
529 1.1 christos { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
530 1.1 christos { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
531 1.1 christos { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
532 1.1 christos { NULL, 0, 0 }
533 1.1 christos };
534 1.1 christos
535 1.1 christos /* Military timezone table. */
536 1.9 christos static const TABLE const MilitaryTable[] = {
537 1.1 christos { "a", tZONE, HOUR( 1) },
538 1.1 christos { "b", tZONE, HOUR( 2) },
539 1.1 christos { "c", tZONE, HOUR( 3) },
540 1.1 christos { "d", tZONE, HOUR( 4) },
541 1.1 christos { "e", tZONE, HOUR( 5) },
542 1.1 christos { "f", tZONE, HOUR( 6) },
543 1.1 christos { "g", tZONE, HOUR( 7) },
544 1.1 christos { "h", tZONE, HOUR( 8) },
545 1.1 christos { "i", tZONE, HOUR( 9) },
546 1.1 christos { "k", tZONE, HOUR( 10) },
547 1.1 christos { "l", tZONE, HOUR( 11) },
548 1.1 christos { "m", tZONE, HOUR( 12) },
549 1.1 christos { "n", tZONE, HOUR(- 1) },
550 1.1 christos { "o", tZONE, HOUR(- 2) },
551 1.1 christos { "p", tZONE, HOUR(- 3) },
552 1.1 christos { "q", tZONE, HOUR(- 4) },
553 1.1 christos { "r", tZONE, HOUR(- 5) },
554 1.1 christos { "s", tZONE, HOUR(- 6) },
555 1.1 christos { "t", tZONE, HOUR(- 7) },
556 1.1 christos { "u", tZONE, HOUR(- 8) },
557 1.1 christos { "v", tZONE, HOUR(- 9) },
558 1.1 christos { "w", tZONE, HOUR(-10) },
559 1.1 christos { "x", tZONE, HOUR(-11) },
560 1.1 christos { "y", tZONE, HOUR(-12) },
561 1.1 christos { "z", tZONE, HOUR( 0) },
562 1.1 christos { NULL, 0, 0 }
563 1.1 christos };
564 1.1 christos
565 1.1 christos
566 1.1 christos
568 1.1 christos
569 1.1 christos /* ARGSUSED */
570 1.9 christos static int
571 1.1 christos yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
572 1.1 christos {
573 1.1 christos return 0;
574 1.1 christos }
575 1.1 christos
576 1.1 christos
577 1.1 christos static time_t
578 1.1 christos ToSeconds(
579 1.1 christos time_t Hours,
580 1.1 christos time_t Minutes,
581 1.1 christos time_t Seconds,
582 1.1 christos MERIDIAN Meridian
583 1.1 christos )
584 1.1 christos {
585 1.1 christos if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
586 1.1 christos return -1;
587 1.1 christos switch (Meridian) {
588 1.1 christos case MER24:
589 1.1 christos if (Hours < 0 || Hours > 23)
590 1.1 christos return -1;
591 1.1 christos return (Hours * 60L + Minutes) * 60L + Seconds;
592 1.1 christos case MERam:
593 1.1 christos if (Hours < 1 || Hours > 12)
594 1.1 christos return -1;
595 1.1 christos if (Hours == 12)
596 1.1 christos Hours = 0;
597 1.1 christos return (Hours * 60L + Minutes) * 60L + Seconds;
598 1.1 christos case MERpm:
599 1.1 christos if (Hours < 1 || Hours > 12)
600 1.1 christos return -1;
601 1.1 christos if (Hours == 12)
602 1.1 christos Hours = 0;
603 1.1 christos return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
604 1.1 christos default:
605 1.1 christos abort ();
606 1.1 christos }
607 1.1 christos /* NOTREACHED */
608 1.1 christos }
609 1.6 christos
610 1.6 christos static int
611 1.6 christos isLeap(int year)
612 1.6 christos {
613 1.6 christos return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
614 1.6 christos }
615 1.1 christos
616 1.1 christos
617 1.1 christos /* Year is either
618 1.1 christos * A negative number, which means to use its absolute value (why?)
619 1.1 christos * A number from 0 to 99, which means a year from 1900 to 1999, or
620 1.1 christos * The actual year (>=100). */
621 1.1 christos static time_t
622 1.1 christos Convert(
623 1.1 christos time_t Month,
624 1.1 christos time_t Day,
625 1.1 christos time_t Year,
626 1.1 christos time_t Hours,
627 1.1 christos time_t Minutes,
628 1.9 christos time_t Seconds,
629 1.1 christos time_t Timezone,
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.9 christos oJulian = Julian;
675 1.9 christos Julian += Timezone * 60L;
676 1.6 christos if (oJulian > Julian)
677 1.6 christos return -1;
678 1.6 christos oJulian = Julian;
679 1.1 christos
680 1.1 christos if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
681 1.6 christos return -1;
682 1.1 christos
683 1.6 christos Julian += tod;
684 1.6 christos if (oJulian > Julian)
685 1.6 christos return -1;
686 1.6 christos
687 1.6 christos if (DSTmode == DSTon || (DSTmode == DSTmaybe)) {
688 1.6 christos struct tm *tm;
689 1.6 christos if ((tm = localtime(&Julian)) == NULL)
690 1.6 christos return -1;
691 1.6 christos if (tm->tm_isdst)
692 1.6 christos Julian -= 60 * 60;
693 1.1 christos }
694 1.1 christos return Julian;
695 1.1 christos }
696 1.1 christos
697 1.1 christos
698 1.1 christos static time_t
699 1.1 christos DSTcorrect(
700 1.1 christos time_t Start,
701 1.1 christos time_t Future
702 1.1 christos )
703 1.1 christos {
704 1.1 christos time_t StartDay;
705 1.6 christos time_t FutureDay;
706 1.6 christos struct tm *tm;
707 1.6 christos
708 1.6 christos if ((tm = localtime(&Start)) == NULL)
709 1.6 christos return -1;
710 1.6 christos StartDay = (tm->tm_hour + 1) % 24;
711 1.6 christos
712 1.6 christos if ((tm = localtime(&Future)) == NULL)
713 1.6 christos return -1;
714 1.1 christos FutureDay = (tm->tm_hour + 1) % 24;
715 1.1 christos
716 1.1 christos return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
717 1.1 christos }
718 1.1 christos
719 1.1 christos
720 1.1 christos static time_t
721 1.1 christos RelativeDate(
722 1.1 christos time_t Start,
723 1.1 christos time_t DayOrdinal,
724 1.1 christos time_t DayNumber
725 1.1 christos )
726 1.1 christos {
727 1.1 christos struct tm *tm;
728 1.1 christos time_t now;
729 1.1 christos
730 1.1 christos now = Start;
731 1.1 christos tm = localtime(&now);
732 1.1 christos now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
733 1.1 christos now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
734 1.1 christos return DSTcorrect(Start, now);
735 1.1 christos }
736 1.1 christos
737 1.1 christos
738 1.1 christos static time_t
739 1.1 christos RelativeMonth(
740 1.9 christos time_t Start,
741 1.9 christos time_t RelMonth,
742 1.1 christos time_t Timezone
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.9 christos (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
760 1.1 christos Timezone, MER24, DSTmaybe));
761 1.1 christos }
762 1.1 christos
763 1.1 christos
764 1.9 christos static int
765 1.1 christos LookupWord(YYSTYPE *yylval, 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.9 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.9 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.9 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.9 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.9 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.9 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.9 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.9 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.9 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.9 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.9 christos static int
871 1.1 christos yylex(YYSTYPE *yylval, const char **yyInput)
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.9 christos int sign;
878 1.1 christos const char *inp = *yyInput;
879 1.1 christos
880 1.9 christos for ( ; ; ) {
881 1.9 christos while (isspace((unsigned char)*inp))
882 1.1 christos inp++;
883 1.9 christos
884 1.1 christos if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
885 1.1 christos if (c == '-' || c == '+') {
886 1.9 christos sign = c == '-' ? -1 : 1;
887 1.1 christos if (!isdigit((unsigned char)*++inp))
888 1.1 christos /* skip the '-' sign */
889 1.1 christos continue;
890 1.1 christos }
891 1.1 christos else
892 1.9 christos sign = 0;
893 1.9 christos for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
894 1.1 christos yylval->Number = 10 * yylval->Number + c - '0';
895 1.9 christos if (sign < 0)
896 1.9 christos yylval->Number = -yylval->Number;
897 1.1 christos *yyInput = --inp;
898 1.1 christos return sign ? tSNUMBER : tUNUMBER;
899 1.1 christos }
900 1.9 christos if (isalpha((unsigned char)c)) {
901 1.1 christos for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
902 1.1 christos if (p < &buff[sizeof buff - 1])
903 1.1 christos *p++ = c;
904 1.9 christos *p = '\0';
905 1.9 christos *yyInput = --inp;
906 1.1 christos return LookupWord(yylval, buff);
907 1.5 tron }
908 1.9 christos if (c == '@') {
909 1.5 tron *yyInput = ++inp;
910 1.5 tron return AT_SIGN;
911 1.9 christos }
912 1.9 christos if (c != '(') {
913 1.9 christos *yyInput = ++inp;
914 1.9 christos return c;
915 1.1 christos }
916 1.1 christos Count = 0;
917 1.9 christos do {
918 1.1 christos c = *inp++;
919 1.1 christos if (c == '\0')
920 1.1 christos return c;
921 1.1 christos if (c == '(')
922 1.1 christos Count++;
923 1.1 christos else if (c == ')')
924 1.1 christos Count--;
925 1.1 christos } while (Count > 0);
926 1.1 christos }
927 1.1 christos }
928 1.1 christos
929 1.1 christos #define TM_YEAR_ORIGIN 1900
930 1.1 christos
931 1.6 christos /* Yield A - B, measured in seconds. */
932 1.1 christos static time_t
933 1.1 christos difftm (struct tm *a, struct tm *b)
934 1.1 christos {
935 1.1 christos int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
936 1.1 christos int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
937 1.1 christos int days = (
938 1.1 christos /* difference in day of year */
939 1.1 christos a->tm_yday - b->tm_yday
940 1.1 christos /* + intervening leap days */
941 1.1 christos + ((ay >> 2) - (by >> 2))
942 1.1 christos - (ay/100 - by/100)
943 1.1 christos + ((ay/100 >> 2) - (by/100 >> 2))
944 1.1 christos /* + difference in years * 365 */
945 1.1 christos + (long)(ay-by) * 365
946 1.6 christos );
947 1.1 christos return ((time_t)60*(60*(24*days + (a->tm_hour - b->tm_hour))
948 1.1 christos + (a->tm_min - b->tm_min))
949 1.1 christos + (a->tm_sec - b->tm_sec));
950 1.1 christos }
951 1.1 christos
952 1.1 christos time_t
953 1.1 christos parsedate(const char *p, const time_t *now, const int *zone)
954 1.1 christos {
955 1.1 christos struct tm gmt, local, *gmt_ptr, *tm;
956 1.1 christos time_t nowt;
957 1.1 christos int zonet;
958 1.6 christos time_t Start;
959 1.9 christos time_t tod, rm;
960 1.1 christos struct dateinfo param;
961 1.1 christos
962 1.1 christos if (now == NULL || zone == NULL) {
963 1.1 christos now = &nowt;
964 1.1 christos zone = &zonet;
965 1.1 christos (void)time(&nowt);
966 1.1 christos
967 1.1 christos gmt_ptr = gmtime_r(now, &gmt);
968 1.1 christos if ((tm = localtime_r(now, &local)) == NULL)
969 1.1 christos return -1;
970 1.1 christos
971 1.1 christos if (gmt_ptr != NULL)
972 1.1 christos zonet = difftm(&gmt, &local) / 60;
973 1.1 christos else
974 1.1 christos /* We are on a system like VMS, where the system clock is
975 1.1 christos in local time and the system has no concept of timezones.
976 1.1 christos Hopefully we can fake this out (for the case in which the
977 1.1 christos user specifies no timezone) by just saying the timezone
978 1.1 christos is zero. */
979 1.1 christos zonet = 0;
980 1.1 christos
981 1.1 christos if (local.tm_isdst)
982 1.1 christos zonet += 60;
983 1.1 christos } else {
984 1.1 christos if ((tm = localtime_r(now, &local)) == NULL)
985 1.1 christos return -1;
986 1.9 christos }
987 1.9 christos param.yyYear = tm->tm_year + 1900;
988 1.9 christos param.yyMonth = tm->tm_mon + 1;
989 1.9 christos param.yyDay = tm->tm_mday;
990 1.9 christos param.yyTimezone = *zone;
991 1.9 christos param.yyDSTmode = DSTmaybe;
992 1.9 christos param.yyHour = 0;
993 1.9 christos param.yyMinutes = 0;
994 1.9 christos param.yySeconds = 0;
995 1.9 christos param.yyMeridian = MER24;
996 1.9 christos param.yyRelSeconds = 0;
997 1.9 christos param.yyRelMonth = 0;
998 1.9 christos param.yyHaveDate = 0;
999 1.9 christos param.yyHaveDay = 0;
1000 1.9 christos param.yyHaveRel = 0;
1001 1.9 christos param.yyHaveTime = 0;
1002 1.9 christos param.yyHaveZone = 0;
1003 1.9 christos
1004 1.9 christos if (yyparse(¶m, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
1005 1.9 christos param.yyHaveDate > 1 || param.yyHaveDay > 1)
1006 1.9 christos return -1;
1007 1.9 christos
1008 1.9 christos if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
1009 1.9 christos Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
1010 1.9 christos param.yyMinutes, param.yySeconds, param.yyTimezone,
1011 1.1 christos param.yyMeridian, param.yyDSTmode);
1012 1.1 christos if (Start < 0)
1013 1.1 christos return -1;
1014 1.1 christos }
1015 1.1 christos else {
1016 1.9 christos Start = *now;
1017 1.1 christos if (!param.yyHaveRel)
1018 1.1 christos Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
1019 1.1 christos }
1020 1.9 christos
1021 1.9 christos Start += param.yyRelSeconds;
1022 1.6 christos rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
1023 1.6 christos if (rm == -1)
1024 1.6 christos return -1;
1025 1.1 christos Start += rm;
1026 1.9 christos
1027 1.9 christos if (param.yyHaveDay && !param.yyHaveDate) {
1028 1.1 christos tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
1029 1.1 christos Start += tod;
1030 1.1 christos }
1031 1.6 christos
1032 1.1 christos return Start;
1033 1.1 christos }
1034 1.1 christos
1035 1.1 christos
1036 1.1 christos #if defined(TEST)
1037 1.1 christos
1038 1.1 christos /* ARGSUSED */
1039 1.1 christos int
1040 1.1 christos main(ac, av)
1041 1.1 christos int ac;
1042 1.1 christos char *av[];
1043 1.1 christos {
1044 1.1 christos char buff[128];
1045 1.1 christos time_t d;
1046 1.1 christos
1047 1.1 christos (void)printf("Enter date, or blank line to exit.\n\t> ");
1048 1.1 christos (void)fflush(stdout);
1049 1.1 christos while (gets(buff) && buff[0]) {
1050 1.1 christos d = parsedate(buff, NULL, NULL);
1051 1.1 christos if (d == -1)
1052 1.1 christos (void)printf("Bad format - couldn't convert.\n");
1053 1.1 christos else
1054 1.1 christos (void)printf("%s", ctime(&d));
1055 1.1 christos (void)printf("\t> ");
1056 1.1 christos (void)fflush(stdout);
1057 1.1 christos }
1058 1.1 christos exit(0);
1059 1.1 christos /* NOTREACHED */
1060 1.1 christos }
1061 #endif /* defined(TEST) */
1062