source: trunk/essentials/app-arch/tar/lib/getdate.y

Last change on this file was 3342, checked in by bird, 18 years ago

tar 1.16.1

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