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