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