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