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 | number
261 ;
262
263 time:
264 tUNUMBER tMERIDIAN
265 {
266 pc->hour = $1.value;
267 pc->minutes = 0;
268 pc->seconds.tv_sec = 0;
269 pc->seconds.tv_nsec = 0;
270 pc->meridian = $2;
271 }
272 | tUNUMBER ':' tUNUMBER o_merid
273 {
274 pc->hour = $1.value;
275 pc->minutes = $3.value;
276 pc->seconds.tv_sec = 0;
277 pc->seconds.tv_nsec = 0;
278 pc->meridian = $4;
279 }
280 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
281 {
282 pc->hour = $1.value;
283 pc->minutes = $3.value;
284 pc->seconds.tv_sec = 0;
285 pc->seconds.tv_nsec = 0;
286 pc->meridian = MER24;
287 pc->zones_seen++;
288 pc->time_zone = time_zone_hhmm ($4, $5);
289 }
290 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
291 {
292 pc->hour = $1.value;
293 pc->minutes = $3.value;
294 pc->seconds = $5;
295 pc->meridian = $6;
296 }
297 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
298 {
299 pc->hour = $1.value;
300 pc->minutes = $3.value;
301 pc->seconds = $5;
302 pc->meridian = MER24;
303 pc->zones_seen++;
304 pc->time_zone = time_zone_hhmm ($6, $7);
305 }
306 ;
307
308 local_zone:
309 tLOCAL_ZONE
310 {
311 pc->local_isdst = $1;
312 pc->dsts_seen += (0 < $1);
313 }
314 | tLOCAL_ZONE tDST
315 {
316 pc->local_isdst = 1;
317 pc->dsts_seen += (0 < $1) + 1;
318 }
319 ;
320
321 zone:
322 tZONE
323 { pc->time_zone = $1; }
324 | tZONE relunit_snumber
325 { pc->time_zone = $1; pc->rels_seen = true; }
326 | tZONE tSNUMBER o_colon_minutes
327 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
328 | tDAYZONE
329 { pc->time_zone = $1 + 60; }
330 | tZONE tDST
331 { pc->time_zone = $1 + 60; }
332 ;
333
334 day:
335 tDAY
336 {
337 pc->day_ordinal = 1;
338 pc->day_number = $1;
339 }
340 | tDAY ','
341 {
342 pc->day_ordinal = 1;
343 pc->day_number = $1;
344 }
345 | tORDINAL tDAY
346 {
347 pc->day_ordinal = $1;
348 pc->day_number = $2;
349 }
350 | tUNUMBER tDAY
351 {
352 pc->day_ordinal = $1.value;
353 pc->day_number = $2;
354 }
355 ;
356
357 date:
358 tUNUMBER '/' tUNUMBER
359 {
360 pc->month = $1.value;
361 pc->day = $3.value;
362 }
363 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
364 {
365 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
366 otherwise as MM/DD/YY.
367 The goal in recognizing YYYY/MM/DD is solely to support legacy
368 machine-generated dates like those in an RCS log listing. If
369 you want portability, use the ISO 8601 format. */
370 if (4 <= $1.digits)
371 {
372 pc->year = $1;
373 pc->month = $3.value;
374 pc->day = $5.value;
375 }
376 else
377 {
378 pc->month = $1.value;
379 pc->day = $3.value;
380 pc->year = $5;
381 }
382 }
383 | tUNUMBER tSNUMBER tSNUMBER
384 {
385 /* ISO 8601 format. YYYY-MM-DD. */
386 pc->year = $1;
387 pc->month = -$2.value;
388 pc->day = -$3.value;
389 }
390 | tUNUMBER tMONTH tSNUMBER
391 {
392 /* e.g. 17-JUN-1992. */
393 pc->day = $1.value;
394 pc->month = $2;
395 pc->year.value = -$3.value;
396 pc->year.digits = $3.digits;
397 }
398 | tMONTH tSNUMBER tSNUMBER
399 {
400 /* e.g. JUN-17-1992. */
401 pc->month = $1;
402 pc->day = -$2.value;
403 pc->year.value = -$3.value;
404 pc->year.digits = $3.digits;
405 }
406 | tMONTH tUNUMBER
407 {
408 pc->month = $1;
409 pc->day = $2.value;
410 }
411 | tMONTH tUNUMBER ',' tUNUMBER
412 {
413 pc->month = $1;
414 pc->day = $2.value;
415 pc->year = $4;
416 }
417 | tUNUMBER tMONTH
418 {
419 pc->day = $1.value;
420 pc->month = $2;
421 }
422 | tUNUMBER tMONTH tUNUMBER
423 {
424 pc->day = $1.value;
425 pc->month = $2;
426 pc->year = $3;
427 }
428 ;
429
430 rel:
431 relunit tAGO
432 {
433 pc->rel_ns = -pc->rel_ns;
434 pc->rel_seconds = -pc->rel_seconds;
435 pc->rel_minutes = -pc->rel_minutes;
436 pc->rel_hour = -pc->rel_hour;
437 pc->rel_day = -pc->rel_day;
438 pc->rel_month = -pc->rel_month;
439 pc->rel_year = -pc->rel_year;
440 }
441 | relunit
442 ;
443
444 relunit:
445 tORDINAL tYEAR_UNIT
446 { pc->rel_year += $1 * $2; }
447 | tUNUMBER tYEAR_UNIT
448 { pc->rel_year += $1.value * $2; }
449 | tYEAR_UNIT
450 { pc->rel_year += $1; }
451 | tORDINAL tMONTH_UNIT
452 { pc->rel_month += $1 * $2; }
453 | tUNUMBER tMONTH_UNIT
454 { pc->rel_month += $1.value * $2; }
455 | tMONTH_UNIT
456 { pc->rel_month += $1; }
457 | tORDINAL tDAY_UNIT
458 { pc->rel_day += $1 * $2; }
459 | tUNUMBER tDAY_UNIT
460 { pc->rel_day += $1.value * $2; }
461 | tDAY_UNIT
462 { pc->rel_day += $1; }
463 | tORDINAL tHOUR_UNIT
464 { pc->rel_hour += $1 * $2; }
465 | tUNUMBER tHOUR_UNIT
466 { pc->rel_hour += $1.value * $2; }
467 | tHOUR_UNIT
468 { pc->rel_hour += $1; }
469 | tORDINAL tMINUTE_UNIT
470 { pc->rel_minutes += $1 * $2; }
471 | tUNUMBER tMINUTE_UNIT
472 { pc->rel_minutes += $1.value * $2; }
473 | tMINUTE_UNIT
474 { pc->rel_minutes += $1; }
475 | tORDINAL tSEC_UNIT
476 { pc->rel_seconds += $1 * $2; }
477 | tUNUMBER tSEC_UNIT
478 { pc->rel_seconds += $1.value * $2; }
479 | tSDECIMAL_NUMBER tSEC_UNIT
480 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
481 | tUDECIMAL_NUMBER tSEC_UNIT
482 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
483 | tSEC_UNIT
484 { pc->rel_seconds += $1; }
485 | relunit_snumber
486 ;
487
488 relunit_snumber:
489 tSNUMBER tYEAR_UNIT
490 { pc->rel_year += $1.value * $2; }
491 | tSNUMBER tMONTH_UNIT
492 { pc->rel_month += $1.value * $2; }
493 | tSNUMBER tDAY_UNIT
494 { pc->rel_day += $1.value * $2; }
495 | tSNUMBER tHOUR_UNIT
496 { pc->rel_hour += $1.value * $2; }
497 | tSNUMBER tMINUTE_UNIT
498 { pc->rel_minutes += $1.value * $2; }
499 | tSNUMBER tSEC_UNIT
500 { pc->rel_seconds += $1.value * $2; }
501 ;
502
503 seconds: signed_seconds | unsigned_seconds;
504
505 signed_seconds:
506 tSDECIMAL_NUMBER
507 | tSNUMBER
508 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
509 ;
510
511 unsigned_seconds:
512 tUDECIMAL_NUMBER
513 | tUNUMBER
514 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
515 ;
516
517 number:
518 tUNUMBER
519 {
520 if (pc->dates_seen && ! pc->year.digits
521 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
522 pc->year = $1;
523 else
524 {
525 if (4 < $1.digits)
526 {
527 pc->dates_seen++;
528 pc->day = $1.value % 100;
529 pc->month = ($1.value / 100) % 100;
530 pc->year.value = $1.value / 10000;
531 pc->year.digits = $1.digits - 4;
532 }
533 else
534 {
535 pc->times_seen++;
536 if ($1.digits <= 2)
537 {
538 pc->hour = $1.value;
539 pc->minutes = 0;
540 }
541 else
542 {
543 pc->hour = $1.value / 100;
544 pc->minutes = $1.value % 100;
545 }
546 pc->seconds.tv_sec = 0;
547 pc->seconds.tv_nsec = 0;
548 pc->meridian = MER24;
549 }
550 }
551 }
552 ;
553
554 o_colon_minutes:
555 /* empty */
556 { $$ = -1; }
557 | ':' tUNUMBER
558 { $$ = $2.value; }
559 ;
560
561 o_merid:
562 /* empty */
563 { $$ = MER24; }
564 | tMERIDIAN
565 { $$ = $1; }
566 ;
567
568 %%
569
570 static table const meridian_table[] =
571 {
572 { "AM", tMERIDIAN, MERam },
573 { "A.M.", tMERIDIAN, MERam },
574 { "PM", tMERIDIAN, MERpm },
575 { "P.M.", tMERIDIAN, MERpm },
576 { NULL, 0, 0 }
577 };
578
579 static table const dst_table[] =
580 {
581 { "DST", tDST, 0 }
582 };
583
584 static table const month_and_day_table[] =
585 {
586 { "JANUARY", tMONTH, 1 },
587 { "FEBRUARY", tMONTH, 2 },
588 { "MARCH", tMONTH, 3 },
589 { "APRIL", tMONTH, 4 },
590 { "MAY", tMONTH, 5 },
591 { "JUNE", tMONTH, 6 },
592 { "JULY", tMONTH, 7 },
593 { "AUGUST", tMONTH, 8 },
594 { "SEPTEMBER",tMONTH, 9 },
595 { "SEPT", tMONTH, 9 },
596 { "OCTOBER", tMONTH, 10 },
597 { "NOVEMBER", tMONTH, 11 },
598 { "DECEMBER", tMONTH, 12 },
599 { "SUNDAY", tDAY, 0 },
600 { "MONDAY", tDAY, 1 },
601 { "TUESDAY", tDAY, 2 },
602 { "TUES", tDAY, 2 },
603 { "WEDNESDAY",tDAY, 3 },
604 { "WEDNES", tDAY, 3 },
605 { "THURSDAY", tDAY, 4 },
606 { "THUR", tDAY, 4 },
607 { "THURS", tDAY, 4 },
608 { "FRIDAY", tDAY, 5 },
609 { "SATURDAY", tDAY, 6 },
610 { NULL, 0, 0 }
611 };
612
613 static table const time_units_table[] =
614 {
615 { "YEAR", tYEAR_UNIT, 1 },
616 { "MONTH", tMONTH_UNIT, 1 },
617 { "FORTNIGHT",tDAY_UNIT, 14 },
618 { "WEEK", tDAY_UNIT, 7 },
619 { "DAY", tDAY_UNIT, 1 },
620 { "HOUR", tHOUR_UNIT, 1 },
621 { "MINUTE", tMINUTE_UNIT, 1 },
622 { "MIN", tMINUTE_UNIT, 1 },
623 { "SECOND", tSEC_UNIT, 1 },
624 { "SEC", tSEC_UNIT, 1 },
625 { NULL, 0, 0 }
626 };
627
628 /* Assorted relative-time words. */
629 static table const relative_time_table[] =
630 {
631 { "TOMORROW", tDAY_UNIT, 1 },
632 { "YESTERDAY",tDAY_UNIT, -1 },
633 { "TODAY", tDAY_UNIT, 0 },
634 { "NOW", tDAY_UNIT, 0 },
635 { "LAST", tORDINAL, -1 },
636 { "THIS", tORDINAL, 0 },
637 { "NEXT", tORDINAL, 1 },
638 { "FIRST", tORDINAL, 1 },
639 /*{ "SECOND", tORDINAL, 2 }, */
640 { "THIRD", tORDINAL, 3 },
641 { "FOURTH", tORDINAL, 4 },
642 { "FIFTH", tORDINAL, 5 },
643 { "SIXTH", tORDINAL, 6 },
644 { "SEVENTH", tORDINAL, 7 },
645 { "EIGHTH", tORDINAL, 8 },
646 { "NINTH", tORDINAL, 9 },
647 { "TENTH", tORDINAL, 10 },
648 { "ELEVENTH", tORDINAL, 11 },
649 { "TWELFTH", tORDINAL, 12 },
650 { "AGO", tAGO, 1 },
651 { NULL, 0, 0 }
652 };
653
654 /* The universal time zone table. These labels can be used even for
655 time stamps that would not otherwise be valid, e.g., GMT time
656 stamps in London during summer. */
657 static table const universal_time_zone_table[] =
658 {
659 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
660 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
661 { "UTC", tZONE, HOUR ( 0) },
662 { NULL, 0, 0 }
663 };
664
665 /* The time zone table. This table is necessarily incomplete, as time
666 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
667 as Eastern time in Australia, not as US Eastern Standard Time.
668 You cannot rely on getdate to handle arbitrary time zone
669 abbreviations; use numeric abbreviations like `-0500' instead. */
670 static table const time_zone_table[] =
671 {
672 { "WET", tZONE, HOUR ( 0) }, /* Western European */
673 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
674 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
675 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
676 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
677 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
678 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
679 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
680 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
681 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
682 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
683 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
684 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
685 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
686 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
687 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
688 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
689 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
690 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
691 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
692 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
693 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
694 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
695 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
696 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
697 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
698 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
699 { "CET", tZONE, HOUR ( 1) }, /* Central European */
700 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
701 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
702 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
703 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
704 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
705 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
706 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
707 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
708 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
709 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
710 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
711 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
712 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
713 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
714 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
715 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
716 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
717 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
718 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
719 { NULL, 0, 0 }
720 };
721
722 /* Military time zone table. */
723 static table const military_table[] =
724 {
725 { "A", tZONE, -HOUR ( 1) },
726 { "B", tZONE, -HOUR ( 2) },
727 { "C", tZONE, -HOUR ( 3) },
728 { "D", tZONE, -HOUR ( 4) },
729 { "E", tZONE, -HOUR ( 5) },
730 { "F", tZONE, -HOUR ( 6) },
731 { "G", tZONE, -HOUR ( 7) },
732 { "H", tZONE, -HOUR ( 8) },
733 { "I", tZONE, -HOUR ( 9) },
734 { "K", tZONE, -HOUR (10) },
735 { "L", tZONE, -HOUR (11) },
736 { "M", tZONE, -HOUR (12) },
737 { "N", tZONE, HOUR ( 1) },
738 { "O", tZONE, HOUR ( 2) },
739 { "P", tZONE, HOUR ( 3) },
740 { "Q", tZONE, HOUR ( 4) },
741 { "R", tZONE, HOUR ( 5) },
742 { "S", tZONE, HOUR ( 6) },
743 { "T", tZONE, HOUR ( 7) },
744 { "U", tZONE, HOUR ( 8) },
745 { "V", tZONE, HOUR ( 9) },
746 { "W", tZONE, HOUR (10) },
747 { "X", tZONE, HOUR (11) },
748 { "Y", tZONE, HOUR (12) },
749 { "Z", tZONE, HOUR ( 0) },
750 { NULL, 0, 0 }
751 };
752
753
754
755 /* Convert a time zone expressed as HH:MM into an integer count of
756 minutes. If MM is negative, then S is of the form HHMM and needs
757 to be picked apart; otherwise, S is of the form HH. */
758
759 static long int
time_zone_hhmm(textint s,long int mm)760 time_zone_hhmm (textint s, long int mm)
761 {
762 if (mm < 0)
763 return (s.value / 100) * 60 + s.value % 100;
764 else
765 return s.value * 60 + (s.negative ? -mm : mm);
766 }
767
768 static int
to_hour(long int hours,int meridian)769 to_hour (long int hours, int meridian)
770 {
771 switch (meridian)
772 {
773 default: /* Pacify GCC. */
774 case MER24:
775 return 0 <= hours && hours < 24 ? hours : -1;
776 case MERam:
777 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
778 case MERpm:
779 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
780 }
781 }
782
783 static long int
to_year(textint textyear)784 to_year (textint textyear)
785 {
786 long int year = textyear.value;
787
788 if (year < 0)
789 year = -year;
790
791 /* XPG4 suggests that years 00-68 map to 2000-2068, and
792 years 69-99 map to 1969-1999. */
793 else if (textyear.digits == 2)
794 year += year < 69 ? 2000 : 1900;
795
796 return year;
797 }
798
799 static table const *
lookup_zone(parser_control const * pc,char const * name)800 lookup_zone (parser_control const *pc, char const *name)
801 {
802 table const *tp;
803
804 for (tp = universal_time_zone_table; tp->name; tp++)
805 if (strcmp (name, tp->name) == 0)
806 return tp;
807
808 /* Try local zone abbreviations before those in time_zone_table, as
809 the local ones are more likely to be right. */
810 for (tp = pc->local_time_zone_table; tp->name; tp++)
811 if (strcmp (name, tp->name) == 0)
812 return tp;
813
814 for (tp = time_zone_table; tp->name; tp++)
815 if (strcmp (name, tp->name) == 0)
816 return tp;
817
818 return NULL;
819 }
820
821 #if ! HAVE_TM_GMTOFF
822 /* Yield the difference between *A and *B,
823 measured in seconds, ignoring leap seconds.
824 The body of this function is taken directly from the GNU C Library;
825 see src/strftime.c. */
826 static long int
tm_diff(struct tm const * a,struct tm const * b)827 tm_diff (struct tm const *a, struct tm const *b)
828 {
829 /* Compute intervening leap days correctly even if year is negative.
830 Take care to avoid int overflow in leap day calculations. */
831 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
832 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
833 int a100 = a4 / 25 - (a4 % 25 < 0);
834 int b100 = b4 / 25 - (b4 % 25 < 0);
835 int a400 = SHR (a100, 2);
836 int b400 = SHR (b100, 2);
837 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
838 long int ayear = a->tm_year;
839 long int years = ayear - b->tm_year;
840 long int days = (365 * years + intervening_leap_days
841 + (a->tm_yday - b->tm_yday));
842 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
843 + (a->tm_min - b->tm_min))
844 + (a->tm_sec - b->tm_sec));
845 }
846 #endif /* ! HAVE_TM_GMTOFF */
847
848 static table const *
lookup_word(parser_control const * pc,char * word)849 lookup_word (parser_control const *pc, char *word)
850 {
851 char *p;
852 char *q;
853 size_t wordlen;
854 table const *tp;
855 bool period_found;
856 bool abbrev;
857
858 /* Make it uppercase. */
859 for (p = word; *p; p++)
860 {
861 unsigned char ch = *p;
862 if (ISLOWER (ch))
863 *p = toupper (ch);
864 }
865
866 for (tp = meridian_table; tp->name; tp++)
867 if (strcmp (word, tp->name) == 0)
868 return tp;
869
870 /* See if we have an abbreviation for a month. */
871 wordlen = strlen (word);
872 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
873
874 for (tp = month_and_day_table; tp->name; tp++)
875 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
876 return tp;
877
878 if ((tp = lookup_zone (pc, word)))
879 return tp;
880
881 if (strcmp (word, dst_table[0].name) == 0)
882 return dst_table;
883
884 for (tp = time_units_table; tp->name; tp++)
885 if (strcmp (word, tp->name) == 0)
886 return tp;
887
888 /* Strip off any plural and try the units table again. */
889 if (word[wordlen - 1] == 'S')
890 {
891 word[wordlen - 1] = '\0';
892 for (tp = time_units_table; tp->name; tp++)
893 if (strcmp (word, tp->name) == 0)
894 return tp;
895 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
896 }
897
898 for (tp = relative_time_table; tp->name; tp++)
899 if (strcmp (word, tp->name) == 0)
900 return tp;
901
902 /* Military time zones. */
903 if (wordlen == 1)
904 for (tp = military_table; tp->name; tp++)
905 if (word[0] == tp->name[0])
906 return tp;
907
908 /* Drop out any periods and try the time zone table again. */
909 for (period_found = false, p = q = word; (*p = *q); q++)
910 if (*q == '.')
911 period_found = true;
912 else
913 p++;
914 if (period_found && (tp = lookup_zone (pc, word)))
915 return tp;
916
917 return NULL;
918 }
919
920 static int
yylex(YYSTYPE * lvalp,parser_control * pc)921 yylex (YYSTYPE *lvalp, parser_control *pc)
922 {
923 unsigned char c;
924 size_t count;
925
926 for (;;)
927 {
928 while (c = *pc->input, ISSPACE (c))
929 pc->input++;
930
931 if (ISDIGIT (c) || c == '-' || c == '+')
932 {
933 char const *p;
934 int sign;
935 unsigned long int value;
936 if (c == '-' || c == '+')
937 {
938 sign = c == '-' ? -1 : 1;
939 while (c = *++pc->input, ISSPACE (c))
940 continue;
941 if (! ISDIGIT (c))
942 /* skip the '-' sign */
943 continue;
944 }
945 else
946 sign = 0;
947 p = pc->input;
948 for (value = 0; ; value *= 10)
949 {
950 unsigned long int value1 = value + (c - '0');
951 if (value1 < value)
952 return '?';
953 value = value1;
954 c = *++p;
955 if (! ISDIGIT (c))
956 break;
957 if (ULONG_MAX / 10 < value)
958 return '?';
959 }
960 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
961 {
962 time_t s;
963 int ns;
964 int digits;
965 unsigned long int value1;
966
967 /* Check for overflow when converting value to time_t. */
968 if (sign < 0)
969 {
970 s = - value;
971 if (0 < s)
972 return '?';
973 value1 = -s;
974 }
975 else
976 {
977 s = value;
978 if (s < 0)
979 return '?';
980 value1 = s;
981 }
982 if (value != value1)
983 return '?';
984
985 /* Accumulate fraction, to ns precision. */
986 p++;
987 ns = *p++ - '0';
988 for (digits = 2; digits <= LOG10_BILLION; digits++)
989 {
990 ns *= 10;
991 if (ISDIGIT (*p))
992 ns += *p++ - '0';
993 }
994
995 /* Skip excess digits, truncating toward -Infinity. */
996 if (sign < 0)
997 for (; ISDIGIT (*p); p++)
998 if (*p != '0')
999 {
1000 ns++;
1001 break;
1002 }
1003 while (ISDIGIT (*p))
1004 p++;
1005
1006 /* Adjust to the timespec convention, which is that
1007 tv_nsec is always a positive offset even if tv_sec is
1008 negative. */
1009 if (sign < 0 && ns)
1010 {
1011 s--;
1012 if (! (s < 0))
1013 return '?';
1014 ns = BILLION - ns;
1015 }
1016
1017 lvalp->timespec.tv_sec = s;
1018 lvalp->timespec.tv_nsec = ns;
1019 pc->input = p;
1020 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1021 }
1022 else
1023 {
1024 lvalp->textintval.negative = sign < 0;
1025 if (sign < 0)
1026 {
1027 lvalp->textintval.value = - value;
1028 if (0 < lvalp->textintval.value)
1029 return '?';
1030 }
1031 else
1032 {
1033 lvalp->textintval.value = value;
1034 if (lvalp->textintval.value < 0)
1035 return '?';
1036 }
1037 lvalp->textintval.digits = p - pc->input;
1038 pc->input = p;
1039 return sign ? tSNUMBER : tUNUMBER;
1040 }
1041 }
1042
1043 if (ISALPHA (c))
1044 {
1045 char buff[20];
1046 char *p = buff;
1047 table const *tp;
1048
1049 do
1050 {
1051 if (p < buff + sizeof buff - 1)
1052 *p++ = c;
1053 c = *++pc->input;
1054 }
1055 while (ISALPHA (c) || c == '.');
1056
1057 *p = '\0';
1058 tp = lookup_word (pc, buff);
1059 if (! tp)
1060 return '?';
1061 lvalp->intval = tp->value;
1062 return tp->type;
1063 }
1064
1065 if (c != '(')
1066 return *pc->input++;
1067 count = 0;
1068 do
1069 {
1070 c = *pc->input++;
1071 if (c == '\0')
1072 return c;
1073 if (c == '(')
1074 count++;
1075 else if (c == ')')
1076 count--;
1077 }
1078 while (count != 0);
1079 }
1080 }
1081
1082 /* Do nothing if the parser reports an error. */
1083 static int
yyerror(parser_control * pc ATTRIBUTE_UNUSED,char * s ATTRIBUTE_UNUSED)1084 yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED)
1085 {
1086 return 0;
1087 }
1088
1089 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1090 passing it to mktime, return true if it's OK that mktime returned T.
1091 It's not OK if *TM0 has out-of-range members. */
1092
1093 static bool
mktime_ok(struct tm const * tm0,struct tm const * tm1,time_t t)1094 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1095 {
1096 if (t == (time_t) -1)
1097 {
1098 /* Guard against falsely reporting an error when parsing a time
1099 stamp that happens to equal (time_t) -1, on a host that
1100 supports such a time stamp. */
1101 tm1 = localtime (&t);
1102 if (!tm1)
1103 return false;
1104 }
1105
1106 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1107 | (tm0->tm_min ^ tm1->tm_min)
1108 | (tm0->tm_hour ^ tm1->tm_hour)
1109 | (tm0->tm_mday ^ tm1->tm_mday)
1110 | (tm0->tm_mon ^ tm1->tm_mon)
1111 | (tm0->tm_year ^ tm1->tm_year));
1112 }
1113
1114 /* A reasonable upper bound for the size of ordinary TZ strings.
1115 Use heap allocation if TZ's length exceeds this. */
1116 enum { TZBUFSIZE = 100 };
1117
1118 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1119 otherwise. */
1120 static char *
get_tz(char tzbuf[TZBUFSIZE])1121 get_tz (char tzbuf[TZBUFSIZE])
1122 {
1123 char *tz = getenv ("TZ");
1124 if (tz)
1125 {
1126 size_t tzsize = strlen (tz) + 1;
1127 tz = (tzsize <= TZBUFSIZE
1128 ? memcpy (tzbuf, tz, tzsize)
1129 : xmemdup (tz, tzsize));
1130 }
1131 return tz;
1132 }
1133
1134 /* Parse a date/time string, storing the resulting time value into *RESULT.
1135 The string itself is pointed to by P. Return true if successful.
1136 P can be an incomplete or relative time specification; if so, use
1137 *NOW as the basis for the returned time. */
1138 bool
get_date(struct timespec * result,char const * p,struct timespec const * now)1139 get_date (struct timespec *result, char const *p, struct timespec const *now)
1140 {
1141 time_t Start;
1142 long int Start_ns;
1143 struct tm const *tmp;
1144 struct tm tm;
1145 struct tm tm0;
1146 parser_control pc;
1147 struct timespec gettime_buffer;
1148 unsigned char c;
1149 bool tz_was_altered = false;
1150 char *tz0 = NULL;
1151 char tz0buf[TZBUFSIZE];
1152 bool ok = true;
1153
1154 if (! now)
1155 {
1156 gettime (&gettime_buffer);
1157 now = &gettime_buffer;
1158 }
1159
1160 Start = now->tv_sec;
1161 Start_ns = now->tv_nsec;
1162
1163 tmp = localtime (&now->tv_sec);
1164 if (! tmp)
1165 return false;
1166
1167 while (c = *p, ISSPACE (c))
1168 p++;
1169
1170 if (strncmp (p, "TZ=\"", 4) == 0)
1171 {
1172 char const *tzbase = p + 4;
1173 size_t tzsize = 1;
1174 char const *s;
1175
1176 for (s = tzbase; *s; s++, tzsize++)
1177 if (*s == '\\')
1178 {
1179 s++;
1180 if (! (*s == '\\' || *s == '"'))
1181 break;
1182 }
1183 else if (*s == '"')
1184 {
1185 char *z;
1186 char *tz1;
1187 char tz1buf[TZBUFSIZE];
1188 bool large_tz = TZBUFSIZE < tzsize;
1189 bool setenv_ok;
1190 tz0 = get_tz (tz0buf);
1191 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1192 for (s = tzbase; *s != '"'; s++)
1193 *z++ = *(s += *s == '\\');
1194 *z = '\0';
1195 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1196 if (large_tz)
1197 free (tz1);
1198 if (!setenv_ok)
1199 goto fail;
1200 tz_was_altered = true;
1201 p = s + 1;
1202 }
1203 }
1204
1205 pc.input = p;
1206 pc.year.value = tmp->tm_year;
1207 pc.year.value += TM_YEAR_BASE;
1208 pc.year.digits = 0;
1209 pc.month = tmp->tm_mon + 1;
1210 pc.day = tmp->tm_mday;
1211 pc.hour = tmp->tm_hour;
1212 pc.minutes = tmp->tm_min;
1213 pc.seconds.tv_sec = tmp->tm_sec;
1214 pc.seconds.tv_nsec = Start_ns;
1215 tm.tm_isdst = tmp->tm_isdst;
1216
1217 pc.meridian = MER24;
1218 pc.rel_ns = 0;
1219 pc.rel_seconds = 0;
1220 pc.rel_minutes = 0;
1221 pc.rel_hour = 0;
1222 pc.rel_day = 0;
1223 pc.rel_month = 0;
1224 pc.rel_year = 0;
1225 pc.timespec_seen = false;
1226 pc.rels_seen = false;
1227 pc.dates_seen = 0;
1228 pc.days_seen = 0;
1229 pc.times_seen = 0;
1230 pc.local_zones_seen = 0;
1231 pc.dsts_seen = 0;
1232 pc.zones_seen = 0;
1233
1234 #if HAVE_STRUCT_TM_TM_ZONE
1235 pc.local_time_zone_table[0].name = tmp->tm_zone;
1236 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1237 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1238 pc.local_time_zone_table[1].name = NULL;
1239
1240 /* Probe the names used in the next three calendar quarters, looking
1241 for a tm_isdst different from the one we already have. */
1242 {
1243 int quarter;
1244 for (quarter = 1; quarter <= 3; quarter++)
1245 {
1246 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1247 struct tm const *probe_tm = localtime (&probe);
1248 if (probe_tm && probe_tm->tm_zone
1249 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1250 {
1251 {
1252 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1253 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1254 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1255 pc.local_time_zone_table[2].name = NULL;
1256 }
1257 break;
1258 }
1259 }
1260 }
1261 #else
1262 #if HAVE_TZNAME
1263 {
1264 # ifndef tzname
1265 extern char *tzname[];
1266 # endif
1267 int i;
1268 for (i = 0; i < 2; i++)
1269 {
1270 pc.local_time_zone_table[i].name = tzname[i];
1271 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1272 pc.local_time_zone_table[i].value = i;
1273 }
1274 pc.local_time_zone_table[i].name = NULL;
1275 }
1276 #else
1277 pc.local_time_zone_table[0].name = NULL;
1278 #endif
1279 #endif
1280
1281 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1282 && ! strcmp (pc.local_time_zone_table[0].name,
1283 pc.local_time_zone_table[1].name))
1284 {
1285 /* This locale uses the same abbrevation for standard and
1286 daylight times. So if we see that abbreviation, we don't
1287 know whether it's daylight time. */
1288 pc.local_time_zone_table[0].value = -1;
1289 pc.local_time_zone_table[1].name = NULL;
1290 }
1291
1292 if (yyparse (&pc) != 0)
1293 goto fail;
1294
1295 if (pc.timespec_seen)
1296 *result = pc.seconds;
1297 else
1298 {
1299 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1300 | (pc.local_zones_seen + pc.zones_seen)))
1301 goto fail;
1302
1303 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1304 tm.tm_mon = pc.month - 1;
1305 tm.tm_mday = pc.day;
1306 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1307 {
1308 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1309 if (tm.tm_hour < 0)
1310 goto fail;
1311 tm.tm_min = pc.minutes;
1312 tm.tm_sec = pc.seconds.tv_sec;
1313 }
1314 else
1315 {
1316 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1317 pc.seconds.tv_nsec = 0;
1318 }
1319
1320 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1321 if (!pc.rels_seen)
1322 tm.tm_isdst = -1;
1323
1324 /* But if the input explicitly specifies local time with or without
1325 DST, give mktime that information. */
1326 if (pc.local_zones_seen)
1327 tm.tm_isdst = pc.local_isdst;
1328
1329 tm0 = tm;
1330
1331 Start = mktime (&tm);
1332
1333 if (! mktime_ok (&tm0, &tm, Start))
1334 {
1335 if (! pc.zones_seen)
1336 goto fail;
1337 else
1338 {
1339 /* Guard against falsely reporting errors near the time_t
1340 boundaries when parsing times in other time zones. For
1341 example, suppose the input string "1969-12-31 23:00:00 -0100",
1342 the current time zone is 8 hours ahead of UTC, and the min
1343 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1344 localtime value is 1970-01-01 08:00:00, and mktime will
1345 therefore fail on 1969-12-31 23:00:00. To work around the
1346 problem, set the time zone to 1 hour behind UTC temporarily
1347 by setting TZ="XXX1:00" and try mktime again. */
1348
1349 long int time_zone = pc.time_zone;
1350 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1351 long int abs_time_zone_hour = abs_time_zone / 60;
1352 int abs_time_zone_min = abs_time_zone % 60;
1353 char tz1buf[sizeof "XXX+0:00"
1354 + sizeof pc.time_zone * CHAR_BIT / 3];
1355 if (!tz_was_altered)
1356 tz0 = get_tz (tz0buf);
1357 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1358 abs_time_zone_hour, abs_time_zone_min);
1359 if (setenv ("TZ", tz1buf, 1) != 0)
1360 goto fail;
1361 tz_was_altered = true;
1362 tm = tm0;
1363 Start = mktime (&tm);
1364 if (! mktime_ok (&tm0, &tm, Start))
1365 goto fail;
1366 }
1367 }
1368
1369 if (pc.days_seen && ! pc.dates_seen)
1370 {
1371 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1372 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1373 tm.tm_isdst = -1;
1374 Start = mktime (&tm);
1375 if (Start == (time_t) -1)
1376 goto fail;
1377 }
1378
1379 if (pc.zones_seen)
1380 {
1381 long int delta = pc.time_zone * 60;
1382 time_t t1;
1383 #ifdef HAVE_TM_GMTOFF
1384 delta -= tm.tm_gmtoff;
1385 #else
1386 time_t t = Start;
1387 struct tm const *gmt = gmtime (&t);
1388 if (! gmt)
1389 goto fail;
1390 delta -= tm_diff (&tm, gmt);
1391 #endif
1392 t1 = Start - delta;
1393 if ((Start < t1) != (delta < 0))
1394 goto fail; /* time_t overflow */
1395 Start = t1;
1396 }
1397
1398 /* Add relative date. */
1399 if (pc.rel_year | pc.rel_month | pc.rel_day)
1400 {
1401 int year = tm.tm_year + pc.rel_year;
1402 int month = tm.tm_mon + pc.rel_month;
1403 int day = tm.tm_mday + pc.rel_day;
1404 if (((year < tm.tm_year) ^ (pc.rel_year < 0))
1405 | ((month < tm.tm_mon) ^ (pc.rel_month < 0))
1406 | ((day < tm.tm_mday) ^ (pc.rel_day < 0)))
1407 goto fail;
1408 tm.tm_year = year;
1409 tm.tm_mon = month;
1410 tm.tm_mday = day;
1411 Start = mktime (&tm);
1412 if (Start == (time_t) -1)
1413 goto fail;
1414 }
1415
1416 /* Add relative hours, minutes, and seconds. On hosts that support
1417 leap seconds, ignore the possibility of leap seconds; e.g.,
1418 "+ 10 minutes" adds 600 seconds, even if one of them is a
1419 leap second. Typically this is not what the user wants, but it's
1420 too hard to do it the other way, because the time zone indicator
1421 must be applied before relative times, and if mktime is applied
1422 again the time zone will be lost. */
1423 {
1424 long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1425 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1426 time_t t0 = Start;
1427 long int d1 = 60 * 60 * pc.rel_hour;
1428 time_t t1 = t0 + d1;
1429 long int d2 = 60 * pc.rel_minutes;
1430 time_t t2 = t1 + d2;
1431 long int d3 = pc.rel_seconds;
1432 time_t t3 = t2 + d3;
1433 long int d4 = (sum_ns - normalized_ns) / BILLION;
1434 time_t t4 = t3 + d4;
1435
1436 if ((d1 / (60 * 60) ^ pc.rel_hour)
1437 | (d2 / 60 ^ pc.rel_minutes)
1438 | ((t1 < t0) ^ (d1 < 0))
1439 | ((t2 < t1) ^ (d2 < 0))
1440 | ((t3 < t2) ^ (d3 < 0))
1441 | ((t4 < t3) ^ (d4 < 0)))
1442 goto fail;
1443
1444 result->tv_sec = t4;
1445 result->tv_nsec = normalized_ns;
1446 }
1447 }
1448
1449 goto done;
1450
1451 fail:
1452 ok = false;
1453 done:
1454 if (tz_was_altered)
1455 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1456 if (tz0 != tz0buf)
1457 free (tz0);
1458 return ok;
1459 }
1460
1461 #if TEST
1462
1463 int
main(int ac,char ** av)1464 main (int ac, char **av)
1465 {
1466 char buff[BUFSIZ];
1467
1468 printf ("Enter date, or blank line to exit.\n\t> ");
1469 fflush (stdout);
1470
1471 buff[BUFSIZ - 1] = '\0';
1472 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1473 {
1474 struct timespec d;
1475 struct tm const *tm;
1476 if (! get_date (&d, buff, NULL))
1477 printf ("Bad format - couldn't convert.\n");
1478 else if (! (tm = localtime (&d.tv_sec)))
1479 {
1480 long int sec = d.tv_sec;
1481 printf ("localtime (%ld) failed\n", sec);
1482 }
1483 else
1484 {
1485 int ns = d.tv_nsec;
1486 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1487 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1488 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1489 }
1490 printf ("\t> ");
1491 fflush (stdout);
1492 }
1493 return 0;
1494 }
1495 #endif /* TEST */
1496