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