1 %{
2 /* Parse a string into an internal time stamp.
3
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software
5 Foundation, Inc.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
20
21 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
22 at the University of North Carolina at Chapel Hill. Later tweaked by
23 a couple of people on Usenet. Completely overhauled by Rich $alz
24 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
25
26 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
27 the right thing about local DST. Also modified by Paul Eggert
28 <eggert@cs.ucla.edu> in February 2004 to support
29 nanosecond-resolution time stamps, and in October 2004 to support
30 TZ strings in dates. */
31
32 /* FIXME: Check for arithmetic overflow in all cases, not just
33 some of them. */
34
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38
39 #include "getdate.h"
40
41 /* There's no need to extend the stack, so there's no need to involve
42 alloca. */
43 #define YYSTACK_USE_ALLOCA 0
44
45 /* Tell Bison how much stack space is needed. 20 should be plenty for
46 this grammar, which is not right recursive. Beware setting it too
47 high, since that might cause problems on machines whose
48 implementations have lame stack-overflow checking. */
49 #define YYMAXDEPTH 20
50 #define YYINITDEPTH YYMAXDEPTH
51
52 /* Since the code of getdate.y is not included in the Emacs executable
53 itself, there is no need to #define static in this file. Even if
54 the code were included in the Emacs executable, it probably
55 wouldn't do any harm to #undef it here; this will only cause
56 problems if we try to write to a static variable, which I don't
57 think this code needs to do. */
58 #ifdef emacs
59 # undef static
60 #endif
61
62 #include <ctype.h>
63 #include <limits.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67
68 #include "setenv.h"
69 #include "xalloc.h"
70
71 #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
72 # define IN_CTYPE_DOMAIN(c) 1
73 #else
74 # define IN_CTYPE_DOMAIN(c) isascii (c)
75 #endif
76
77 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
78 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
79 #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
80
81 /* ISDIGIT differs from isdigit, as follows:
82 - Its arg may be any int or unsigned int; it need not be an unsigned char.
83 - It's guaranteed to evaluate its argument exactly once.
84 - It's typically faster.
85 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
86 isdigit unless it's important to use the locale's definition
87 of `digit' even when the host does not conform to POSIX. */
88 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
89
90 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
91 # define __attribute__(x)
92 #endif
93
94 #ifndef ATTRIBUTE_UNUSED
95 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
96 #endif
97
98 /* Shift A right by B bits portably, by dividing A by 2**B and
99 truncating towards minus infinity. A and B should be free of side
100 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
101 INT_BITS is the number of useful bits in an int. GNU code can
102 assume that INT_BITS is at least 32.
103
104 ISO C99 says that A >> B is implementation-defined if A < 0. Some
105 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
106 right in the usual way when A < 0, so SHR falls back on division if
107 ordinary A >> B doesn't seem to be the usual signed shift. */
108 #define SHR(a, b) \
109 (-1 >> 1 == -1 \
110 ? (a) >> (b) \
111 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
112
113 #define EPOCH_YEAR 1970
114 #define TM_YEAR_BASE 1900
115
116 #define HOUR(x) ((x) * 60)
117
118 /* An integer value, and the number of digits in its textual
119 representation. */
120 typedef struct
121 {
122 bool negative;
123 long int value;
124 size_t digits;
125 } textint;
126
127 /* An entry in the lexical lookup table. */
128 typedef struct
129 {
130 char const *name;
131 int type;
132 int value;
133 } table;
134
135 /* Meridian: am, pm, or 24-hour style. */
136 enum { MERam, MERpm, MER24 };
137
138 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
139
140 /* Information passed to and from the parser. */
141 typedef struct
142 {
143 /* The input string remaining to be parsed. */
144 const char *input;
145
146 /* N, if this is the Nth Tuesday. */
147 long int day_ordinal;
148
149 /* Day of week; Sunday is 0. */
150 int day_number;
151
152 /* tm_isdst flag for the local zone. */
153 int local_isdst;
154
155 /* Time zone, in minutes east of UTC. */
156 long int time_zone;
157
158 /* Style used for time. */
159 int meridian;
160
161 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
162 textint year;
163 long int month;
164 long int day;
165 long int hour;
166 long int minutes;
167 struct timespec seconds; /* includes nanoseconds */
168
169 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
170 long int rel_year;
171 long int rel_month;
172 long int rel_day;
173 long int rel_hour;
174 long int rel_minutes;
175 long int rel_seconds;
176 long int rel_ns;
177
178 /* Presence or counts of nonterminals of various flavors parsed so far. */
179 bool timespec_seen;
180 bool rels_seen;
181 size_t dates_seen;
182 size_t days_seen;
183 size_t local_zones_seen;
184 size_t dsts_seen;
185 size_t times_seen;
186 size_t zones_seen;
187
188 /* Table of local time zone abbrevations, terminated by a null entry. */
189 table local_time_zone_table[3];
190 } parser_control;
191
192 union YYSTYPE;
193 static int yylex (union YYSTYPE *, parser_control *);
194 static int yyerror (parser_control *, char *);
195 static long int time_zone_hhmm (textint, long int);
196
197 %}
198
199 /* We want a reentrant parser, even if the TZ manipulation and the calls to
200 localtime and gmtime are not reentrant. */
201 %pure-parser
202 %parse-param { parser_control *pc }
203 %lex-param { parser_control *pc }
204
205 /* This grammar has 20 shift/reduce conflicts. */
206 %expect 20
207
208 %union
209 {
210 long int intval;
211 textint textintval;
212 struct timespec timespec;
213 }
214
215 %token tAGO tDST
216
217 %token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
218 %token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tORDINAL
219 %token <intval> tSEC_UNIT tYEAR_UNIT tZONE
220
221 %token <textintval> tSNUMBER tUNUMBER
222 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
223
224 %type <intval> o_colon_minutes o_merid
225 %type <timespec> seconds signed_seconds unsigned_seconds
226
227 %%
228
229 spec:
230 timespec
231 | items
232 ;
233
234 timespec:
235 '@' seconds
236 {
237 pc->seconds = $2;
238 pc->timespec_seen = true;
239 }
240 ;
241
242 items:
243 /* empty */
244 | items item
245 ;
246
247 item:
248 time
249 { pc->times_seen++; }
250 | local_zone
251 { pc->local_zones_seen++; }
252 | zone
253 { pc->zones_seen++; }
254 | date
255 { pc->dates_seen++; }
256 | day
257 { pc->days_seen++; }
258 | rel
259 { pc->rels_seen = true; }
260 | cvsstamp
261 {
262 pc->dates_seen++;
263 pc->zones_seen++;
264 pc->times_seen++;
265 }
266 | number
267 ;
268
269 cvsstamp: tUDECIMAL_NUMBER '.' tUDECIMAL_NUMBER '.' tUDECIMAL_NUMBER
270 {
271 int i;
272 pc->year.negative = 0;
273 pc->year.value = $1.tv_sec;
274
275 if (pc->year.value < 70)
276 pc->year.value += 2000;
277 else if (pc->year.value < 100)
278 pc->year.value += 1900;
279
280 for (i = pc->year.value, pc->year.digits = 0; i; i /= 10, pc->year.digits++)
281 continue;
282 if (pc->year.digits == 0)
283 pc->year.digits++;
284
285 pc->month = $1.tv_nsec / 10000000;
286 pc->day = $3.tv_sec;
287 pc->hour = $3.tv_nsec / 10000000;
288 pc->minutes = $5.tv_sec;
289 pc->seconds.tv_sec = $5.tv_nsec / 10000000;
290 pc->seconds.tv_nsec = 0;
291 pc->meridian = MER24;
292 pc->time_zone = 0;
293 }
294
295 time:
296 tUNUMBER tMERIDIAN
297 {
298 pc->hour = $1.value;
299 pc->minutes = 0;
300 pc->seconds.tv_sec = 0;
301 pc->seconds.tv_nsec = 0;
302 pc->meridian = $2;
303 }
304 | tUNUMBER ':' tUNUMBER o_merid
305 {
306 pc->hour = $1.value;
307 pc->minutes = $3.value;
308 pc->seconds.tv_sec = 0;
309 pc->seconds.tv_nsec = 0;
310 pc->meridian = $4;
311 }
312 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
313 {
314 pc->hour = $1.value;
315 pc->minutes = $3.value;
316 pc->seconds.tv_sec = 0;
317 pc->seconds.tv_nsec = 0;
318 pc->meridian = MER24;
319 pc->zones_seen++;
320 pc->time_zone = time_zone_hhmm ($4, $5);
321 }
322 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
323 {
324 pc->hour = $1.value;
325 pc->minutes = $3.value;
326 pc->seconds = $5;
327 pc->meridian = $6;
328 }
329 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
330 {
331 pc->hour = $1.value;
332 pc->minutes = $3.value;
333 pc->seconds = $5;
334 pc->meridian = MER24;
335 pc->zones_seen++;
336 pc->time_zone = time_zone_hhmm ($6, $7);
337 }
338 ;
339
340 local_zone:
341 tLOCAL_ZONE
342 {
343 pc->local_isdst = $1;
344 pc->dsts_seen += (0 < $1);
345 }
346 | tLOCAL_ZONE tDST
347 {
348 pc->local_isdst = 1;
349 pc->dsts_seen += (0 < $1) + 1;
350 }
351 ;
352
353 zone:
354 tZONE
355 { pc->time_zone = $1; }
356 | tZONE relunit_snumber
357 { pc->time_zone = $1; pc->rels_seen = true; }
358 | tZONE tSNUMBER o_colon_minutes
359 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
360 | tDAYZONE
361 { pc->time_zone = $1 + 60; }
362 | tZONE tDST
363 { pc->time_zone = $1 + 60; }
364 ;
365
366 day:
367 tDAY
368 {
369 pc->day_ordinal = 1;
370 pc->day_number = $1;
371 }
372 | tDAY ','
373 {
374 pc->day_ordinal = 1;
375 pc->day_number = $1;
376 }
377 | tORDINAL tDAY
378 {
379 pc->day_ordinal = $1;
380 pc->day_number = $2;
381 }
382 | tUNUMBER tDAY
383 {
384 pc->day_ordinal = $1.value;
385 pc->day_number = $2;
386 }
387 ;
388
389 date:
390 tUNUMBER '/' tUNUMBER
391 {
392 pc->month = $1.value;
393 pc->day = $3.value;
394 }
395 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
396 {
397 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
398 otherwise as MM/DD/YY.
399 The goal in recognizing YYYY/MM/DD is solely to support legacy
400 machine-generated dates like those in an RCS log listing. If
401 you want portability, use the ISO 8601 format. */
402 if (4 <= $1.digits)
403 {
404 pc->year = $1;
405 pc->month = $3.value;
406 pc->day = $5.value;
407 }
408 else
409 {
410 pc->month = $1.value;
411 pc->day = $3.value;
412 pc->year = $5;
413 }
414 }
415 | tUNUMBER tSNUMBER tSNUMBER
416 {
417 /* ISO 8601 format. YYYY-MM-DD. */
418 pc->year = $1;
419 pc->month = -$2.value;
420 pc->day = -$3.value;
421 }
422 | tUNUMBER tMONTH tSNUMBER
423 {
424 /* e.g. 17-JUN-1992. */
425 pc->day = $1.value;
426 pc->month = $2;
427 pc->year.value = -$3.value;
428 pc->year.digits = $3.digits;
429 }
430 | tMONTH tSNUMBER tSNUMBER
431 {
432 /* e.g. JUN-17-1992. */
433 pc->month = $1;
434 pc->day = -$2.value;
435 pc->year.value = -$3.value;
436 pc->year.digits = $3.digits;
437 }
438 | tMONTH tUNUMBER
439 {
440 pc->month = $1;
441 pc->day = $2.value;
442 }
443 | tMONTH tUNUMBER ',' tUNUMBER
444 {
445 pc->month = $1;
446 pc->day = $2.value;
447 pc->year = $4;
448 }
449 | tUNUMBER tMONTH
450 {
451 pc->day = $1.value;
452 pc->month = $2;
453 }
454 | tUNUMBER tMONTH tUNUMBER
455 {
456 pc->day = $1.value;
457 pc->month = $2;
458 pc->year = $3;
459 }
460 ;
461
462 rel:
463 relunit tAGO
464 {
465 pc->rel_ns = -pc->rel_ns;
466 pc->rel_seconds = -pc->rel_seconds;
467 pc->rel_minutes = -pc->rel_minutes;
468 pc->rel_hour = -pc->rel_hour;
469 pc->rel_day = -pc->rel_day;
470 pc->rel_month = -pc->rel_month;
471 pc->rel_year = -pc->rel_year;
472 }
473 | relunit
474 ;
475
476 relunit:
477 tORDINAL tYEAR_UNIT
478 { pc->rel_year += $1 * $2; }
479 | tUNUMBER tYEAR_UNIT
480 { pc->rel_year += $1.value * $2; }
481 | tYEAR_UNIT
482 { pc->rel_year += $1; }
483 | tORDINAL tMONTH_UNIT
484 { pc->rel_month += $1 * $2; }
485 | tUNUMBER tMONTH_UNIT
486 { pc->rel_month += $1.value * $2; }
487 | tMONTH_UNIT
488 { pc->rel_month += $1; }
489 | tORDINAL tDAY_UNIT
490 { pc->rel_day += $1 * $2; }
491 | tUNUMBER tDAY_UNIT
492 { pc->rel_day += $1.value * $2; }
493 | tDAY_UNIT
494 { pc->rel_day += $1; }
495 | tORDINAL tHOUR_UNIT
496 { pc->rel_hour += $1 * $2; }
497 | tUNUMBER tHOUR_UNIT
498 { pc->rel_hour += $1.value * $2; }
499 | tHOUR_UNIT
500 { pc->rel_hour += $1; }
501 | tORDINAL tMINUTE_UNIT
502 { pc->rel_minutes += $1 * $2; }
503 | tUNUMBER tMINUTE_UNIT
504 { pc->rel_minutes += $1.value * $2; }
505 | tMINUTE_UNIT
506 { pc->rel_minutes += $1; }
507 | tORDINAL tSEC_UNIT
508 { pc->rel_seconds += $1 * $2; }
509 | tUNUMBER tSEC_UNIT
510 { pc->rel_seconds += $1.value * $2; }
511 | tSDECIMAL_NUMBER tSEC_UNIT
512 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
513 | tUDECIMAL_NUMBER tSEC_UNIT
514 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
515 | tSEC_UNIT
516 { pc->rel_seconds += $1; }
517 | relunit_snumber
518 ;
519
520 relunit_snumber:
521 tSNUMBER tYEAR_UNIT
522 { pc->rel_year += $1.value * $2; }
523 | tSNUMBER tMONTH_UNIT
524 { pc->rel_month += $1.value * $2; }
525 | tSNUMBER tDAY_UNIT
526 { pc->rel_day += $1.value * $2; }
527 | tSNUMBER tHOUR_UNIT
528 { pc->rel_hour += $1.value * $2; }
529 | tSNUMBER tMINUTE_UNIT
530 { pc->rel_minutes += $1.value * $2; }
531 | tSNUMBER tSEC_UNIT
532 { pc->rel_seconds += $1.value * $2; }
533 ;
534
535 seconds: signed_seconds | unsigned_seconds;
536
537 signed_seconds:
538 tSDECIMAL_NUMBER
539 | tSNUMBER
540 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
541 ;
542
543 unsigned_seconds:
544 tUDECIMAL_NUMBER
545 | tUNUMBER
546 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
547 ;
548
549 number:
550 tUNUMBER
551 {
552 if (pc->dates_seen && ! pc->year.digits
553 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
554 pc->year = $1;
555 else
556 {
557 if (4 < $1.digits)
558 {
559 pc->dates_seen++;
560 pc->day = $1.value % 100;
561 pc->month = ($1.value / 100) % 100;
562 pc->year.value = $1.value / 10000;
563 pc->year.digits = $1.digits - 4;
564 }
565 else
566 {
567 pc->times_seen++;
568 if ($1.digits <= 2)
569 {
570 pc->hour = $1.value;
571 pc->minutes = 0;
572 }
573 else
574 {
575 pc->hour = $1.value / 100;
576 pc->minutes = $1.value % 100;
577 }
578 pc->seconds.tv_sec = 0;
579 pc->seconds.tv_nsec = 0;
580 pc->meridian = MER24;
581 }
582 }
583 }
584 ;
585
586 o_colon_minutes:
587 /* empty */
588 { $$ = -1; }
589 | ':' tUNUMBER
590 { $$ = $2.value; }
591 ;
592
593 o_merid:
594 /* empty */
595 { $$ = MER24; }
596 | tMERIDIAN
597 { $$ = $1; }
598 ;
599
600 %%
601
602 static table const meridian_table[] =
603 {
604 { "AM", tMERIDIAN, MERam },
605 { "A.M.", tMERIDIAN, MERam },
606 { "PM", tMERIDIAN, MERpm },
607 { "P.M.", tMERIDIAN, MERpm },
608 { NULL, 0, 0 }
609 };
610
611 static table const dst_table[] =
612 {
613 { "DST", tDST, 0 }
614 };
615
616 static table const month_and_day_table[] =
617 {
618 { "JANUARY", tMONTH, 1 },
619 { "FEBRUARY", tMONTH, 2 },
620 { "MARCH", tMONTH, 3 },
621 { "APRIL", tMONTH, 4 },
622 { "MAY", tMONTH, 5 },
623 { "JUNE", tMONTH, 6 },
624 { "JULY", tMONTH, 7 },
625 { "AUGUST", tMONTH, 8 },
626 { "SEPTEMBER",tMONTH, 9 },
627 { "SEPT", tMONTH, 9 },
628 { "OCTOBER", tMONTH, 10 },
629 { "NOVEMBER", tMONTH, 11 },
630 { "DECEMBER", tMONTH, 12 },
631 { "SUNDAY", tDAY, 0 },
632 { "MONDAY", tDAY, 1 },
633 { "TUESDAY", tDAY, 2 },
634 { "TUES", tDAY, 2 },
635 { "WEDNESDAY",tDAY, 3 },
636 { "WEDNES", tDAY, 3 },
637 { "THURSDAY", tDAY, 4 },
638 { "THUR", tDAY, 4 },
639 { "THURS", tDAY, 4 },
640 { "FRIDAY", tDAY, 5 },
641 { "SATURDAY", tDAY, 6 },
642 { NULL, 0, 0 }
643 };
644
645 static table const time_units_table[] =
646 {
647 { "YEAR", tYEAR_UNIT, 1 },
648 { "MONTH", tMONTH_UNIT, 1 },
649 { "FORTNIGHT",tDAY_UNIT, 14 },
650 { "WEEK", tDAY_UNIT, 7 },
651 { "DAY", tDAY_UNIT, 1 },
652 { "HOUR", tHOUR_UNIT, 1 },
653 { "MINUTE", tMINUTE_UNIT, 1 },
654 { "MIN", tMINUTE_UNIT, 1 },
655 { "SECOND", tSEC_UNIT, 1 },
656 { "SEC", tSEC_UNIT, 1 },
657 { NULL, 0, 0 }
658 };
659
660 /* Assorted relative-time words. */
661 static table const relative_time_table[] =
662 {
663 { "TOMORROW", tDAY_UNIT, 1 },
664 { "YESTERDAY",tDAY_UNIT, -1 },
665 { "TODAY", tDAY_UNIT, 0 },
666 { "NOW", tDAY_UNIT, 0 },
667 { "LAST", tORDINAL, -1 },
668 { "THIS", tORDINAL, 0 },
669 { "NEXT", tORDINAL, 1 },
670 { "FIRST", tORDINAL, 1 },
671 /*{ "SECOND", tORDINAL, 2 }, */
672 { "THIRD", tORDINAL, 3 },
673 { "FOURTH", tORDINAL, 4 },
674 { "FIFTH", tORDINAL, 5 },
675 { "SIXTH", tORDINAL, 6 },
676 { "SEVENTH", tORDINAL, 7 },
677 { "EIGHTH", tORDINAL, 8 },
678 { "NINTH", tORDINAL, 9 },
679 { "TENTH", tORDINAL, 10 },
680 { "ELEVENTH", tORDINAL, 11 },
681 { "TWELFTH", tORDINAL, 12 },
682 { "AGO", tAGO, 1 },
683 { NULL, 0, 0 }
684 };
685
686 /* The universal time zone table. These labels can be used even for
687 time stamps that would not otherwise be valid, e.g., GMT time
688 stamps in London during summer. */
689 static table const universal_time_zone_table[] =
690 {
691 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
692 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
693 { "UTC", tZONE, HOUR ( 0) },
694 { NULL, 0, 0 }
695 };
696
697 /* The time zone table. This table is necessarily incomplete, as time
698 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
699 as Eastern time in Australia, not as US Eastern Standard Time.
700 You cannot rely on getdate to handle arbitrary time zone
701 abbreviations; use numeric abbreviations like `-0500' instead. */
702 static table const time_zone_table[] =
703 {
704 { "WET", tZONE, HOUR ( 0) }, /* Western European */
705 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
706 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
707 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
708 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
709 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
710 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
711 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
712 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
713 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
714 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
715 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
716 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
717 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
718 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
719 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
720 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
721 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
722 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
723 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
724 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
725 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
726 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
727 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
728 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
729 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
730 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
731 { "CET", tZONE, HOUR ( 1) }, /* Central European */
732 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
733 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
734 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
735 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
736 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
737 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
738 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
739 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
740 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
741 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
742 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
743 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
744 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
745 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
746 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
747 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
748 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
749 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
750 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
751 { NULL, 0, 0 }
752 };
753
754 /* Military time zone table. */
755 static table const military_table[] =
756 {
757 { "A", tZONE, -HOUR ( 1) },
758 { "B", tZONE, -HOUR ( 2) },
759 { "C", tZONE, -HOUR ( 3) },
760 { "D", tZONE, -HOUR ( 4) },
761 { "E", tZONE, -HOUR ( 5) },
762 { "F", tZONE, -HOUR ( 6) },
763 { "G", tZONE, -HOUR ( 7) },
764 { "H", tZONE, -HOUR ( 8) },
765 { "I", tZONE, -HOUR ( 9) },
766 { "K", tZONE, -HOUR (10) },
767 { "L", tZONE, -HOUR (11) },
768 { "M", tZONE, -HOUR (12) },
769 { "N", tZONE, HOUR ( 1) },
770 { "O", tZONE, HOUR ( 2) },
771 { "P", tZONE, HOUR ( 3) },
772 { "Q", tZONE, HOUR ( 4) },
773 { "R", tZONE, HOUR ( 5) },
774 { "S", tZONE, HOUR ( 6) },
775 { "T", tZONE, HOUR ( 7) },
776 { "U", tZONE, HOUR ( 8) },
777 { "V", tZONE, HOUR ( 9) },
778 { "W", tZONE, HOUR (10) },
779 { "X", tZONE, HOUR (11) },
780 { "Y", tZONE, HOUR (12) },
781 { "Z", tZONE, HOUR ( 0) },
782 { NULL, 0, 0 }
783 };
784
785
786
787 /* Convert a time zone expressed as HH:MM into an integer count of
788 minutes. If MM is negative, then S is of the form HHMM and needs
789 to be picked apart; otherwise, S is of the form HH. */
790
791 static long int
time_zone_hhmm(textint s,long int mm)792 time_zone_hhmm (textint s, long int mm)
793 {
794 if (mm < 0)
795 return (s.value / 100) * 60 + s.value % 100;
796 else
797 return s.value * 60 + (s.negative ? -mm : mm);
798 }
799
800 static int
to_hour(long int hours,int meridian)801 to_hour (long int hours, int meridian)
802 {
803 switch (meridian)
804 {
805 default: /* Pacify GCC. */
806 case MER24:
807 return 0 <= hours && hours < 24 ? hours : -1;
808 case MERam:
809 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
810 case MERpm:
811 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
812 }
813 }
814
815 static long int
to_year(textint textyear)816 to_year (textint textyear)
817 {
818 long int year = textyear.value;
819
820 if (year < 0)
821 year = -year;
822
823 /* XPG4 suggests that years 00-68 map to 2000-2068, and
824 years 69-99 map to 1969-1999. */
825 else if (textyear.digits == 2)
826 year += year < 69 ? 2000 : 1900;
827
828 return year;
829 }
830
831 static table const *
lookup_zone(parser_control const * pc,char const * name)832 lookup_zone (parser_control const *pc, char const *name)
833 {
834 table const *tp;
835
836 for (tp = universal_time_zone_table; tp->name; tp++)
837 if (strcmp (name, tp->name) == 0)
838 return tp;
839
840 /* Try local zone abbreviations before those in time_zone_table, as
841 the local ones are more likely to be right. */
842 for (tp = pc->local_time_zone_table; tp->name; tp++)
843 if (strcmp (name, tp->name) == 0)
844 return tp;
845
846 for (tp = time_zone_table; tp->name; tp++)
847 if (strcmp (name, tp->name) == 0)
848 return tp;
849
850 return NULL;
851 }
852
853 #if ! HAVE_TM_GMTOFF
854 /* Yield the difference between *A and *B,
855 measured in seconds, ignoring leap seconds.
856 The body of this function is taken directly from the GNU C Library;
857 see src/strftime.c. */
858 static long int
tm_diff(struct tm const * a,struct tm const * b)859 tm_diff (struct tm const *a, struct tm const *b)
860 {
861 /* Compute intervening leap days correctly even if year is negative.
862 Take care to avoid int overflow in leap day calculations. */
863 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
864 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
865 int a100 = a4 / 25 - (a4 % 25 < 0);
866 int b100 = b4 / 25 - (b4 % 25 < 0);
867 int a400 = SHR (a100, 2);
868 int b400 = SHR (b100, 2);
869 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
870 long int ayear = a->tm_year;
871 long int years = ayear - b->tm_year;
872 long int days = (365 * years + intervening_leap_days
873 + (a->tm_yday - b->tm_yday));
874 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
875 + (a->tm_min - b->tm_min))
876 + (a->tm_sec - b->tm_sec));
877 }
878 #endif /* ! HAVE_TM_GMTOFF */
879
880 static table const *
lookup_word(parser_control const * pc,char * word)881 lookup_word (parser_control const *pc, char *word)
882 {
883 char *p;
884 char *q;
885 size_t wordlen;
886 table const *tp;
887 bool period_found;
888 bool abbrev;
889
890 /* Make it uppercase. */
891 for (p = word; *p; p++)
892 {
893 unsigned char ch = *p;
894 if (ISLOWER (ch))
895 *p = toupper (ch);
896 }
897
898 for (tp = meridian_table; tp->name; tp++)
899 if (strcmp (word, tp->name) == 0)
900 return tp;
901
902 /* See if we have an abbreviation for a month. */
903 wordlen = strlen (word);
904 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
905
906 for (tp = month_and_day_table; tp->name; tp++)
907 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
908 return tp;
909
910 if ((tp = lookup_zone (pc, word)))
911 return tp;
912
913 if (strcmp (word, dst_table[0].name) == 0)
914 return dst_table;
915
916 for (tp = time_units_table; tp->name; tp++)
917 if (strcmp (word, tp->name) == 0)
918 return tp;
919
920 /* Strip off any plural and try the units table again. */
921 if (word[wordlen - 1] == 'S')
922 {
923 word[wordlen - 1] = '\0';
924 for (tp = time_units_table; tp->name; tp++)
925 if (strcmp (word, tp->name) == 0)
926 return tp;
927 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
928 }
929
930 for (tp = relative_time_table; tp->name; tp++)
931 if (strcmp (word, tp->name) == 0)
932 return tp;
933
934 /* Military time zones. */
935 if (wordlen == 1)
936 for (tp = military_table; tp->name; tp++)
937 if (word[0] == tp->name[0])
938 return tp;
939
940 /* Drop out any periods and try the time zone table again. */
941 for (period_found = false, p = q = word; (*p = *q); q++)
942 if (*q == '.')
943 period_found = true;
944 else
945 p++;
946 if (period_found && (tp = lookup_zone (pc, word)))
947 return tp;
948
949 return NULL;
950 }
951
952 static int
yylex(YYSTYPE * lvalp,parser_control * pc)953 yylex (YYSTYPE *lvalp, parser_control *pc)
954 {
955 unsigned char c;
956 size_t count;
957
958 for (;;)
959 {
960 while (c = *pc->input, ISSPACE (c))
961 pc->input++;
962
963 if (ISDIGIT (c) || c == '-' || c == '+')
964 {
965 char const *p;
966 int sign;
967 unsigned long int value;
968 if (c == '-' || c == '+')
969 {
970 sign = c == '-' ? -1 : 1;
971 while (c = *++pc->input, ISSPACE (c))
972 continue;
973 if (! ISDIGIT (c))
974 /* skip the '-' sign */
975 continue;
976 }
977 else
978 sign = 0;
979 p = pc->input;
980 for (value = 0; ; value *= 10)
981 {
982 unsigned long int value1 = value + (c - '0');
983 if (value1 < value)
984 return '?';
985 value = value1;
986 c = *++p;
987 if (! ISDIGIT (c))
988 break;
989 if (ULONG_MAX / 10 < value)
990 return '?';
991 }
992 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
993 {
994 time_t s;
995 int ns;
996 int digits;
997 unsigned long int value1;
998
999 /* Check for overflow when converting value to time_t. */
1000 if (sign < 0)
1001 {
1002 s = - value;
1003 if (0 < s)
1004 return '?';
1005 value1 = -s;
1006 }
1007 else
1008 {
1009 s = value;
1010 if (s < 0)
1011 return '?';
1012 value1 = s;
1013 }
1014 if (value != value1)
1015 return '?';
1016
1017 /* Accumulate fraction, to ns precision. */
1018 p++;
1019 ns = *p++ - '0';
1020 for (digits = 2; digits <= LOG10_BILLION; digits++)
1021 {
1022 ns *= 10;
1023 if (ISDIGIT (*p))
1024 ns += *p++ - '0';
1025 }
1026
1027 /* Skip excess digits, truncating toward -Infinity. */
1028 if (sign < 0)
1029 for (; ISDIGIT (*p); p++)
1030 if (*p != '0')
1031 {
1032 ns++;
1033 break;
1034 }
1035 while (ISDIGIT (*p))
1036 p++;
1037
1038 /* Adjust to the timespec convention, which is that
1039 tv_nsec is always a positive offset even if tv_sec is
1040 negative. */
1041 if (sign < 0 && ns)
1042 {
1043 s--;
1044 if (! (s < 0))
1045 return '?';
1046 ns = BILLION - ns;
1047 }
1048
1049 lvalp->timespec.tv_sec = s;
1050 lvalp->timespec.tv_nsec = ns;
1051 pc->input = p;
1052 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1053 }
1054 else
1055 {
1056 lvalp->textintval.negative = sign < 0;
1057 if (sign < 0)
1058 {
1059 lvalp->textintval.value = - value;
1060 if (0 < lvalp->textintval.value)
1061 return '?';
1062 }
1063 else
1064 {
1065 lvalp->textintval.value = value;
1066 if (lvalp->textintval.value < 0)
1067 return '?';
1068 }
1069 lvalp->textintval.digits = p - pc->input;
1070 pc->input = p;
1071 return sign ? tSNUMBER : tUNUMBER;
1072 }
1073 }
1074
1075 if (ISALPHA (c))
1076 {
1077 char buff[20];
1078 char *p = buff;
1079 table const *tp;
1080
1081 do
1082 {
1083 if (p < buff + sizeof buff - 1)
1084 *p++ = c;
1085 c = *++pc->input;
1086 }
1087 while (ISALPHA (c) || c == '.');
1088
1089 *p = '\0';
1090 tp = lookup_word (pc, buff);
1091 if (! tp)
1092 return '?';
1093 lvalp->intval = tp->value;
1094 return tp->type;
1095 }
1096
1097 if (c != '(')
1098 return *pc->input++;
1099 count = 0;
1100 do
1101 {
1102 c = *pc->input++;
1103 if (c == '\0')
1104 return c;
1105 if (c == '(')
1106 count++;
1107 else if (c == ')')
1108 count--;
1109 }
1110 while (count != 0);
1111 }
1112 }
1113
1114 /* Do nothing if the parser reports an error. */
1115 static int
yyerror(parser_control * pc ATTRIBUTE_UNUSED,char * s ATTRIBUTE_UNUSED)1116 yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED)
1117 {
1118 return 0;
1119 }
1120
1121 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1122 passing it to mktime, return true if it's OK that mktime returned T.
1123 It's not OK if *TM0 has out-of-range members. */
1124
1125 static bool
mktime_ok(struct tm const * tm0,struct tm const * tm1,time_t t)1126 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1127 {
1128 if (t == (time_t) -1)
1129 {
1130 /* Guard against falsely reporting an error when parsing a time
1131 stamp that happens to equal (time_t) -1, on a host that
1132 supports such a time stamp. */
1133 tm1 = localtime (&t);
1134 if (!tm1)
1135 return false;
1136 }
1137
1138 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1139 | (tm0->tm_min ^ tm1->tm_min)
1140 | (tm0->tm_hour ^ tm1->tm_hour)
1141 | (tm0->tm_mday ^ tm1->tm_mday)
1142 | (tm0->tm_mon ^ tm1->tm_mon)
1143 | (tm0->tm_year ^ tm1->tm_year));
1144 }
1145
1146 /* A reasonable upper bound for the size of ordinary TZ strings.
1147 Use heap allocation if TZ's length exceeds this. */
1148 enum { TZBUFSIZE = 100 };
1149
1150 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1151 otherwise. */
1152 static char *
get_tz(char tzbuf[TZBUFSIZE])1153 get_tz (char tzbuf[TZBUFSIZE])
1154 {
1155 char *tz = getenv ("TZ");
1156 if (tz)
1157 {
1158 size_t tzsize = strlen (tz) + 1;
1159 tz = (tzsize <= TZBUFSIZE
1160 ? memcpy (tzbuf, tz, tzsize)
1161 : xmemdup (tz, tzsize));
1162 }
1163 return tz;
1164 }
1165
1166 /* Parse a date/time string, storing the resulting time value into *RESULT.
1167 The string itself is pointed to by P. Return true if successful.
1168 P can be an incomplete or relative time specification; if so, use
1169 *NOW as the basis for the returned time. */
1170 bool
get_date(struct timespec * result,char const * p,struct timespec const * now)1171 get_date (struct timespec *result, char const *p, struct timespec const *now)
1172 {
1173 time_t Start;
1174 long int Start_ns;
1175 struct tm const *tmp;
1176 struct tm tm;
1177 struct tm tm0;
1178 parser_control pc;
1179 struct timespec gettime_buffer;
1180 unsigned char c;
1181 bool tz_was_altered = false;
1182 char *tz0 = NULL;
1183 char tz0buf[TZBUFSIZE];
1184 bool ok = true;
1185
1186 if (! now)
1187 {
1188 gettime (&gettime_buffer);
1189 now = &gettime_buffer;
1190 }
1191
1192 Start = now->tv_sec;
1193 Start_ns = now->tv_nsec;
1194
1195 tmp = localtime (&now->tv_sec);
1196 if (! tmp)
1197 return false;
1198
1199 while (c = *p, ISSPACE (c))
1200 p++;
1201
1202 if (strncmp (p, "TZ=\"", 4) == 0)
1203 {
1204 char const *tzbase = p + 4;
1205 size_t tzsize = 1;
1206 char const *s;
1207
1208 for (s = tzbase; *s; s++, tzsize++)
1209 if (*s == '\\')
1210 {
1211 s++;
1212 if (! (*s == '\\' || *s == '"'))
1213 break;
1214 }
1215 else if (*s == '"')
1216 {
1217 char *z;
1218 char *tz1;
1219 char tz1buf[TZBUFSIZE];
1220 bool large_tz = TZBUFSIZE < tzsize;
1221 bool setenv_ok;
1222 tz0 = get_tz (tz0buf);
1223 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1224 for (s = tzbase; *s != '"'; s++)
1225 *z++ = *(s += *s == '\\');
1226 *z = '\0';
1227 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1228 if (large_tz)
1229 free (tz1);
1230 if (!setenv_ok)
1231 goto fail;
1232 tz_was_altered = true;
1233 p = s + 1;
1234 }
1235 }
1236
1237 pc.input = p;
1238 pc.year.value = tmp->tm_year;
1239 pc.year.value += TM_YEAR_BASE;
1240 pc.year.digits = 0;
1241 pc.month = tmp->tm_mon + 1;
1242 pc.day = tmp->tm_mday;
1243 pc.hour = tmp->tm_hour;
1244 pc.minutes = tmp->tm_min;
1245 pc.seconds.tv_sec = tmp->tm_sec;
1246 pc.seconds.tv_nsec = Start_ns;
1247 tm.tm_isdst = tmp->tm_isdst;
1248
1249 pc.meridian = MER24;
1250 pc.rel_ns = 0;
1251 pc.rel_seconds = 0;
1252 pc.rel_minutes = 0;
1253 pc.rel_hour = 0;
1254 pc.rel_day = 0;
1255 pc.rel_month = 0;
1256 pc.rel_year = 0;
1257 pc.timespec_seen = false;
1258 pc.rels_seen = false;
1259 pc.dates_seen = 0;
1260 pc.days_seen = 0;
1261 pc.times_seen = 0;
1262 pc.local_zones_seen = 0;
1263 pc.dsts_seen = 0;
1264 pc.zones_seen = 0;
1265
1266 #if HAVE_STRUCT_TM_TM_ZONE
1267 pc.local_time_zone_table[0].name = tmp->tm_zone;
1268 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1269 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1270 pc.local_time_zone_table[1].name = NULL;
1271
1272 /* Probe the names used in the next three calendar quarters, looking
1273 for a tm_isdst different from the one we already have. */
1274 {
1275 int quarter;
1276 for (quarter = 1; quarter <= 3; quarter++)
1277 {
1278 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1279 struct tm const *probe_tm = localtime (&probe);
1280 if (probe_tm && probe_tm->tm_zone
1281 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1282 {
1283 {
1284 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1285 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1286 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1287 pc.local_time_zone_table[2].name = NULL;
1288 }
1289 break;
1290 }
1291 }
1292 }
1293 #else
1294 #if HAVE_TZNAME
1295 {
1296 # ifndef tzname
1297 extern char *tzname[];
1298 # endif
1299 int i;
1300 for (i = 0; i < 2; i++)
1301 {
1302 pc.local_time_zone_table[i].name = tzname[i];
1303 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1304 pc.local_time_zone_table[i].value = i;
1305 }
1306 pc.local_time_zone_table[i].name = NULL;
1307 }
1308 #else
1309 pc.local_time_zone_table[0].name = NULL;
1310 #endif
1311 #endif
1312
1313 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1314 && ! strcmp (pc.local_time_zone_table[0].name,
1315 pc.local_time_zone_table[1].name))
1316 {
1317 /* This locale uses the same abbrevation for standard and
1318 daylight times. So if we see that abbreviation, we don't
1319 know whether it's daylight time. */
1320 pc.local_time_zone_table[0].value = -1;
1321 pc.local_time_zone_table[1].name = NULL;
1322 }
1323
1324 if (yyparse (&pc) != 0)
1325 goto fail;
1326
1327 if (pc.timespec_seen)
1328 *result = pc.seconds;
1329 else
1330 {
1331 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1332 | (pc.local_zones_seen + pc.zones_seen)))
1333 goto fail;
1334
1335 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1336 tm.tm_mon = pc.month - 1;
1337 tm.tm_mday = pc.day;
1338 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1339 {
1340 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1341 if (tm.tm_hour < 0)
1342 goto fail;
1343 tm.tm_min = pc.minutes;
1344 tm.tm_sec = pc.seconds.tv_sec;
1345 }
1346 else
1347 {
1348 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1349 pc.seconds.tv_nsec = 0;
1350 }
1351
1352 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1353 if (!pc.rels_seen)
1354 tm.tm_isdst = -1;
1355
1356 /* But if the input explicitly specifies local time with or without
1357 DST, give mktime that information. */
1358 if (pc.local_zones_seen)
1359 tm.tm_isdst = pc.local_isdst;
1360
1361 tm0 = tm;
1362
1363 Start = mktime (&tm);
1364
1365 if (! mktime_ok (&tm0, &tm, Start))
1366 {
1367 if (! pc.zones_seen)
1368 goto fail;
1369 else
1370 {
1371 /* Guard against falsely reporting errors near the time_t
1372 boundaries when parsing times in other time zones. For
1373 example, suppose the input string "1969-12-31 23:00:00 -0100",
1374 the current time zone is 8 hours ahead of UTC, and the min
1375 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1376 localtime value is 1970-01-01 08:00:00, and mktime will
1377 therefore fail on 1969-12-31 23:00:00. To work around the
1378 problem, set the time zone to 1 hour behind UTC temporarily
1379 by setting TZ="XXX1:00" and try mktime again. */
1380
1381 long int time_zone = pc.time_zone;
1382 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1383 long int abs_time_zone_hour = abs_time_zone / 60;
1384 int abs_time_zone_min = abs_time_zone % 60;
1385 char tz1buf[sizeof "XXX+0:00"
1386 + sizeof pc.time_zone * CHAR_BIT / 3];
1387 if (!tz_was_altered)
1388 tz0 = get_tz (tz0buf);
1389 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1390 abs_time_zone_hour, abs_time_zone_min);
1391 if (setenv ("TZ", tz1buf, 1) != 0)
1392 goto fail;
1393 tz_was_altered = true;
1394 tm = tm0;
1395 Start = mktime (&tm);
1396 if (! mktime_ok (&tm0, &tm, Start))
1397 goto fail;
1398 }
1399 }
1400
1401 if (pc.days_seen && ! pc.dates_seen)
1402 {
1403 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1404 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1405 tm.tm_isdst = -1;
1406 Start = mktime (&tm);
1407 if (Start == (time_t) -1)
1408 goto fail;
1409 }
1410
1411 if (pc.zones_seen)
1412 {
1413 long int delta = pc.time_zone * 60;
1414 time_t t1;
1415 #ifdef HAVE_TM_GMTOFF
1416 delta -= tm.tm_gmtoff;
1417 #else
1418 time_t t = Start;
1419 struct tm const *gmt = gmtime (&t);
1420 if (! gmt)
1421 goto fail;
1422 delta -= tm_diff (&tm, gmt);
1423 #endif
1424 t1 = Start - delta;
1425 if ((Start < t1) != (delta < 0))
1426 goto fail; /* time_t overflow */
1427 Start = t1;
1428 }
1429
1430 /* Add relative date. */
1431 if (pc.rel_year | pc.rel_month | pc.rel_day)
1432 {
1433 int year = tm.tm_year + pc.rel_year;
1434 int month = tm.tm_mon + pc.rel_month;
1435 int day = tm.tm_mday + pc.rel_day;
1436 if (((year < tm.tm_year) ^ (pc.rel_year < 0))
1437 | ((month < tm.tm_mon) ^ (pc.rel_month < 0))
1438 | ((day < tm.tm_mday) ^ (pc.rel_day < 0)))
1439 goto fail;
1440 tm.tm_year = year;
1441 tm.tm_mon = month;
1442 tm.tm_mday = day;
1443 Start = mktime (&tm);
1444 if (Start == (time_t) -1)
1445 goto fail;
1446 }
1447
1448 /* Add relative hours, minutes, and seconds. On hosts that support
1449 leap seconds, ignore the possibility of leap seconds; e.g.,
1450 "+ 10 minutes" adds 600 seconds, even if one of them is a
1451 leap second. Typically this is not what the user wants, but it's
1452 too hard to do it the other way, because the time zone indicator
1453 must be applied before relative times, and if mktime is applied
1454 again the time zone will be lost. */
1455 {
1456 long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1457 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1458 time_t t0 = Start;
1459 long int d1 = 60 * 60 * pc.rel_hour;
1460 time_t t1 = t0 + d1;
1461 long int d2 = 60 * pc.rel_minutes;
1462 time_t t2 = t1 + d2;
1463 long int d3 = pc.rel_seconds;
1464 time_t t3 = t2 + d3;
1465 long int d4 = (sum_ns - normalized_ns) / BILLION;
1466 time_t t4 = t3 + d4;
1467
1468 if ((d1 / (60 * 60) ^ pc.rel_hour)
1469 | (d2 / 60 ^ pc.rel_minutes)
1470 | ((t1 < t0) ^ (d1 < 0))
1471 | ((t2 < t1) ^ (d2 < 0))
1472 | ((t3 < t2) ^ (d3 < 0))
1473 | ((t4 < t3) ^ (d4 < 0)))
1474 goto fail;
1475
1476 result->tv_sec = t4;
1477 result->tv_nsec = normalized_ns;
1478 }
1479 }
1480
1481 goto done;
1482
1483 fail:
1484 ok = false;
1485 done:
1486 if (tz_was_altered)
1487 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1488 if (tz0 != tz0buf)
1489 free (tz0);
1490 return ok;
1491 }
1492
1493 #if TEST
1494
1495 int
main(int ac,char ** av)1496 main (int ac, char **av)
1497 {
1498 char buff[BUFSIZ];
1499
1500 printf ("Enter date, or blank line to exit.\n\t> ");
1501 fflush (stdout);
1502
1503 buff[BUFSIZ - 1] = '\0';
1504 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1505 {
1506 struct timespec d;
1507 struct tm const *tm;
1508 if (! get_date (&d, buff, NULL))
1509 printf ("Bad format - couldn't convert.\n");
1510 else if (! (tm = localtime (&d.tv_sec)))
1511 {
1512 long int sec = d.tv_sec;
1513 printf ("localtime (%ld) failed\n", sec);
1514 }
1515 else
1516 {
1517 int ns = d.tv_nsec;
1518 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1519 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1520 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1521 }
1522 printf ("\t> ");
1523 fflush (stdout);
1524 }
1525 return 0;
1526 }
1527 #endif /* TEST */
1528