Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / gpl2 / xcvs / dist / lib / getdate.y
blob3984b4717c7604d0ff26abe9015dcc8eb47c1629
1 %{
2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software
5 Foundation, Inc.
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.
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.
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. */
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.
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. */
32 /* FIXME: Check for arithmetic overflow in all cases, not just
33 some of them. */
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
39 #include "getdate.h"
41 /* There's no need to extend the stack, so there's no need to involve
42 alloca. */
43 #define YYSTACK_USE_ALLOCA 0
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
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
62 #include <ctype.h>
63 #include <limits.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
68 #include "setenv.h"
69 #include "xalloc.h"
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
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))
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)
90 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
91 # define __attribute__(x)
92 #endif
94 #ifndef ATTRIBUTE_UNUSED
95 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
96 #endif
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.
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))
113 #define EPOCH_YEAR 1970
114 #define TM_YEAR_BASE 1900
116 #define HOUR(x) ((x) * 60)
118 /* An integer value, and the number of digits in its textual
119 representation. */
120 typedef struct
122 bool negative;
123 long int value;
124 size_t digits;
125 } textint;
127 /* An entry in the lexical lookup table. */
128 typedef struct
130 char const *name;
131 int type;
132 int value;
133 } table;
135 /* Meridian: am, pm, or 24-hour style. */
136 enum { MERam, MERpm, MER24 };
138 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
140 /* Information passed to and from the parser. */
141 typedef struct
143 /* The input string remaining to be parsed. */
144 const char *input;
146 /* N, if this is the Nth Tuesday. */
147 long int day_ordinal;
149 /* Day of week; Sunday is 0. */
150 int day_number;
152 /* tm_isdst flag for the local zone. */
153 int local_isdst;
155 /* Time zone, in minutes east of UTC. */
156 long int time_zone;
158 /* Style used for time. */
159 int meridian;
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 */
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;
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;
188 /* Table of local time zone abbrevations, terminated by a null entry. */
189 table local_time_zone_table[3];
190 } parser_control;
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);
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 }
205 /* This grammar has 20 shift/reduce conflicts. */
206 %expect 20
208 %union
210 long int intval;
211 textint textintval;
212 struct timespec timespec;
215 %token tAGO tDST
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
221 %token <textintval> tSNUMBER tUNUMBER
222 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
224 %type <intval> o_colon_minutes o_merid
225 %type <timespec> seconds signed_seconds unsigned_seconds
229 spec:
230 timespec
231 | items
234 timespec:
235 '@' seconds
237 pc->seconds = $2;
238 pc->timespec_seen = true;
242 items:
243 /* empty */
244 | items item
247 item:
248 time
249 { pc->times_seen++; }
250 | local_zone
251 { pc->local_zones_seen++; }
252 | zone
253 { pc->zones_seen++; }
254 | date
255 { pc->dates_seen++; }
256 | day
257 { pc->days_seen++; }
258 | rel
259 { pc->rels_seen = true; }
260 | cvsstamp
262 pc->dates_seen++;
263 pc->zones_seen++;
264 pc->times_seen++;
266 | number
269 cvsstamp: tUDECIMAL_NUMBER '.' tUDECIMAL_NUMBER '.' tUDECIMAL_NUMBER
271 int i;
272 pc->year.negative = 0;
273 pc->year.value = $1.tv_sec;
275 if (pc->year.value < 70)
276 pc->year.value += 2000;
277 else if (pc->year.value < 100)
278 pc->year.value += 1900;
280 for (i = pc->year.value, pc->year.digits = 0; i; i /= 10, pc->year.digits++)
281 continue;
282 if (pc->year.digits == 0)
283 pc->year.digits++;
285 pc->month = $1.tv_nsec / 10000000;
286 pc->day = $3.tv_sec;
287 pc->hour = $3.tv_nsec / 10000000;
288 pc->minutes = $5.tv_sec;
289 pc->seconds.tv_sec = $5.tv_nsec / 10000000;
290 pc->seconds.tv_nsec = 0;
291 pc->meridian = MER24;
292 pc->time_zone = 0;
295 time:
296 tUNUMBER tMERIDIAN
298 pc->hour = $1.value;
299 pc->minutes = 0;
300 pc->seconds.tv_sec = 0;
301 pc->seconds.tv_nsec = 0;
302 pc->meridian = $2;
304 | tUNUMBER ':' tUNUMBER o_merid
306 pc->hour = $1.value;
307 pc->minutes = $3.value;
308 pc->seconds.tv_sec = 0;
309 pc->seconds.tv_nsec = 0;
310 pc->meridian = $4;
312 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
314 pc->hour = $1.value;
315 pc->minutes = $3.value;
316 pc->seconds.tv_sec = 0;
317 pc->seconds.tv_nsec = 0;
318 pc->meridian = MER24;
319 pc->zones_seen++;
320 pc->time_zone = time_zone_hhmm ($4, $5);
322 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
324 pc->hour = $1.value;
325 pc->minutes = $3.value;
326 pc->seconds = $5;
327 pc->meridian = $6;
329 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
331 pc->hour = $1.value;
332 pc->minutes = $3.value;
333 pc->seconds = $5;
334 pc->meridian = MER24;
335 pc->zones_seen++;
336 pc->time_zone = time_zone_hhmm ($6, $7);
340 local_zone:
341 tLOCAL_ZONE
343 pc->local_isdst = $1;
344 pc->dsts_seen += (0 < $1);
346 | tLOCAL_ZONE tDST
348 pc->local_isdst = 1;
349 pc->dsts_seen += (0 < $1) + 1;
353 zone:
354 tZONE
355 { pc->time_zone = $1; }
356 | tZONE relunit_snumber
357 { pc->time_zone = $1; pc->rels_seen = true; }
358 | tZONE tSNUMBER o_colon_minutes
359 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
360 | tDAYZONE
361 { pc->time_zone = $1 + 60; }
362 | tZONE tDST
363 { pc->time_zone = $1 + 60; }
366 day:
367 tDAY
369 pc->day_ordinal = 1;
370 pc->day_number = $1;
372 | tDAY ','
374 pc->day_ordinal = 1;
375 pc->day_number = $1;
377 | tORDINAL tDAY
379 pc->day_ordinal = $1;
380 pc->day_number = $2;
382 | tUNUMBER tDAY
384 pc->day_ordinal = $1.value;
385 pc->day_number = $2;
389 date:
390 tUNUMBER '/' tUNUMBER
392 pc->month = $1.value;
393 pc->day = $3.value;
395 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
397 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
398 otherwise as MM/DD/YY.
399 The goal in recognizing YYYY/MM/DD is solely to support legacy
400 machine-generated dates like those in an RCS log listing. If
401 you want portability, use the ISO 8601 format. */
402 if (4 <= $1.digits)
404 pc->year = $1;
405 pc->month = $3.value;
406 pc->day = $5.value;
408 else
410 pc->month = $1.value;
411 pc->day = $3.value;
412 pc->year = $5;
415 | tUNUMBER tSNUMBER tSNUMBER
417 /* ISO 8601 format. YYYY-MM-DD. */
418 pc->year = $1;
419 pc->month = -$2.value;
420 pc->day = -$3.value;
422 | tUNUMBER tMONTH tSNUMBER
424 /* e.g. 17-JUN-1992. */
425 pc->day = $1.value;
426 pc->month = $2;
427 pc->year.value = -$3.value;
428 pc->year.digits = $3.digits;
430 | tMONTH tSNUMBER tSNUMBER
432 /* e.g. JUN-17-1992. */
433 pc->month = $1;
434 pc->day = -$2.value;
435 pc->year.value = -$3.value;
436 pc->year.digits = $3.digits;
438 | tMONTH tUNUMBER
440 pc->month = $1;
441 pc->day = $2.value;
443 | tMONTH tUNUMBER ',' tUNUMBER
445 pc->month = $1;
446 pc->day = $2.value;
447 pc->year = $4;
449 | tUNUMBER tMONTH
451 pc->day = $1.value;
452 pc->month = $2;
454 | tUNUMBER tMONTH tUNUMBER
456 pc->day = $1.value;
457 pc->month = $2;
458 pc->year = $3;
462 rel:
463 relunit tAGO
465 pc->rel_ns = -pc->rel_ns;
466 pc->rel_seconds = -pc->rel_seconds;
467 pc->rel_minutes = -pc->rel_minutes;
468 pc->rel_hour = -pc->rel_hour;
469 pc->rel_day = -pc->rel_day;
470 pc->rel_month = -pc->rel_month;
471 pc->rel_year = -pc->rel_year;
473 | relunit
476 relunit:
477 tORDINAL tYEAR_UNIT
478 { pc->rel_year += $1 * $2; }
479 | tUNUMBER tYEAR_UNIT
480 { pc->rel_year += $1.value * $2; }
481 | tYEAR_UNIT
482 { pc->rel_year += $1; }
483 | tORDINAL tMONTH_UNIT
484 { pc->rel_month += $1 * $2; }
485 | tUNUMBER tMONTH_UNIT
486 { pc->rel_month += $1.value * $2; }
487 | tMONTH_UNIT
488 { pc->rel_month += $1; }
489 | tORDINAL tDAY_UNIT
490 { pc->rel_day += $1 * $2; }
491 | tUNUMBER tDAY_UNIT
492 { pc->rel_day += $1.value * $2; }
493 | tDAY_UNIT
494 { pc->rel_day += $1; }
495 | tORDINAL tHOUR_UNIT
496 { pc->rel_hour += $1 * $2; }
497 | tUNUMBER tHOUR_UNIT
498 { pc->rel_hour += $1.value * $2; }
499 | tHOUR_UNIT
500 { pc->rel_hour += $1; }
501 | tORDINAL tMINUTE_UNIT
502 { pc->rel_minutes += $1 * $2; }
503 | tUNUMBER tMINUTE_UNIT
504 { pc->rel_minutes += $1.value * $2; }
505 | tMINUTE_UNIT
506 { pc->rel_minutes += $1; }
507 | tORDINAL tSEC_UNIT
508 { pc->rel_seconds += $1 * $2; }
509 | tUNUMBER tSEC_UNIT
510 { pc->rel_seconds += $1.value * $2; }
511 | tSDECIMAL_NUMBER tSEC_UNIT
512 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
513 | tUDECIMAL_NUMBER tSEC_UNIT
514 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
515 | tSEC_UNIT
516 { pc->rel_seconds += $1; }
517 | relunit_snumber
520 relunit_snumber:
521 tSNUMBER tYEAR_UNIT
522 { pc->rel_year += $1.value * $2; }
523 | tSNUMBER tMONTH_UNIT
524 { pc->rel_month += $1.value * $2; }
525 | tSNUMBER tDAY_UNIT
526 { pc->rel_day += $1.value * $2; }
527 | tSNUMBER tHOUR_UNIT
528 { pc->rel_hour += $1.value * $2; }
529 | tSNUMBER tMINUTE_UNIT
530 { pc->rel_minutes += $1.value * $2; }
531 | tSNUMBER tSEC_UNIT
532 { pc->rel_seconds += $1.value * $2; }
535 seconds: signed_seconds | unsigned_seconds;
537 signed_seconds:
538 tSDECIMAL_NUMBER
539 | tSNUMBER
540 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
543 unsigned_seconds:
544 tUDECIMAL_NUMBER
545 | tUNUMBER
546 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
549 number:
550 tUNUMBER
552 if (pc->dates_seen && ! pc->year.digits
553 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
554 pc->year = $1;
555 else
557 if (4 < $1.digits)
559 pc->dates_seen++;
560 pc->day = $1.value % 100;
561 pc->month = ($1.value / 100) % 100;
562 pc->year.value = $1.value / 10000;
563 pc->year.digits = $1.digits - 4;
565 else
567 pc->times_seen++;
568 if ($1.digits <= 2)
570 pc->hour = $1.value;
571 pc->minutes = 0;
573 else
575 pc->hour = $1.value / 100;
576 pc->minutes = $1.value % 100;
578 pc->seconds.tv_sec = 0;
579 pc->seconds.tv_nsec = 0;
580 pc->meridian = MER24;
586 o_colon_minutes:
587 /* empty */
588 { $$ = -1; }
589 | ':' tUNUMBER
590 { $$ = $2.value; }
593 o_merid:
594 /* empty */
595 { $$ = MER24; }
596 | tMERIDIAN
597 { $$ = $1; }
602 static table const meridian_table[] =
604 { "AM", tMERIDIAN, MERam },
605 { "A.M.", tMERIDIAN, MERam },
606 { "PM", tMERIDIAN, MERpm },
607 { "P.M.", tMERIDIAN, MERpm },
608 { NULL, 0, 0 }
611 static table const dst_table[] =
613 { "DST", tDST, 0 }
616 static table const month_and_day_table[] =
618 { "JANUARY", tMONTH, 1 },
619 { "FEBRUARY", tMONTH, 2 },
620 { "MARCH", tMONTH, 3 },
621 { "APRIL", tMONTH, 4 },
622 { "MAY", tMONTH, 5 },
623 { "JUNE", tMONTH, 6 },
624 { "JULY", tMONTH, 7 },
625 { "AUGUST", tMONTH, 8 },
626 { "SEPTEMBER",tMONTH, 9 },
627 { "SEPT", tMONTH, 9 },
628 { "OCTOBER", tMONTH, 10 },
629 { "NOVEMBER", tMONTH, 11 },
630 { "DECEMBER", tMONTH, 12 },
631 { "SUNDAY", tDAY, 0 },
632 { "MONDAY", tDAY, 1 },
633 { "TUESDAY", tDAY, 2 },
634 { "TUES", tDAY, 2 },
635 { "WEDNESDAY",tDAY, 3 },
636 { "WEDNES", tDAY, 3 },
637 { "THURSDAY", tDAY, 4 },
638 { "THUR", tDAY, 4 },
639 { "THURS", tDAY, 4 },
640 { "FRIDAY", tDAY, 5 },
641 { "SATURDAY", tDAY, 6 },
642 { NULL, 0, 0 }
645 static table const time_units_table[] =
647 { "YEAR", tYEAR_UNIT, 1 },
648 { "MONTH", tMONTH_UNIT, 1 },
649 { "FORTNIGHT",tDAY_UNIT, 14 },
650 { "WEEK", tDAY_UNIT, 7 },
651 { "DAY", tDAY_UNIT, 1 },
652 { "HOUR", tHOUR_UNIT, 1 },
653 { "MINUTE", tMINUTE_UNIT, 1 },
654 { "MIN", tMINUTE_UNIT, 1 },
655 { "SECOND", tSEC_UNIT, 1 },
656 { "SEC", tSEC_UNIT, 1 },
657 { NULL, 0, 0 }
660 /* Assorted relative-time words. */
661 static table const relative_time_table[] =
663 { "TOMORROW", tDAY_UNIT, 1 },
664 { "YESTERDAY",tDAY_UNIT, -1 },
665 { "TODAY", tDAY_UNIT, 0 },
666 { "NOW", tDAY_UNIT, 0 },
667 { "LAST", tORDINAL, -1 },
668 { "THIS", tORDINAL, 0 },
669 { "NEXT", tORDINAL, 1 },
670 { "FIRST", tORDINAL, 1 },
671 /*{ "SECOND", tORDINAL, 2 }, */
672 { "THIRD", tORDINAL, 3 },
673 { "FOURTH", tORDINAL, 4 },
674 { "FIFTH", tORDINAL, 5 },
675 { "SIXTH", tORDINAL, 6 },
676 { "SEVENTH", tORDINAL, 7 },
677 { "EIGHTH", tORDINAL, 8 },
678 { "NINTH", tORDINAL, 9 },
679 { "TENTH", tORDINAL, 10 },
680 { "ELEVENTH", tORDINAL, 11 },
681 { "TWELFTH", tORDINAL, 12 },
682 { "AGO", tAGO, 1 },
683 { NULL, 0, 0 }
686 /* The universal time zone table. These labels can be used even for
687 time stamps that would not otherwise be valid, e.g., GMT time
688 stamps in London during summer. */
689 static table const universal_time_zone_table[] =
691 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
692 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
693 { "UTC", tZONE, HOUR ( 0) },
694 { NULL, 0, 0 }
697 /* The time zone table. This table is necessarily incomplete, as time
698 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
699 as Eastern time in Australia, not as US Eastern Standard Time.
700 You cannot rely on getdate to handle arbitrary time zone
701 abbreviations; use numeric abbreviations like `-0500' instead. */
702 static table const time_zone_table[] =
704 { "WET", tZONE, HOUR ( 0) }, /* Western European */
705 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
706 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
707 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
708 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
709 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
710 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
711 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
712 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
713 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
714 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
715 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
716 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
717 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
718 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
719 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
720 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
721 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
722 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
723 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
724 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
725 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
726 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
727 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
728 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
729 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
730 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
731 { "CET", tZONE, HOUR ( 1) }, /* Central European */
732 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
733 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
734 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
735 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
736 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
737 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
738 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
739 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
740 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
741 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
742 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
743 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
744 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
745 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
746 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
747 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
748 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
749 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
750 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
751 { NULL, 0, 0 }
754 /* Military time zone table. */
755 static table const military_table[] =
757 { "A", tZONE, -HOUR ( 1) },
758 { "B", tZONE, -HOUR ( 2) },
759 { "C", tZONE, -HOUR ( 3) },
760 { "D", tZONE, -HOUR ( 4) },
761 { "E", tZONE, -HOUR ( 5) },
762 { "F", tZONE, -HOUR ( 6) },
763 { "G", tZONE, -HOUR ( 7) },
764 { "H", tZONE, -HOUR ( 8) },
765 { "I", tZONE, -HOUR ( 9) },
766 { "K", tZONE, -HOUR (10) },
767 { "L", tZONE, -HOUR (11) },
768 { "M", tZONE, -HOUR (12) },
769 { "N", tZONE, HOUR ( 1) },
770 { "O", tZONE, HOUR ( 2) },
771 { "P", tZONE, HOUR ( 3) },
772 { "Q", tZONE, HOUR ( 4) },
773 { "R", tZONE, HOUR ( 5) },
774 { "S", tZONE, HOUR ( 6) },
775 { "T", tZONE, HOUR ( 7) },
776 { "U", tZONE, HOUR ( 8) },
777 { "V", tZONE, HOUR ( 9) },
778 { "W", tZONE, HOUR (10) },
779 { "X", tZONE, HOUR (11) },
780 { "Y", tZONE, HOUR (12) },
781 { "Z", tZONE, HOUR ( 0) },
782 { NULL, 0, 0 }
787 /* Convert a time zone expressed as HH:MM into an integer count of
788 minutes. If MM is negative, then S is of the form HHMM and needs
789 to be picked apart; otherwise, S is of the form HH. */
791 static long int
792 time_zone_hhmm (textint s, long int mm)
794 if (mm < 0)
795 return (s.value / 100) * 60 + s.value % 100;
796 else
797 return s.value * 60 + (s.negative ? -mm : mm);
800 static int
801 to_hour (long int hours, int meridian)
803 switch (meridian)
805 default: /* Pacify GCC. */
806 case MER24:
807 return 0 <= hours && hours < 24 ? hours : -1;
808 case MERam:
809 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
810 case MERpm:
811 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
815 static long int
816 to_year (textint textyear)
818 long int year = textyear.value;
820 if (year < 0)
821 year = -year;
823 /* XPG4 suggests that years 00-68 map to 2000-2068, and
824 years 69-99 map to 1969-1999. */
825 else if (textyear.digits == 2)
826 year += year < 69 ? 2000 : 1900;
828 return year;
831 static table const *
832 lookup_zone (parser_control const *pc, char const *name)
834 table const *tp;
836 for (tp = universal_time_zone_table; tp->name; tp++)
837 if (strcmp (name, tp->name) == 0)
838 return tp;
840 /* Try local zone abbreviations before those in time_zone_table, as
841 the local ones are more likely to be right. */
842 for (tp = pc->local_time_zone_table; tp->name; tp++)
843 if (strcmp (name, tp->name) == 0)
844 return tp;
846 for (tp = time_zone_table; tp->name; tp++)
847 if (strcmp (name, tp->name) == 0)
848 return tp;
850 return NULL;
853 #if ! HAVE_TM_GMTOFF
854 /* Yield the difference between *A and *B,
855 measured in seconds, ignoring leap seconds.
856 The body of this function is taken directly from the GNU C Library;
857 see src/strftime.c. */
858 static long int
859 tm_diff (struct tm const *a, struct tm const *b)
861 /* Compute intervening leap days correctly even if year is negative.
862 Take care to avoid int overflow in leap day calculations. */
863 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
864 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
865 int a100 = a4 / 25 - (a4 % 25 < 0);
866 int b100 = b4 / 25 - (b4 % 25 < 0);
867 int a400 = SHR (a100, 2);
868 int b400 = SHR (b100, 2);
869 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
870 long int ayear = a->tm_year;
871 long int years = ayear - b->tm_year;
872 long int days = (365 * years + intervening_leap_days
873 + (a->tm_yday - b->tm_yday));
874 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
875 + (a->tm_min - b->tm_min))
876 + (a->tm_sec - b->tm_sec));
878 #endif /* ! HAVE_TM_GMTOFF */
880 static table const *
881 lookup_word (parser_control const *pc, char *word)
883 char *p;
884 char *q;
885 size_t wordlen;
886 table const *tp;
887 bool period_found;
888 bool abbrev;
890 /* Make it uppercase. */
891 for (p = word; *p; p++)
893 unsigned char ch = *p;
894 if (ISLOWER (ch))
895 *p = toupper (ch);
898 for (tp = meridian_table; tp->name; tp++)
899 if (strcmp (word, tp->name) == 0)
900 return tp;
902 /* See if we have an abbreviation for a month. */
903 wordlen = strlen (word);
904 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
906 for (tp = month_and_day_table; tp->name; tp++)
907 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
908 return tp;
910 if ((tp = lookup_zone (pc, word)))
911 return tp;
913 if (strcmp (word, dst_table[0].name) == 0)
914 return dst_table;
916 for (tp = time_units_table; tp->name; tp++)
917 if (strcmp (word, tp->name) == 0)
918 return tp;
920 /* Strip off any plural and try the units table again. */
921 if (word[wordlen - 1] == 'S')
923 word[wordlen - 1] = '\0';
924 for (tp = time_units_table; tp->name; tp++)
925 if (strcmp (word, tp->name) == 0)
926 return tp;
927 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
930 for (tp = relative_time_table; tp->name; tp++)
931 if (strcmp (word, tp->name) == 0)
932 return tp;
934 /* Military time zones. */
935 if (wordlen == 1)
936 for (tp = military_table; tp->name; tp++)
937 if (word[0] == tp->name[0])
938 return tp;
940 /* Drop out any periods and try the time zone table again. */
941 for (period_found = false, p = q = word; (*p = *q); q++)
942 if (*q == '.')
943 period_found = true;
944 else
945 p++;
946 if (period_found && (tp = lookup_zone (pc, word)))
947 return tp;
949 return NULL;
952 static int
953 yylex (YYSTYPE *lvalp, parser_control *pc)
955 unsigned char c;
956 size_t count;
958 for (;;)
960 while (c = *pc->input, ISSPACE (c))
961 pc->input++;
963 if (ISDIGIT (c) || c == '-' || c == '+')
965 char const *p;
966 int sign;
967 unsigned long int value;
968 if (c == '-' || c == '+')
970 sign = c == '-' ? -1 : 1;
971 while (c = *++pc->input, ISSPACE (c))
972 continue;
973 if (! ISDIGIT (c))
974 /* skip the '-' sign */
975 continue;
977 else
978 sign = 0;
979 p = pc->input;
980 for (value = 0; ; value *= 10)
982 unsigned long int value1 = value + (c - '0');
983 if (value1 < value)
984 return '?';
985 value = value1;
986 c = *++p;
987 if (! ISDIGIT (c))
988 break;
989 if (ULONG_MAX / 10 < value)
990 return '?';
992 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
994 time_t s;
995 int ns;
996 int digits;
997 unsigned long int value1;
999 /* Check for overflow when converting value to time_t. */
1000 if (sign < 0)
1002 s = - value;
1003 if (0 < s)
1004 return '?';
1005 value1 = -s;
1007 else
1009 s = value;
1010 if (s < 0)
1011 return '?';
1012 value1 = s;
1014 if (value != value1)
1015 return '?';
1017 /* Accumulate fraction, to ns precision. */
1018 p++;
1019 ns = *p++ - '0';
1020 for (digits = 2; digits <= LOG10_BILLION; digits++)
1022 ns *= 10;
1023 if (ISDIGIT (*p))
1024 ns += *p++ - '0';
1027 /* Skip excess digits, truncating toward -Infinity. */
1028 if (sign < 0)
1029 for (; ISDIGIT (*p); p++)
1030 if (*p != '0')
1032 ns++;
1033 break;
1035 while (ISDIGIT (*p))
1036 p++;
1038 /* Adjust to the timespec convention, which is that
1039 tv_nsec is always a positive offset even if tv_sec is
1040 negative. */
1041 if (sign < 0 && ns)
1043 s--;
1044 if (! (s < 0))
1045 return '?';
1046 ns = BILLION - ns;
1049 lvalp->timespec.tv_sec = s;
1050 lvalp->timespec.tv_nsec = ns;
1051 pc->input = p;
1052 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1054 else
1056 lvalp->textintval.negative = sign < 0;
1057 if (sign < 0)
1059 lvalp->textintval.value = - value;
1060 if (0 < lvalp->textintval.value)
1061 return '?';
1063 else
1065 lvalp->textintval.value = value;
1066 if (lvalp->textintval.value < 0)
1067 return '?';
1069 lvalp->textintval.digits = p - pc->input;
1070 pc->input = p;
1071 return sign ? tSNUMBER : tUNUMBER;
1075 if (ISALPHA (c))
1077 char buff[20];
1078 char *p = buff;
1079 table const *tp;
1083 if (p < buff + sizeof buff - 1)
1084 *p++ = c;
1085 c = *++pc->input;
1087 while (ISALPHA (c) || c == '.');
1089 *p = '\0';
1090 tp = lookup_word (pc, buff);
1091 if (! tp)
1092 return '?';
1093 lvalp->intval = tp->value;
1094 return tp->type;
1097 if (c != '(')
1098 return *pc->input++;
1099 count = 0;
1102 c = *pc->input++;
1103 if (c == '\0')
1104 return c;
1105 if (c == '(')
1106 count++;
1107 else if (c == ')')
1108 count--;
1110 while (count != 0);
1114 /* Do nothing if the parser reports an error. */
1115 static int
1116 yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED)
1118 return 0;
1121 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1122 passing it to mktime, return true if it's OK that mktime returned T.
1123 It's not OK if *TM0 has out-of-range members. */
1125 static bool
1126 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1128 if (t == (time_t) -1)
1130 /* Guard against falsely reporting an error when parsing a time
1131 stamp that happens to equal (time_t) -1, on a host that
1132 supports such a time stamp. */
1133 tm1 = localtime (&t);
1134 if (!tm1)
1135 return false;
1138 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1139 | (tm0->tm_min ^ tm1->tm_min)
1140 | (tm0->tm_hour ^ tm1->tm_hour)
1141 | (tm0->tm_mday ^ tm1->tm_mday)
1142 | (tm0->tm_mon ^ tm1->tm_mon)
1143 | (tm0->tm_year ^ tm1->tm_year));
1146 /* A reasonable upper bound for the size of ordinary TZ strings.
1147 Use heap allocation if TZ's length exceeds this. */
1148 enum { TZBUFSIZE = 100 };
1150 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1151 otherwise. */
1152 static char *
1153 get_tz (char tzbuf[TZBUFSIZE])
1155 char *tz = getenv ("TZ");
1156 if (tz)
1158 size_t tzsize = strlen (tz) + 1;
1159 tz = (tzsize <= TZBUFSIZE
1160 ? memcpy (tzbuf, tz, tzsize)
1161 : xmemdup (tz, tzsize));
1163 return tz;
1166 /* Parse a date/time string, storing the resulting time value into *RESULT.
1167 The string itself is pointed to by P. Return true if successful.
1168 P can be an incomplete or relative time specification; if so, use
1169 *NOW as the basis for the returned time. */
1170 bool
1171 get_date (struct timespec *result, char const *p, struct timespec const *now)
1173 time_t Start;
1174 long int Start_ns;
1175 struct tm const *tmp;
1176 struct tm tm;
1177 struct tm tm0;
1178 parser_control pc;
1179 struct timespec gettime_buffer;
1180 unsigned char c;
1181 bool tz_was_altered = false;
1182 char *tz0 = NULL;
1183 char tz0buf[TZBUFSIZE];
1184 bool ok = true;
1186 if (! now)
1188 gettime (&gettime_buffer);
1189 now = &gettime_buffer;
1192 Start = now->tv_sec;
1193 Start_ns = now->tv_nsec;
1195 tmp = localtime (&now->tv_sec);
1196 if (! tmp)
1197 return false;
1199 while (c = *p, ISSPACE (c))
1200 p++;
1202 if (strncmp (p, "TZ=\"", 4) == 0)
1204 char const *tzbase = p + 4;
1205 size_t tzsize = 1;
1206 char const *s;
1208 for (s = tzbase; *s; s++, tzsize++)
1209 if (*s == '\\')
1211 s++;
1212 if (! (*s == '\\' || *s == '"'))
1213 break;
1215 else if (*s == '"')
1217 char *z;
1218 char *tz1;
1219 char tz1buf[TZBUFSIZE];
1220 bool large_tz = TZBUFSIZE < tzsize;
1221 bool setenv_ok;
1222 tz0 = get_tz (tz0buf);
1223 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1224 for (s = tzbase; *s != '"'; s++)
1225 *z++ = *(s += *s == '\\');
1226 *z = '\0';
1227 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1228 if (large_tz)
1229 free (tz1);
1230 if (!setenv_ok)
1231 goto fail;
1232 tz_was_altered = true;
1233 p = s + 1;
1237 pc.input = p;
1238 pc.year.value = tmp->tm_year;
1239 pc.year.value += TM_YEAR_BASE;
1240 pc.year.digits = 0;
1241 pc.month = tmp->tm_mon + 1;
1242 pc.day = tmp->tm_mday;
1243 pc.hour = tmp->tm_hour;
1244 pc.minutes = tmp->tm_min;
1245 pc.seconds.tv_sec = tmp->tm_sec;
1246 pc.seconds.tv_nsec = Start_ns;
1247 tm.tm_isdst = tmp->tm_isdst;
1249 pc.meridian = MER24;
1250 pc.rel_ns = 0;
1251 pc.rel_seconds = 0;
1252 pc.rel_minutes = 0;
1253 pc.rel_hour = 0;
1254 pc.rel_day = 0;
1255 pc.rel_month = 0;
1256 pc.rel_year = 0;
1257 pc.timespec_seen = false;
1258 pc.rels_seen = false;
1259 pc.dates_seen = 0;
1260 pc.days_seen = 0;
1261 pc.times_seen = 0;
1262 pc.local_zones_seen = 0;
1263 pc.dsts_seen = 0;
1264 pc.zones_seen = 0;
1266 #if HAVE_STRUCT_TM_TM_ZONE
1267 pc.local_time_zone_table[0].name = tmp->tm_zone;
1268 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1269 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1270 pc.local_time_zone_table[1].name = NULL;
1272 /* Probe the names used in the next three calendar quarters, looking
1273 for a tm_isdst different from the one we already have. */
1275 int quarter;
1276 for (quarter = 1; quarter <= 3; quarter++)
1278 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1279 struct tm const *probe_tm = localtime (&probe);
1280 if (probe_tm && probe_tm->tm_zone
1281 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1284 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1285 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1286 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1287 pc.local_time_zone_table[2].name = NULL;
1289 break;
1293 #else
1294 #if HAVE_TZNAME
1296 # ifndef tzname
1297 extern char *tzname[];
1298 # endif
1299 int i;
1300 for (i = 0; i < 2; i++)
1302 pc.local_time_zone_table[i].name = tzname[i];
1303 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1304 pc.local_time_zone_table[i].value = i;
1306 pc.local_time_zone_table[i].name = NULL;
1308 #else
1309 pc.local_time_zone_table[0].name = NULL;
1310 #endif
1311 #endif
1313 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1314 && ! strcmp (pc.local_time_zone_table[0].name,
1315 pc.local_time_zone_table[1].name))
1317 /* This locale uses the same abbrevation for standard and
1318 daylight times. So if we see that abbreviation, we don't
1319 know whether it's daylight time. */
1320 pc.local_time_zone_table[0].value = -1;
1321 pc.local_time_zone_table[1].name = NULL;
1324 if (yyparse (&pc) != 0)
1325 goto fail;
1327 if (pc.timespec_seen)
1328 *result = pc.seconds;
1329 else
1331 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1332 | (pc.local_zones_seen + pc.zones_seen)))
1333 goto fail;
1335 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1336 tm.tm_mon = pc.month - 1;
1337 tm.tm_mday = pc.day;
1338 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1340 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1341 if (tm.tm_hour < 0)
1342 goto fail;
1343 tm.tm_min = pc.minutes;
1344 tm.tm_sec = pc.seconds.tv_sec;
1346 else
1348 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1349 pc.seconds.tv_nsec = 0;
1352 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1353 if (!pc.rels_seen)
1354 tm.tm_isdst = -1;
1356 /* But if the input explicitly specifies local time with or without
1357 DST, give mktime that information. */
1358 if (pc.local_zones_seen)
1359 tm.tm_isdst = pc.local_isdst;
1361 tm0 = tm;
1363 Start = mktime (&tm);
1365 if (! mktime_ok (&tm0, &tm, Start))
1367 if (! pc.zones_seen)
1368 goto fail;
1369 else
1371 /* Guard against falsely reporting errors near the time_t
1372 boundaries when parsing times in other time zones. For
1373 example, suppose the input string "1969-12-31 23:00:00 -0100",
1374 the current time zone is 8 hours ahead of UTC, and the min
1375 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1376 localtime value is 1970-01-01 08:00:00, and mktime will
1377 therefore fail on 1969-12-31 23:00:00. To work around the
1378 problem, set the time zone to 1 hour behind UTC temporarily
1379 by setting TZ="XXX1:00" and try mktime again. */
1381 long int time_zone = pc.time_zone;
1382 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1383 long int abs_time_zone_hour = abs_time_zone / 60;
1384 int abs_time_zone_min = abs_time_zone % 60;
1385 char tz1buf[sizeof "XXX+0:00"
1386 + sizeof pc.time_zone * CHAR_BIT / 3];
1387 if (!tz_was_altered)
1388 tz0 = get_tz (tz0buf);
1389 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1390 abs_time_zone_hour, abs_time_zone_min);
1391 if (setenv ("TZ", tz1buf, 1) != 0)
1392 goto fail;
1393 tz_was_altered = true;
1394 tm = tm0;
1395 Start = mktime (&tm);
1396 if (! mktime_ok (&tm0, &tm, Start))
1397 goto fail;
1401 if (pc.days_seen && ! pc.dates_seen)
1403 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1404 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1405 tm.tm_isdst = -1;
1406 Start = mktime (&tm);
1407 if (Start == (time_t) -1)
1408 goto fail;
1411 if (pc.zones_seen)
1413 long int delta = pc.time_zone * 60;
1414 time_t t1;
1415 #ifdef HAVE_TM_GMTOFF
1416 delta -= tm.tm_gmtoff;
1417 #else
1418 time_t t = Start;
1419 struct tm const *gmt = gmtime (&t);
1420 if (! gmt)
1421 goto fail;
1422 delta -= tm_diff (&tm, gmt);
1423 #endif
1424 t1 = Start - delta;
1425 if ((Start < t1) != (delta < 0))
1426 goto fail; /* time_t overflow */
1427 Start = t1;
1430 /* Add relative date. */
1431 if (pc.rel_year | pc.rel_month | pc.rel_day)
1433 int year = tm.tm_year + pc.rel_year;
1434 int month = tm.tm_mon + pc.rel_month;
1435 int day = tm.tm_mday + pc.rel_day;
1436 if (((year < tm.tm_year) ^ (pc.rel_year < 0))
1437 | ((month < tm.tm_mon) ^ (pc.rel_month < 0))
1438 | ((day < tm.tm_mday) ^ (pc.rel_day < 0)))
1439 goto fail;
1440 tm.tm_year = year;
1441 tm.tm_mon = month;
1442 tm.tm_mday = day;
1443 Start = mktime (&tm);
1444 if (Start == (time_t) -1)
1445 goto fail;
1448 /* Add relative hours, minutes, and seconds. On hosts that support
1449 leap seconds, ignore the possibility of leap seconds; e.g.,
1450 "+ 10 minutes" adds 600 seconds, even if one of them is a
1451 leap second. Typically this is not what the user wants, but it's
1452 too hard to do it the other way, because the time zone indicator
1453 must be applied before relative times, and if mktime is applied
1454 again the time zone will be lost. */
1456 long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1457 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1458 time_t t0 = Start;
1459 long int d1 = 60 * 60 * pc.rel_hour;
1460 time_t t1 = t0 + d1;
1461 long int d2 = 60 * pc.rel_minutes;
1462 time_t t2 = t1 + d2;
1463 long int d3 = pc.rel_seconds;
1464 time_t t3 = t2 + d3;
1465 long int d4 = (sum_ns - normalized_ns) / BILLION;
1466 time_t t4 = t3 + d4;
1468 if ((d1 / (60 * 60) ^ pc.rel_hour)
1469 | (d2 / 60 ^ pc.rel_minutes)
1470 | ((t1 < t0) ^ (d1 < 0))
1471 | ((t2 < t1) ^ (d2 < 0))
1472 | ((t3 < t2) ^ (d3 < 0))
1473 | ((t4 < t3) ^ (d4 < 0)))
1474 goto fail;
1476 result->tv_sec = t4;
1477 result->tv_nsec = normalized_ns;
1481 goto done;
1483 fail:
1484 ok = false;
1485 done:
1486 if (tz_was_altered)
1487 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1488 if (tz0 != tz0buf)
1489 free (tz0);
1490 return ok;
1493 #if TEST
1496 main (int ac, char **av)
1498 char buff[BUFSIZ];
1500 printf ("Enter date, or blank line to exit.\n\t> ");
1501 fflush (stdout);
1503 buff[BUFSIZ - 1] = '\0';
1504 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1506 struct timespec d;
1507 struct tm const *tm;
1508 if (! get_date (&d, buff, NULL))
1509 printf ("Bad format - couldn't convert.\n");
1510 else if (! (tm = localtime (&d.tv_sec)))
1512 long int sec = d.tv_sec;
1513 printf ("localtime (%ld) failed\n", sec);
1515 else
1517 int ns = d.tv_nsec;
1518 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1519 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1520 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1522 printf ("\t> ");
1523 fflush (stdout);
1525 return 0;
1527 #endif /* TEST */