*** empty log message ***
[coreutils.git] / lib / getdate.y
blob86a3d46910c7dee958fad9755cf574a8a5f8c555
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 code is in the public domain and has no copyright.
9 */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 # ifdef FORCE_ALLOCA_H
14 # include <alloca.h>
15 # endif
16 #endif
18 /* Since the code of getdate.y is not included in the Emacs executable
19 itself, there is no need to #define static in this file. Even if
20 the code were included in the Emacs executable, it probably
21 wouldn't do any harm to #undef it here; this will only cause
22 problems if we try to write to a static variable, which I don't
23 think this code needs to do. */
24 #ifdef emacs
25 # undef static
26 #endif
28 #include <stdio.h>
29 #include <ctype.h>
31 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
32 # define IN_CTYPE_DOMAIN(c) 1
33 #else
34 # define IN_CTYPE_DOMAIN(c) isascii(c)
35 #endif
37 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
38 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
39 #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
40 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
42 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
43 - Its arg may be any int or unsigned int; it need not be an unsigned char.
44 - It's guaranteed to evaluate its argument exactly once.
45 - It's typically faster.
46 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
47 only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
48 it's important to use the locale's definition of `digit' even when the
49 host does not conform to Posix. */
50 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
52 #if defined (STDC_HEADERS) || defined (USG)
53 # include <string.h>
54 #endif
56 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
57 # define __attribute__(x)
58 #endif
60 #ifndef ATTRIBUTE_UNUSED
61 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
62 #endif
64 /* Some old versions of bison generate parsers that use bcopy.
65 That loses on systems that don't provide the function, so we have
66 to redefine it here. */
67 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
68 # define bcopy(from, to, len) memcpy ((to), (from), (len))
69 #endif
71 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
72 as well as gratuitiously global symbol names, so we can have multiple
73 yacc generated parsers in the same program. Note that these are only
74 the variables produced by yacc. If other parser generators (bison,
75 byacc, etc) produce additional global names that conflict at link time,
76 then those parser generators need to be fixed instead of adding those
77 names to this list. */
79 #define yymaxdepth gd_maxdepth
80 #define yyparse gd_parse
81 #define yylex gd_lex
82 #define yyerror gd_error
83 #define yylval gd_lval
84 #define yychar gd_char
85 #define yydebug gd_debug
86 #define yypact gd_pact
87 #define yyr1 gd_r1
88 #define yyr2 gd_r2
89 #define yydef gd_def
90 #define yychk gd_chk
91 #define yypgo gd_pgo
92 #define yyact gd_act
93 #define yyexca gd_exca
94 #define yyerrflag gd_errflag
95 #define yynerrs gd_nerrs
96 #define yyps gd_ps
97 #define yypv gd_pv
98 #define yys gd_s
99 #define yy_yys gd_yys
100 #define yystate gd_state
101 #define yytmp gd_tmp
102 #define yyv gd_v
103 #define yy_yyv gd_yyv
104 #define yyval gd_val
105 #define yylloc gd_lloc
106 #define yyreds gd_reds /* With YYDEBUG defined */
107 #define yytoks gd_toks /* With YYDEBUG defined */
108 #define yylhs gd_yylhs
109 #define yylen gd_yylen
110 #define yydefred gd_yydefred
111 #define yydgoto gd_yydgoto
112 #define yysindex gd_yysindex
113 #define yyrindex gd_yyrindex
114 #define yygindex gd_yygindex
115 #define yytable gd_yytable
116 #define yycheck gd_yycheck
118 static int yylex ();
119 static int yyerror ();
121 #define EPOCH 1970
122 #define HOUR(x) ((x) * 60)
124 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
127 ** An entry in the lexical lookup table.
129 typedef struct _TABLE {
130 const char *name;
131 int type;
132 int value;
133 } TABLE;
137 ** Meridian: am, pm, or 24-hour style.
139 typedef enum _MERIDIAN {
140 MERam, MERpm, MER24
141 } MERIDIAN;
145 ** Global variables. We could get rid of most of these by using a good
146 ** union as the yacc stack. (This routine was originally written before
147 ** yacc had the %union construct.) Maybe someday; right now we only use
148 ** the %union very rarely.
150 static const char *yyInput;
151 static int yyDayOrdinal;
152 static int yyDayNumber;
153 static int yyHaveDate;
154 static int yyHaveDay;
155 static int yyHaveRel;
156 static int yyHaveTime;
157 static int yyHaveZone;
158 static int yyTimezone;
159 static int yyDay;
160 static int yyHour;
161 static int yyMinutes;
162 static int yyMonth;
163 static int yySeconds;
164 static int yyYear;
165 static MERIDIAN yyMeridian;
166 static int yyRelDay;
167 static int yyRelHour;
168 static int yyRelMinutes;
169 static int yyRelMonth;
170 static int yyRelSeconds;
171 static int yyRelYear;
175 /* This grammar has 13 shift/reduce conflicts. */
176 %expect 13
178 %union {
179 int Number;
180 enum _MERIDIAN Meridian;
183 %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
184 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
185 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
187 %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
188 %type <Number> tMONTH tMONTH_UNIT
189 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
190 %type <Meridian> tMERIDIAN o_merid
194 spec : /* NULL */
195 | spec item
198 item : time {
199 yyHaveTime++;
201 | zone {
202 yyHaveZone++;
204 | date {
205 yyHaveDate++;
207 | day {
208 yyHaveDay++;
210 | rel {
211 yyHaveRel++;
213 | number
216 time : tUNUMBER tMERIDIAN {
217 yyHour = $1;
218 yyMinutes = 0;
219 yySeconds = 0;
220 yyMeridian = $2;
222 | tUNUMBER ':' tUNUMBER o_merid {
223 yyHour = $1;
224 yyMinutes = $3;
225 yySeconds = 0;
226 yyMeridian = $4;
228 | tUNUMBER ':' tUNUMBER tSNUMBER {
229 yyHour = $1;
230 yyMinutes = $3;
231 yyMeridian = MER24;
232 yyHaveZone++;
233 yyTimezone = ($4 < 0
234 ? -$4 % 100 + (-$4 / 100) * 60
235 : - ($4 % 100 + ($4 / 100) * 60));
237 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
238 yyHour = $1;
239 yyMinutes = $3;
240 yySeconds = $5;
241 yyMeridian = $6;
243 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
244 yyHour = $1;
245 yyMinutes = $3;
246 yySeconds = $5;
247 yyMeridian = MER24;
248 yyHaveZone++;
249 yyTimezone = ($6 < 0
250 ? -$6 % 100 + (-$6 / 100) * 60
251 : - ($6 % 100 + ($6 / 100) * 60));
255 zone : tZONE {
256 yyTimezone = $1;
258 | tDAYZONE {
259 yyTimezone = $1 - 60;
262 tZONE tDST {
263 yyTimezone = $1 - 60;
267 day : tDAY {
268 yyDayOrdinal = 1;
269 yyDayNumber = $1;
271 | tDAY ',' {
272 yyDayOrdinal = 1;
273 yyDayNumber = $1;
275 | tUNUMBER tDAY {
276 yyDayOrdinal = $1;
277 yyDayNumber = $2;
281 date : tUNUMBER '/' tUNUMBER {
282 yyMonth = $1;
283 yyDay = $3;
285 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
286 /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
287 The goal in recognizing YYYY/MM/DD is solely to support legacy
288 machine-generated dates like those in an RCS log listing. If
289 you want portability, use the ISO 8601 format. */
290 if ($1 >= 1000)
292 yyYear = $1;
293 yyMonth = $3;
294 yyDay = $5;
296 else
298 yyMonth = $1;
299 yyDay = $3;
300 yyYear = $5;
303 | tUNUMBER tSNUMBER tSNUMBER {
304 /* ISO 8601 format. yyyy-mm-dd. */
305 yyYear = $1;
306 yyMonth = -$2;
307 yyDay = -$3;
309 | tUNUMBER tMONTH tSNUMBER {
310 /* e.g. 17-JUN-1992. */
311 yyDay = $1;
312 yyMonth = $2;
313 yyYear = -$3;
315 | tMONTH tUNUMBER {
316 yyMonth = $1;
317 yyDay = $2;
319 | tMONTH tUNUMBER ',' tUNUMBER {
320 yyMonth = $1;
321 yyDay = $2;
322 yyYear = $4;
324 | tUNUMBER tMONTH {
325 yyMonth = $2;
326 yyDay = $1;
328 | tUNUMBER tMONTH tUNUMBER {
329 yyMonth = $2;
330 yyDay = $1;
331 yyYear = $3;
335 rel : relunit tAGO {
336 yyRelSeconds = -yyRelSeconds;
337 yyRelMinutes = -yyRelMinutes;
338 yyRelHour = -yyRelHour;
339 yyRelDay = -yyRelDay;
340 yyRelMonth = -yyRelMonth;
341 yyRelYear = -yyRelYear;
343 | relunit
346 relunit : tUNUMBER tYEAR_UNIT {
347 yyRelYear += $1 * $2;
349 | tSNUMBER tYEAR_UNIT {
350 yyRelYear += $1 * $2;
352 | tYEAR_UNIT {
353 yyRelYear += $1;
355 | tUNUMBER tMONTH_UNIT {
356 yyRelMonth += $1 * $2;
358 | tSNUMBER tMONTH_UNIT {
359 yyRelMonth += $1 * $2;
361 | tMONTH_UNIT {
362 yyRelMonth += $1;
364 | tUNUMBER tDAY_UNIT {
365 yyRelDay += $1 * $2;
367 | tSNUMBER tDAY_UNIT {
368 yyRelDay += $1 * $2;
370 | tDAY_UNIT {
371 yyRelDay += $1;
373 | tUNUMBER tHOUR_UNIT {
374 yyRelHour += $1 * $2;
376 | tSNUMBER tHOUR_UNIT {
377 yyRelHour += $1 * $2;
379 | tHOUR_UNIT {
380 yyRelHour += $1;
382 | tUNUMBER tMINUTE_UNIT {
383 yyRelMinutes += $1 * $2;
385 | tSNUMBER tMINUTE_UNIT {
386 yyRelMinutes += $1 * $2;
388 | tMINUTE_UNIT {
389 yyRelMinutes += $1;
391 | tUNUMBER tSEC_UNIT {
392 yyRelSeconds += $1 * $2;
394 | tSNUMBER tSEC_UNIT {
395 yyRelSeconds += $1 * $2;
397 | tSEC_UNIT {
398 yyRelSeconds += $1;
402 number : tUNUMBER
404 if (yyHaveTime && yyHaveDate && !yyHaveRel)
405 yyYear = $1;
406 else
408 if ($1>10000)
410 yyHaveDate++;
411 yyDay= ($1)%100;
412 yyMonth= ($1/100)%100;
413 yyYear = $1/10000;
415 else
417 yyHaveTime++;
418 if ($1 < 100)
420 yyHour = $1;
421 yyMinutes = 0;
423 else
425 yyHour = $1 / 100;
426 yyMinutes = $1 % 100;
428 yySeconds = 0;
429 yyMeridian = MER24;
435 o_merid : /* NULL */
437 $$ = MER24;
439 | tMERIDIAN
441 $$ = $1;
447 /* Include this file down here because bison inserts code above which
448 may define-away `const'. We want the prototype for get_date to have
449 the same signature as the function definition does. */
450 #include "getdate.h"
452 extern struct tm *gmtime ();
453 extern struct tm *localtime ();
454 extern time_t mktime ();
456 /* Month and day table. */
457 static TABLE const MonthDayTable[] = {
458 { "january", tMONTH, 1 },
459 { "february", tMONTH, 2 },
460 { "march", tMONTH, 3 },
461 { "april", tMONTH, 4 },
462 { "may", tMONTH, 5 },
463 { "june", tMONTH, 6 },
464 { "july", tMONTH, 7 },
465 { "august", tMONTH, 8 },
466 { "september", tMONTH, 9 },
467 { "sept", tMONTH, 9 },
468 { "october", tMONTH, 10 },
469 { "november", tMONTH, 11 },
470 { "december", tMONTH, 12 },
471 { "sunday", tDAY, 0 },
472 { "monday", tDAY, 1 },
473 { "tuesday", tDAY, 2 },
474 { "tues", tDAY, 2 },
475 { "wednesday", tDAY, 3 },
476 { "wednes", tDAY, 3 },
477 { "thursday", tDAY, 4 },
478 { "thur", tDAY, 4 },
479 { "thurs", tDAY, 4 },
480 { "friday", tDAY, 5 },
481 { "saturday", tDAY, 6 },
482 { NULL, 0, 0 }
485 /* Time units table. */
486 static TABLE const UnitsTable[] = {
487 { "year", tYEAR_UNIT, 1 },
488 { "month", tMONTH_UNIT, 1 },
489 { "fortnight", tDAY_UNIT, 14 },
490 { "week", tDAY_UNIT, 7 },
491 { "day", tDAY_UNIT, 1 },
492 { "hour", tHOUR_UNIT, 1 },
493 { "minute", tMINUTE_UNIT, 1 },
494 { "min", tMINUTE_UNIT, 1 },
495 { "second", tSEC_UNIT, 1 },
496 { "sec", tSEC_UNIT, 1 },
497 { NULL, 0, 0 }
500 /* Assorted relative-time words. */
501 static TABLE const OtherTable[] = {
502 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
503 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
504 { "today", tMINUTE_UNIT, 0 },
505 { "now", tMINUTE_UNIT, 0 },
506 { "last", tUNUMBER, -1 },
507 { "this", tMINUTE_UNIT, 0 },
508 { "next", tUNUMBER, 1 },
509 { "first", tUNUMBER, 1 },
510 /* { "second", tUNUMBER, 2 }, */
511 { "third", tUNUMBER, 3 },
512 { "fourth", tUNUMBER, 4 },
513 { "fifth", tUNUMBER, 5 },
514 { "sixth", tUNUMBER, 6 },
515 { "seventh", tUNUMBER, 7 },
516 { "eighth", tUNUMBER, 8 },
517 { "ninth", tUNUMBER, 9 },
518 { "tenth", tUNUMBER, 10 },
519 { "eleventh", tUNUMBER, 11 },
520 { "twelfth", tUNUMBER, 12 },
521 { "ago", tAGO, 1 },
522 { NULL, 0, 0 }
525 /* The timezone table. */
526 static TABLE const TimezoneTable[] = {
527 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
528 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
529 { "utc", tZONE, HOUR ( 0) },
530 { "wet", tZONE, HOUR ( 0) }, /* Western European */
531 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
532 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
533 { "at", tZONE, HOUR ( 2) }, /* Azores */
534 #if 0
535 /* For completeness. BST is also British Summer, and GST is
536 * also Guam Standard. */
537 { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
538 { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
539 #endif
540 #if 0
541 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
542 { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
543 { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
544 #endif
545 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
546 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
547 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
548 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
549 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
550 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
551 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
552 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
553 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
554 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
555 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
556 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
557 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
558 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
559 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
560 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
561 { "nt", tZONE, HOUR (11) }, /* Nome */
562 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
563 { "cet", tZONE, -HOUR (1) }, /* Central European */
564 { "met", tZONE, -HOUR (1) }, /* Middle European */
565 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
566 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
567 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
568 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
569 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
570 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
571 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
572 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
573 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
574 #if 0
575 { "it", tZONE, -HOUR (3.5) },/* Iran */
576 #endif
577 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
578 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
579 #if 0
580 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
581 #endif
582 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
583 #if 0
584 /* For completeness. NST is also Newfoundland Standard, and SST is
585 * also Swedish Summer. */
586 { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
587 { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
588 #endif /* 0 */
589 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
590 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
591 #if 0
592 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
593 #endif
594 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
595 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
596 #if 0
597 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
598 { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
599 #endif
600 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
601 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
602 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
603 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
604 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
605 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
606 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
607 { NULL, 0, 0 }
610 /* Military timezone table. */
611 static TABLE const MilitaryTable[] = {
612 { "a", tZONE, HOUR ( 1) },
613 { "b", tZONE, HOUR ( 2) },
614 { "c", tZONE, HOUR ( 3) },
615 { "d", tZONE, HOUR ( 4) },
616 { "e", tZONE, HOUR ( 5) },
617 { "f", tZONE, HOUR ( 6) },
618 { "g", tZONE, HOUR ( 7) },
619 { "h", tZONE, HOUR ( 8) },
620 { "i", tZONE, HOUR ( 9) },
621 { "k", tZONE, HOUR ( 10) },
622 { "l", tZONE, HOUR ( 11) },
623 { "m", tZONE, HOUR ( 12) },
624 { "n", tZONE, HOUR (- 1) },
625 { "o", tZONE, HOUR (- 2) },
626 { "p", tZONE, HOUR (- 3) },
627 { "q", tZONE, HOUR (- 4) },
628 { "r", tZONE, HOUR (- 5) },
629 { "s", tZONE, HOUR (- 6) },
630 { "t", tZONE, HOUR (- 7) },
631 { "u", tZONE, HOUR (- 8) },
632 { "v", tZONE, HOUR (- 9) },
633 { "w", tZONE, HOUR (-10) },
634 { "x", tZONE, HOUR (-11) },
635 { "y", tZONE, HOUR (-12) },
636 { "z", tZONE, HOUR ( 0) },
637 { NULL, 0, 0 }
643 /* ARGSUSED */
644 static int
645 yyerror (s)
646 char *s ATTRIBUTE_UNUSED;
648 return 0;
651 static int
652 ToHour (Hours, Meridian)
653 int Hours;
654 MERIDIAN Meridian;
656 switch (Meridian)
658 case MER24:
659 if (Hours < 0 || Hours > 23)
660 return -1;
661 return Hours;
662 case MERam:
663 if (Hours < 1 || Hours > 12)
664 return -1;
665 if (Hours == 12)
666 Hours = 0;
667 return Hours;
668 case MERpm:
669 if (Hours < 1 || Hours > 12)
670 return -1;
671 if (Hours == 12)
672 Hours = 0;
673 return Hours + 12;
674 default:
675 abort ();
677 /* NOTREACHED */
680 static int
681 ToYear (Year)
682 int Year;
684 if (Year < 0)
685 Year = -Year;
687 /* XPG4 suggests that years 00-68 map to 2000-2068, and
688 years 69-99 map to 1969-1999. */
689 if (Year < 69)
690 Year += 2000;
691 else if (Year < 100)
692 Year += 1900;
694 return Year;
697 static int
698 LookupWord (buff)
699 char *buff;
701 register char *p;
702 register char *q;
703 register const TABLE *tp;
704 int i;
705 int abbrev;
707 /* Make it lowercase. */
708 for (p = buff; *p; p++)
709 if (ISUPPER (*p))
710 *p = tolower (*p);
712 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
714 yylval.Meridian = MERam;
715 return tMERIDIAN;
717 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
719 yylval.Meridian = MERpm;
720 return tMERIDIAN;
723 /* See if we have an abbreviation for a month. */
724 if (strlen (buff) == 3)
725 abbrev = 1;
726 else if (strlen (buff) == 4 && buff[3] == '.')
728 abbrev = 1;
729 buff[3] = '\0';
731 else
732 abbrev = 0;
734 for (tp = MonthDayTable; tp->name; tp++)
736 if (abbrev)
738 if (strncmp (buff, tp->name, 3) == 0)
740 yylval.Number = tp->value;
741 return tp->type;
744 else if (strcmp (buff, tp->name) == 0)
746 yylval.Number = tp->value;
747 return tp->type;
751 for (tp = TimezoneTable; tp->name; tp++)
752 if (strcmp (buff, tp->name) == 0)
754 yylval.Number = tp->value;
755 return tp->type;
758 if (strcmp (buff, "dst") == 0)
759 return tDST;
761 for (tp = UnitsTable; tp->name; tp++)
762 if (strcmp (buff, tp->name) == 0)
764 yylval.Number = tp->value;
765 return tp->type;
768 /* Strip off any plural and try the units table again. */
769 i = strlen (buff) - 1;
770 if (buff[i] == 's')
772 buff[i] = '\0';
773 for (tp = UnitsTable; tp->name; tp++)
774 if (strcmp (buff, tp->name) == 0)
776 yylval.Number = tp->value;
777 return tp->type;
779 buff[i] = 's'; /* Put back for "this" in OtherTable. */
782 for (tp = OtherTable; tp->name; tp++)
783 if (strcmp (buff, tp->name) == 0)
785 yylval.Number = tp->value;
786 return tp->type;
789 /* Military timezones. */
790 if (buff[1] == '\0' && ISALPHA (*buff))
792 for (tp = MilitaryTable; tp->name; tp++)
793 if (strcmp (buff, tp->name) == 0)
795 yylval.Number = tp->value;
796 return tp->type;
800 /* Drop out any periods and try the timezone table again. */
801 for (i = 0, p = q = buff; *q; q++)
802 if (*q != '.')
803 *p++ = *q;
804 else
805 i++;
806 *p = '\0';
807 if (i)
808 for (tp = TimezoneTable; tp->name; tp++)
809 if (strcmp (buff, tp->name) == 0)
811 yylval.Number = tp->value;
812 return tp->type;
815 return tID;
818 static int
819 yylex ()
821 register char c;
822 register char *p;
823 char buff[20];
824 int Count;
825 int sign;
827 for (;;)
829 while (ISSPACE (*yyInput))
830 yyInput++;
832 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
834 if (c == '-' || c == '+')
836 sign = c == '-' ? -1 : 1;
837 if (!ISDIGIT (*++yyInput))
838 /* skip the '-' sign */
839 continue;
841 else
842 sign = 0;
843 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
844 yylval.Number = 10 * yylval.Number + c - '0';
845 yyInput--;
846 if (sign < 0)
847 yylval.Number = -yylval.Number;
848 return sign ? tSNUMBER : tUNUMBER;
850 if (ISALPHA (c))
852 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
853 if (p < &buff[sizeof buff - 1])
854 *p++ = c;
855 *p = '\0';
856 yyInput--;
857 return LookupWord (buff);
859 if (c != '(')
860 return *yyInput++;
861 Count = 0;
864 c = *yyInput++;
865 if (c == '\0')
866 return c;
867 if (c == '(')
868 Count++;
869 else if (c == ')')
870 Count--;
872 while (Count > 0);
876 #define TM_YEAR_ORIGIN 1900
878 /* Yield A - B, measured in seconds. */
879 static long
880 difftm (a, b)
881 struct tm *a, *b;
883 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
884 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
885 long days = (
886 /* difference in day of year */
887 a->tm_yday - b->tm_yday
888 /* + intervening leap days */
889 + ((ay >> 2) - (by >> 2))
890 - (ay / 100 - by / 100)
891 + ((ay / 100 >> 2) - (by / 100 >> 2))
892 /* + difference in years * 365 */
893 + (long) (ay - by) * 365
895 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
896 + (a->tm_min - b->tm_min))
897 + (a->tm_sec - b->tm_sec));
900 time_t
901 get_date (const char *p, const time_t *now)
903 struct tm tm, tm0, *tmp;
904 time_t Start;
906 yyInput = p;
907 Start = now ? *now : time ((time_t *) NULL);
908 tmp = localtime (&Start);
909 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
910 yyMonth = tmp->tm_mon + 1;
911 yyDay = tmp->tm_mday;
912 yyHour = tmp->tm_hour;
913 yyMinutes = tmp->tm_min;
914 yySeconds = tmp->tm_sec;
915 yyMeridian = MER24;
916 yyRelSeconds = 0;
917 yyRelMinutes = 0;
918 yyRelHour = 0;
919 yyRelDay = 0;
920 yyRelMonth = 0;
921 yyRelYear = 0;
922 yyHaveDate = 0;
923 yyHaveDay = 0;
924 yyHaveRel = 0;
925 yyHaveTime = 0;
926 yyHaveZone = 0;
928 if (yyparse ()
929 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
930 return -1;
932 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
933 tm.tm_mon = yyMonth - 1 + yyRelMonth;
934 tm.tm_mday = yyDay + yyRelDay;
935 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
937 tm.tm_hour = ToHour (yyHour, yyMeridian);
938 if (tm.tm_hour < 0)
939 return -1;
940 tm.tm_min = yyMinutes;
941 tm.tm_sec = yySeconds;
943 else
945 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
947 tm.tm_hour += yyRelHour;
948 tm.tm_min += yyRelMinutes;
949 tm.tm_sec += yyRelSeconds;
950 tm.tm_isdst = -1;
951 tm0 = tm;
953 Start = mktime (&tm);
955 if (Start == (time_t) -1)
958 /* Guard against falsely reporting errors near the time_t boundaries
959 when parsing times in other time zones. For example, if the min
960 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
961 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
962 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
963 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
964 zone by 24 hours to compensate. This algorithm assumes that
965 there is no DST transition within a day of the time_t boundaries. */
966 if (yyHaveZone)
968 tm = tm0;
969 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
971 tm.tm_mday++;
972 yyTimezone -= 24 * 60;
974 else
976 tm.tm_mday--;
977 yyTimezone += 24 * 60;
979 Start = mktime (&tm);
982 if (Start == (time_t) -1)
983 return Start;
986 if (yyHaveDay && !yyHaveDate)
988 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
989 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
990 Start = mktime (&tm);
991 if (Start == (time_t) -1)
992 return Start;
995 if (yyHaveZone)
997 long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
998 if ((Start + delta < Start) != (delta < 0))
999 return -1; /* time_t overflow */
1000 Start += delta;
1003 return Start;
1006 #if defined (TEST)
1008 /* ARGSUSED */
1010 main (ac, av)
1011 int ac;
1012 char *av[];
1014 char buff[MAX_BUFF_LEN + 1];
1015 time_t d;
1017 (void) printf ("Enter date, or blank line to exit.\n\t> ");
1018 (void) fflush (stdout);
1020 buff[MAX_BUFF_LEN] = 0;
1021 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
1023 d = get_date (buff, (time_t *) NULL);
1024 if (d == -1)
1025 (void) printf ("Bad format - couldn't convert.\n");
1026 else
1027 (void) printf ("%s", ctime (&d));
1028 (void) printf ("\t> ");
1029 (void) fflush (stdout);
1031 exit (0);
1032 /* NOTREACHED */
1034 #endif /* defined (TEST) */