1 %{
2 /* Parse a string into an internal time stamp.
3
4 Copyright (C) 1999-2000, 2002-2017 Free Software Foundation, Inc.
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
20 at the University of North Carolina at Chapel Hill. Later tweaked by
21 a couple of people on Usenet. Completely overhauled by Rich $alz
22 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
23
24 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
25 the right thing about local DST. Also modified by Paul Eggert
26 <eggert@cs.ucla.edu> in February 2004 to support
27 nanosecond-resolution time stamps, and in October 2004 to support
28 TZ strings in dates. */
29
30 /* FIXME: Check for arithmetic overflow in all cases, not just
31 some of them. */
32
33 #include <config.h>
34
35 #include "parse-datetime.h"
36
37 #include "intprops.h"
38 #include "timespec.h"
39 #include "verify.h"
40 #include "strftime.h"
41
42 /* There's no need to extend the stack, so there's no need to involve
43 alloca. */
44 #define YYSTACK_USE_ALLOCA 0
45
46 /* Tell Bison how much stack space is needed. 20 should be plenty for
47 this grammar, which is not right recursive. Beware setting it too
48 high, since that might cause problems on machines whose
49 implementations have lame stack-overflow checking. */
50 #define YYMAXDEPTH 20
51 #define YYINITDEPTH YYMAXDEPTH
52
53 /* Since the code of parse-datetime.y is not included in the Emacs executable
54 itself, there is no need to #define static in this file. Even if
55 the code were included in the Emacs executable, it probably
56 wouldn't do any harm to #undef it here; this will only cause
57 problems if we try to write to a static variable, which I don't
58 think this code needs to do. */
59 #ifdef emacs
60 # undef static
61 #endif
62
63 #include <inttypes.h>
64 #include <c-ctype.h>
65 #include <limits.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69
70 #include "gettext.h"
71 #include "xalloc.h"
72
73 #define _(str) gettext (str)
74
75 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
76 use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
77 /* FIXME: this is temporary. Remove when we have a mechanism to ensure
78 that the version we're using is fixed, too. */
79 #ifdef _STDLIB_H_
80 # undef _STDLIB_H
81 # define _STDLIB_H 1
82 #endif
83
84 /* ISDIGIT differs from isdigit, as follows:
85 - Its arg may be any int or unsigned int; it need not be an unsigned char
86 or EOF.
87 - It's typically faster.
88 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
89 isdigit unless it's important to use the locale's definition
90 of "digit" even when the host does not conform to POSIX. */
91 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
92
93 /* Shift A right by B bits portably, by dividing A by 2**B and
94 truncating towards minus infinity. A and B should be free of side
95 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
96 INT_BITS is the number of useful bits in an int. GNU code can
97 assume that INT_BITS is at least 32.
98
99 ISO C99 says that A >> B is implementation-defined if A < 0. Some
100 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
101 right in the usual way when A < 0, so SHR falls back on division if
102 ordinary A >> B doesn't seem to be the usual signed shift. */
103 #define SHR(a, b) \
104 (-1 >> 1 == -1 \
105 ? (a) >> (b) \
106 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
107
108 #define EPOCH_YEAR 1970
109 #define TM_YEAR_BASE 1900
110
111 #define HOUR(x) ((x) * 60)
112
113 #define STREQ(a, b) (strcmp (a, b) == 0)
114
115 /* long_time_t is a signed integer type that contains all time_t values. */
116 verify (TYPE_IS_INTEGER (time_t));
117 #if TIME_T_FITS_IN_LONG_INT
118 typedef long int long_time_t;
119 #else
120 typedef time_t long_time_t;
121 #endif
122
123 /* Convert a possibly-signed character to an unsigned character. This is
124 a bit safer than casting to unsigned char, since it catches some type
125 errors that the cast doesn't. */
to_uchar(char ch)126 static unsigned char to_uchar (char ch) { return ch; }
127
128 static void
dbg_printf(const char * msg,...)129 dbg_printf (const char *msg,...)
130 {
131 va_list args;
132 /* TODO: use gnulib's 'program_name' instead? */
133 fputs ("date: ", stderr);
134
135 va_start (args, msg);
136 vfprintf (stderr, msg, args);
137 va_end (args);
138 }
139
140
141
142 /* Lots of this code assumes time_t and time_t-like values fit into
143 long_time_t. */
144 verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
145 && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t));
146
147 /* FIXME: It also assumes that signed integer overflow silently wraps around,
148 but this is not true any more with recent versions of GCC 4. */
149
150 /* An integer value, and the number of digits in its textual
151 representation. */
152 typedef struct
153 {
154 bool negative;
155 long int value;
156 size_t digits;
157 } textint;
158
159 /* An entry in the lexical lookup table. */
160 typedef struct
161 {
162 char const *name;
163 int type;
164 int value;
165 } table;
166
167 /* Meridian: am, pm, or 24-hour style. */
168 enum { MERam, MERpm, MER24 };
169
170 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
171
172 /* Relative times. */
173 typedef struct
174 {
175 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
176 long int year;
177 long int month;
178 long int day;
179 long int hour;
180 long int minutes;
181 long_time_t seconds;
182 long int ns;
183 } relative_time;
184
185 #if HAVE_COMPOUND_LITERALS
186 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
187 #else
188 static relative_time const RELATIVE_TIME_0;
189 #endif
190
191 /* Information passed to and from the parser. */
192 typedef struct
193 {
194 /* The input string remaining to be parsed. */
195 const char *input;
196
197 /* N, if this is the Nth Tuesday. */
198 long int day_ordinal;
199
200 /* Day of week; Sunday is 0. */
201 int day_number;
202
203 /* tm_isdst flag for the local zone. */
204 int local_isdst;
205
206 /* Time zone, in minutes east of UTC. */
207 long int time_zone;
208
209 /* Style used for time. */
210 int meridian;
211
212 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
213 textint year;
214 long int month;
215 long int day;
216 long int hour;
217 long int minutes;
218 struct timespec seconds; /* includes nanoseconds */
219
220 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
221 relative_time rel;
222
223 /* Presence or counts of nonterminals of various flavors parsed so far. */
224 bool timespec_seen;
225 bool rels_seen;
226 size_t dates_seen;
227 size_t days_seen;
228 size_t local_zones_seen;
229 size_t dsts_seen;
230 size_t times_seen;
231 size_t zones_seen;
232
233 /* if true, print debugging output to stderr */
234 bool parse_datetime_debug;
235
236 /* which of the 'seen' parts has been printed when debugging */
237 size_t debug_dates_seen;
238 size_t debug_days_seen;
239 size_t debug_local_zones_seen;
240 size_t debug_dsts_seen;
241 size_t debug_times_seen;
242 size_t debug_zones_seen;
243
244 /* true if the user specified explicit ordinal day value, */
245 bool debug_ordinal_day_seen;
246
247 /* the default input timezone, set by TZ value */
248 long int debug_default_input_timezone;
249
250 /* Table of local time zone abbreviations, terminated by a null entry. */
251 table local_time_zone_table[3];
252 } parser_control;
253
254 union YYSTYPE;
255 static int yylex (union YYSTYPE *, parser_control *);
256 static int yyerror (parser_control const *, char const *);
257 static long int time_zone_hhmm (parser_control *, textint, long int);
258
259 /* Extract into *PC any date and time info from a string of digits
260 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
261 YYYY, ...). */
262 static void
digits_to_date_time(parser_control * pc,textint text_int)263 digits_to_date_time (parser_control *pc, textint text_int)
264 {
265 if (pc->dates_seen && ! pc->year.digits
266 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
267 pc->year = text_int;
268 else
269 {
270 if (4 < text_int.digits)
271 {
272 pc->dates_seen++;
273 pc->day = text_int.value % 100;
274 pc->month = (text_int.value / 100) % 100;
275 pc->year.value = text_int.value / 10000;
276 pc->year.digits = text_int.digits - 4;
277 }
278 else
279 {
280 pc->times_seen++;
281 if (text_int.digits <= 2)
282 {
283 pc->hour = text_int.value;
284 pc->minutes = 0;
285 }
286 else
287 {
288 pc->hour = text_int.value / 100;
289 pc->minutes = text_int.value % 100;
290 }
291 pc->seconds.tv_sec = 0;
292 pc->seconds.tv_nsec = 0;
293 pc->meridian = MER24;
294 }
295 }
296 }
297
298 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
299 static void
apply_relative_time(parser_control * pc,relative_time rel,int factor)300 apply_relative_time (parser_control *pc, relative_time rel, int factor)
301 {
302 pc->rel.ns += factor * rel.ns;
303 pc->rel.seconds += factor * rel.seconds;
304 pc->rel.minutes += factor * rel.minutes;
305 pc->rel.hour += factor * rel.hour;
306 pc->rel.day += factor * rel.day;
307 pc->rel.month += factor * rel.month;
308 pc->rel.year += factor * rel.year;
309 pc->rels_seen = true;
310 }
311
312 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
313 static void
set_hhmmss(parser_control * pc,long int hour,long int minutes,time_t sec,long int nsec)314 set_hhmmss (parser_control *pc, long int hour, long int minutes,
315 time_t sec, long int nsec)
316 {
317 pc->hour = hour;
318 pc->minutes = minutes;
319 pc->seconds.tv_sec = sec;
320 pc->seconds.tv_nsec = nsec;
321 }
322
323 /* returns a textual representation of the day ordinal/number values
324 in the parser_control struct (e.g. 'last wed', 'this tues', 'thu') */
325 static const char*
str_days(parser_control * pc,char * buffer,size_t n)326 str_days (parser_control *pc, char* /*output*/ buffer, size_t n)
327 {
328 /* TODO: use the relative_time_table[] for reverse lookup */
329 static const char* ordinal_values[] = {
330 "last",
331 "this",
332 "next/first",
333 "(SECOND)", /* SECOND is commented out in relative_time_table[] */
334 "third",
335 "fourth",
336 "fifth",
337 "sixth",
338 "seventh",
339 "eight",
340 "ninth",
341 "tenth",
342 "eleventh",
343 "twelfth"};
344
345 static const char* days_values[] = {
346 "Sun",
347 "Mon",
348 "Tue",
349 "Wed",
350 "Thu",
351 "Fri",
352 "Sat"
353 };
354
355 /* don't add an ordinal prefix if the user didn't specify it
356 (e.g., "this wed" vs "wed") */
357 if (pc->debug_ordinal_day_seen)
358 {
359 /* use word description of possible (e.g. -1 = last, 3 = third) */
360 if (pc->day_ordinal>=-1 && pc->day_ordinal <=12)
361 {
362 strncpy (buffer, ordinal_values[ pc->day_ordinal+1 ], n);
363 buffer[n-1]='\0';
364 }
365 else
366 {
367 snprintf (buffer,n,"%ld",pc->day_ordinal);
368 }
369 }
370 else
371 {
372 buffer[0] = '\0';
373 }
374
375 /* Add the day name */
376 if (pc->day_number>=0 && pc->day_number<=6)
377 {
378 size_t l = strlen (buffer);
379 if (l>0)
380 {
381 strncat (buffer," ",n-l);
382 ++l;
383 }
384 strncat (buffer,days_values[pc->day_number],n-l);
385 }
386 else
387 {
388 /* invalid day_number value - should never happen */
389 }
390 return buffer;
391 }
392
393 /* debugging: print the current time in the parser_control structure.
394 The parser will increment "*_seen" members for those which were parsed.
395 This function will print only newly seen parts. */
396 static void
debug_print_current_time(const char * item,parser_control * pc)397 debug_print_current_time (const char* item, parser_control *pc)
398 {
399 char tmp[100] = {0};
400 int space = 0; /* if true, add space delimiter */
401
402 if (!pc->parse_datetime_debug)
403 return;
404
405 /* no newline, more items printed below */
406 dbg_printf (_("parsed %s part: "), item);
407
408 if (pc->dates_seen != pc->debug_dates_seen)
409 {
410 /*TODO: use pc->year.negative? */
411 fprintf (stderr,"(Y-M-D) %04ld-%02ld-%02ld",
412 pc->year.value, pc->month, pc->day);
413 pc->debug_dates_seen = pc->dates_seen;
414 space = 1;
415 }
416
417 if (pc->times_seen != pc->debug_times_seen)
418 {
419 if (space)
420 fputc (' ',stderr);
421 fprintf (stderr,"%02ld:%02ld:%02ld",
422 pc->hour, pc->minutes, pc->seconds.tv_sec);
423 if (pc->seconds.tv_nsec!=0)
424 fprintf (stderr,"%09ld", pc->seconds.tv_nsec);
425 if (pc->meridian==MERpm)
426 fputs ("pm",stderr);
427
428 pc->debug_times_seen = pc->times_seen;
429 space = 1;
430 }
431
432 if (pc->days_seen != pc->debug_days_seen)
433 {
434 if (space)
435 fputc (' ',stderr);
436 fprintf (stderr,_("%s (day ordinal=%ld number=%d)"),
437 str_days (pc,tmp,sizeof (tmp)),
438 pc->day_ordinal, pc->day_number);
439 pc->debug_days_seen = pc->days_seen ;
440 space = 1;
441 }
442
443 if (pc->dsts_seen != pc->debug_dsts_seen)
444 {
445 if (space)
446 fputc (' ',stderr);
447 fprintf (stderr,_("is-dst=%d"), pc->local_isdst);
448 pc->dsts_seen = pc->debug_dsts_seen;
449 space = 1;
450 }
451
452 /* TODO: fix incorrect display of EST=2:08h? */
453 if (pc->zones_seen != pc->debug_zones_seen)
454 {
455 if (space)
456 fputc (' ',stderr);
457 fprintf (stderr,_("TZ=%+03d:%02d"), (int)(pc->time_zone/60),
458 abs ((int)pc->time_zone%60));
459 pc->debug_zones_seen = pc->zones_seen;
460 space = 1;
461 }
462
463 if (pc->local_zones_seen != pc->debug_local_zones_seen)
464 {
465 if (space)
466 fputc (' ',stderr);
467 fprintf (stderr,_("Local-TZ=%+03d:%02d"), (int)(pc->time_zone/60),
468 abs ((int)pc->time_zone%60));
469 pc->debug_local_zones_seen = pc->local_zones_seen;
470 space = 1;
471 }
472
473 if (pc->timespec_seen)
474 {
475 if (space)
476 fputc (' ',stderr);
477 fprintf (stderr,_("number of seconds: %ld"), pc->seconds.tv_sec);
478 }
479
480 fputc ('\n', stderr);
481 }
482
483 /* debugging: print the current relative values. */
484 static void
debug_print_relative_time(const char * item,const parser_control * pc)485 debug_print_relative_time (const char* item, const parser_control *pc)
486 {
487 int space = 0; /* if true, add space delimiter */
488
489 if (!pc->parse_datetime_debug)
490 return;
491
492 /* no newline, more items printed below */
493 dbg_printf (_("parsed %s part: "), item);
494
495 if (pc->rel.year==0 && pc->rel.month==0 && pc->rel.day==0
496 && pc->rel.hour==0 && pc->rel.minutes==00 && pc->rel.seconds == 0
497 && pc->rel.ns==0)
498 {
499 /* Special case: relative time of this/today/now */
500 fputs (_("today/this/now\n"),stderr);
501 return ;
502 }
503
504 #define PRINT_REL_PART(x,name) \
505 do { \
506 if ( (pc->rel.x) != 0 ) \
507 { \
508 if (space) \
509 fputc (' ',stderr); \
510 fprintf (stderr,"%+ld %s", pc->rel.x, name); \
511 space = 1; \
512 } \
513 } while (0)
514
515 PRINT_REL_PART (year,"year(s)");
516 PRINT_REL_PART (month,"month(s)");
517 PRINT_REL_PART (day,"day(s)");
518 PRINT_REL_PART (hour,"hour(s)");
519 PRINT_REL_PART (minutes,"minutes");
520 PRINT_REL_PART (seconds,"seconds");
521 PRINT_REL_PART (ns,"nanoseconds");
522
523 fputc ('\n',stderr);
524 }
525
526
527
528 %}
529
530 /* We want a reentrant parser, even if the TZ manipulation and the calls to
531 localtime and gmtime are not reentrant. */
532 %pure-parser
533 %parse-param { parser_control *pc }
534 %lex-param { parser_control *pc }
535
536 /* This grammar has 31 shift/reduce conflicts. */
537 %expect 31
538
539 %union
540 {
541 long int intval;
542 textint textintval;
543 struct timespec timespec;
544 relative_time rel;
545 }
546
547 %token <intval> tAGO
548 %token tDST
549
550 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
551 %token <intval> tDAY_UNIT tDAY_SHIFT
552
553 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
554 %token <intval> tMONTH tORDINAL tZONE
555
556 %token <textintval> tSNUMBER tUNUMBER
557 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
558
559 %type <intval> o_colon_minutes
560 %type <timespec> seconds signed_seconds unsigned_seconds
561
562 %type <rel> relunit relunit_snumber dayshift
563
564 %%
565
566 spec:
567 timespec
568 | items
569 ;
570
571 timespec:
572 '@' seconds
573 {
574 pc->seconds = $2;
575 pc->timespec_seen = true;
576 debug_print_current_time (_("number of seconds"), pc);
577 }
578 ;
579
580 items:
581 /* empty */
582 | items item
583 ;
584
585 item:
586 datetime
587 {
588 pc->times_seen++; pc->dates_seen++;
589 debug_print_current_time (_("datetime"), pc);
590 }
591 | time
592 {
593 pc->times_seen++;
594 debug_print_current_time (_("time"), pc);
595 }
596 | local_zone
597 {
598 pc->local_zones_seen++;
599 debug_print_current_time (_("local_zone"), pc);
600 }
601 | zone
602 {
603 pc->zones_seen++;
604 debug_print_current_time (_("zone"), pc);
605 }
606 | date
607 {
608 pc->dates_seen++;
609 debug_print_current_time (_("date"), pc);
610 }
611 | day
612 {
613 pc->days_seen++;
614 debug_print_current_time (_("day"), pc);
615 }
616 | rel
617 {
618 debug_print_relative_time (_("relative"), pc);
619 }
620 | number
621 {
622 debug_print_relative_time (_("number"), pc);
623 }
624 | hybrid
625 {
626 debug_print_relative_time (_("hybrid"), pc);
627 }
628 ;
629
630 datetime:
631 iso_8601_datetime
632 ;
633
634 iso_8601_datetime:
635 iso_8601_date 'T' iso_8601_time
636 ;
637
638 time:
639 tUNUMBER tMERIDIAN
640 {
641 set_hhmmss (pc, $1.value, 0, 0, 0);
642 pc->meridian = $2;
643 }
644 | tUNUMBER ':' tUNUMBER tMERIDIAN
645 {
646 set_hhmmss (pc, $1.value, $3.value, 0, 0);
647 pc->meridian = $4;
648 }
649 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
650 {
651 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
652 pc->meridian = $6;
653 }
654 | iso_8601_time
655 ;
656
657 iso_8601_time:
658 tUNUMBER zone_offset
659 {
660 set_hhmmss (pc, $1.value, 0, 0, 0);
661 pc->meridian = MER24;
662 }
663 | tUNUMBER ':' tUNUMBER o_zone_offset
664 {
665 set_hhmmss (pc, $1.value, $3.value, 0, 0);
666 pc->meridian = MER24;
667 }
668 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
669 {
670 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
671 pc->meridian = MER24;
672 }
673 ;
674
675 o_zone_offset:
676 /* empty */
677 | zone_offset
678 ;
679
680 zone_offset:
681 tSNUMBER o_colon_minutes
682 {
683 pc->zones_seen++;
684 pc->time_zone = time_zone_hhmm (pc, $1, $2);
685 }
686 ;
687
688 local_zone:
689 tLOCAL_ZONE
690 {
691 pc->local_isdst = $1;
692 pc->dsts_seen += (0 < $1);
693 }
694 | tLOCAL_ZONE tDST
695 {
696 pc->local_isdst = 1;
697 pc->dsts_seen += (0 < $1) + 1;
698 }
699 ;
700
701 /* Note 'T' is a special case, as it is used as the separator in ISO
702 8601 date and time of day representation. */
703 zone:
704 tZONE
705 { pc->time_zone = $1; }
706 | 'T'
707 { pc->time_zone = HOUR(7); }
708 | tZONE relunit_snumber
709 { pc->time_zone = $1;
710 apply_relative_time (pc, $2, 1); }
711 | 'T' relunit_snumber
712 { pc->time_zone = HOUR(7);
713 apply_relative_time (pc, $2, 1); }
714 | tZONE tSNUMBER o_colon_minutes
715 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
716 | tDAYZONE
717 { pc->time_zone = $1 + 60; }
718 | tZONE tDST
719 { pc->time_zone = $1 + 60; }
720 ;
721
722 day:
723 tDAY
724 {
725 pc->day_ordinal = 0;
726 pc->day_number = $1;
727 }
728 | tDAY ','
729 {
730 pc->day_ordinal = 0;
731 pc->day_number = $1;
732 }
733 | tORDINAL tDAY
734 {
735 pc->day_ordinal = $1;
736 pc->day_number = $2;
737 pc->debug_ordinal_day_seen = true;
738 }
739 | tUNUMBER tDAY
740 {
741 pc->day_ordinal = $1.value;
742 pc->day_number = $2;
743 pc->debug_ordinal_day_seen = true;
744 }
745 ;
746
747 date:
748 tUNUMBER '/' tUNUMBER
749 {
750 pc->month = $1.value;
751 pc->day = $3.value;
752 }
753 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
754 {
755 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
756 otherwise as MM/DD/YY.
757 The goal in recognizing YYYY/MM/DD is solely to support legacy
758 machine-generated dates like those in an RCS log listing. If
759 you want portability, use the ISO 8601 format. */
760 if (4 <= $1.digits)
761 {
762 if (pc->parse_datetime_debug)
763 dbg_printf (_("warning: value %ld has %"PRIuMAX" digits. " \
764 "Assuming YYYY/MM/DD\n"), $1.value, $1.digits);
765
766 pc->year = $1;
767 pc->month = $3.value;
768 pc->day = $5.value;
769 }
770 else
771 {
772 if (pc->parse_datetime_debug)
773 dbg_printf (_("warning: value %ld has less than 4 digits. " \
774 "Assuming MM/DD/YY[YY]\n"), $1.value);
775
776 pc->month = $1.value;
777 pc->day = $3.value;
778 pc->year = $5;
779 }
780 }
781 | tUNUMBER tMONTH tSNUMBER
782 {
783 /* e.g. 17-JUN-1992. */
784 pc->day = $1.value;
785 pc->month = $2;
786 pc->year.value = -$3.value;
787 pc->year.digits = $3.digits;
788 }
789 | tMONTH tSNUMBER tSNUMBER
790 {
791 /* e.g. JUN-17-1992. */
792 pc->month = $1;
793 pc->day = -$2.value;
794 pc->year.value = -$3.value;
795 pc->year.digits = $3.digits;
796 }
797 | tMONTH tUNUMBER
798 {
799 pc->month = $1;
800 pc->day = $2.value;
801 }
802 | tMONTH tUNUMBER ',' tUNUMBER
803 {
804 pc->month = $1;
805 pc->day = $2.value;
806 pc->year = $4;
807 }
808 | tUNUMBER tMONTH
809 {
810 pc->day = $1.value;
811 pc->month = $2;
812 }
813 | tUNUMBER tMONTH tUNUMBER
814 {
815 pc->day = $1.value;
816 pc->month = $2;
817 pc->year = $3;
818 }
819 | iso_8601_date
820 ;
821
822 iso_8601_date:
823 tUNUMBER tSNUMBER tSNUMBER
824 {
825 /* ISO 8601 format. YYYY-MM-DD. */
826 pc->year = $1;
827 pc->month = -$2.value;
828 pc->day = -$3.value;
829 }
830 ;
831
832 rel:
833 relunit tAGO
834 { apply_relative_time (pc, $1, $2); }
835 | relunit
836 { apply_relative_time (pc, $1, 1); }
837 | dayshift
838 { apply_relative_time (pc, $1, 1); }
839 ;
840
841 relunit:
842 tORDINAL tYEAR_UNIT
843 { $$ = RELATIVE_TIME_0; $$.year = $1; }
844 | tUNUMBER tYEAR_UNIT
845 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
846 | tYEAR_UNIT
847 { $$ = RELATIVE_TIME_0; $$.year = 1; }
848 | tORDINAL tMONTH_UNIT
849 { $$ = RELATIVE_TIME_0; $$.month = $1; }
850 | tUNUMBER tMONTH_UNIT
851 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
852 | tMONTH_UNIT
853 { $$ = RELATIVE_TIME_0; $$.month = 1; }
854 | tORDINAL tDAY_UNIT
855 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
856 | tUNUMBER tDAY_UNIT
857 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
858 | tDAY_UNIT
859 { $$ = RELATIVE_TIME_0; $$.day = $1; }
860 | tORDINAL tHOUR_UNIT
861 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
862 | tUNUMBER tHOUR_UNIT
863 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
864 | tHOUR_UNIT
865 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
866 | tORDINAL tMINUTE_UNIT
867 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
868 | tUNUMBER tMINUTE_UNIT
869 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
870 | tMINUTE_UNIT
871 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
872 | tORDINAL tSEC_UNIT
873 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
874 | tUNUMBER tSEC_UNIT
875 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
876 | tSDECIMAL_NUMBER tSEC_UNIT
877 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
878 | tUDECIMAL_NUMBER tSEC_UNIT
879 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
880 | tSEC_UNIT
881 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
882 | relunit_snumber
883 ;
884
885 relunit_snumber:
886 tSNUMBER tYEAR_UNIT
887 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
888 | tSNUMBER tMONTH_UNIT
889 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
890 | tSNUMBER tDAY_UNIT
891 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
892 | tSNUMBER tHOUR_UNIT
893 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
894 | tSNUMBER tMINUTE_UNIT
895 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
896 | tSNUMBER tSEC_UNIT
897 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
898 ;
899
900 dayshift:
901 tDAY_SHIFT
902 { $$ = RELATIVE_TIME_0; $$.day = $1; }
903 ;
904
905 seconds: signed_seconds | unsigned_seconds;
906
907 signed_seconds:
908 tSDECIMAL_NUMBER
909 | tSNUMBER
910 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
911 ;
912
913 unsigned_seconds:
914 tUDECIMAL_NUMBER
915 | tUNUMBER
916 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
917 ;
918
919 number:
920 tUNUMBER
921 { digits_to_date_time (pc, $1); }
922 ;
923
924 hybrid:
925 tUNUMBER relunit_snumber
926 {
927 /* Hybrid all-digit and relative offset, so that we accept e.g.,
928 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
929 digits_to_date_time (pc, $1);
930 apply_relative_time (pc, $2, 1);
931 }
932 ;
933
934 o_colon_minutes:
935 /* empty */
936 { $$ = -1; }
937 | ':' tUNUMBER
938 { $$ = $2.value; }
939 ;
940
941 %%
942
943 static table const meridian_table[] =
944 {
945 { "AM", tMERIDIAN, MERam },
946 { "A.M.", tMERIDIAN, MERam },
947 { "PM", tMERIDIAN, MERpm },
948 { "P.M.", tMERIDIAN, MERpm },
949 { NULL, 0, 0 }
950 };
951
952 static table const dst_table[] =
953 {
954 { "DST", tDST, 0 }
955 };
956
957 static table const month_and_day_table[] =
958 {
959 { "JANUARY", tMONTH, 1 },
960 { "FEBRUARY", tMONTH, 2 },
961 { "MARCH", tMONTH, 3 },
962 { "APRIL", tMONTH, 4 },
963 { "MAY", tMONTH, 5 },
964 { "JUNE", tMONTH, 6 },
965 { "JULY", tMONTH, 7 },
966 { "AUGUST", tMONTH, 8 },
967 { "SEPTEMBER",tMONTH, 9 },
968 { "SEPT", tMONTH, 9 },
969 { "OCTOBER", tMONTH, 10 },
970 { "NOVEMBER", tMONTH, 11 },
971 { "DECEMBER", tMONTH, 12 },
972 { "SUNDAY", tDAY, 0 },
973 { "MONDAY", tDAY, 1 },
974 { "TUESDAY", tDAY, 2 },
975 { "TUES", tDAY, 2 },
976 { "WEDNESDAY",tDAY, 3 },
977 { "WEDNES", tDAY, 3 },
978 { "THURSDAY", tDAY, 4 },
979 { "THUR", tDAY, 4 },
980 { "THURS", tDAY, 4 },
981 { "FRIDAY", tDAY, 5 },
982 { "SATURDAY", tDAY, 6 },
983 { NULL, 0, 0 }
984 };
985
986 static table const time_units_table[] =
987 {
988 { "YEAR", tYEAR_UNIT, 1 },
989 { "MONTH", tMONTH_UNIT, 1 },
990 { "FORTNIGHT",tDAY_UNIT, 14 },
991 { "WEEK", tDAY_UNIT, 7 },
992 { "DAY", tDAY_UNIT, 1 },
993 { "HOUR", tHOUR_UNIT, 1 },
994 { "MINUTE", tMINUTE_UNIT, 1 },
995 { "MIN", tMINUTE_UNIT, 1 },
996 { "SECOND", tSEC_UNIT, 1 },
997 { "SEC", tSEC_UNIT, 1 },
998 { NULL, 0, 0 }
999 };
1000
1001 /* Assorted relative-time words. */
1002 static table const relative_time_table[] =
1003 {
1004 { "TOMORROW", tDAY_SHIFT, 1 },
1005 { "YESTERDAY",tDAY_SHIFT, -1 },
1006 { "TODAY", tDAY_SHIFT, 0 },
1007 { "NOW", tDAY_SHIFT, 0 },
1008 { "LAST", tORDINAL, -1 },
1009 { "THIS", tORDINAL, 0 },
1010 { "NEXT", tORDINAL, 1 },
1011 { "FIRST", tORDINAL, 1 },
1012 /*{ "SECOND", tORDINAL, 2 }, */
1013 { "THIRD", tORDINAL, 3 },
1014 { "FOURTH", tORDINAL, 4 },
1015 { "FIFTH", tORDINAL, 5 },
1016 { "SIXTH", tORDINAL, 6 },
1017 { "SEVENTH", tORDINAL, 7 },
1018 { "EIGHTH", tORDINAL, 8 },
1019 { "NINTH", tORDINAL, 9 },
1020 { "TENTH", tORDINAL, 10 },
1021 { "ELEVENTH", tORDINAL, 11 },
1022 { "TWELFTH", tORDINAL, 12 },
1023 { "AGO", tAGO, -1 },
1024 { "HENCE", tAGO, 1 },
1025 { NULL, 0, 0 }
1026 };
1027
1028 /* The universal time zone table. These labels can be used even for
1029 time stamps that would not otherwise be valid, e.g., GMT time
1030 stamps in London during summer. */
1031 static table const universal_time_zone_table[] =
1032 {
1033 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
1034 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
1035 { "UTC", tZONE, HOUR ( 0) },
1036 { NULL, 0, 0 }
1037 };
1038
1039 /* The time zone table. This table is necessarily incomplete, as time
1040 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
1041 as Eastern time in Australia, not as US Eastern Standard Time.
1042 You cannot rely on parse_datetime to handle arbitrary time zone
1043 abbreviations; use numeric abbreviations like "-0500" instead. */
1044 static table const time_zone_table[] =
1045 {
1046 { "WET", tZONE, HOUR ( 0) }, /* Western European */
1047 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
1048 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
1049 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
1050 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
1051 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
1052 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
1053 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
1054 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
1055 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
1056 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
1057 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
1058 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
1059 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
1060 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
1061 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
1062 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
1063 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
1064 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
1065 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
1066 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
1067 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
1068 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
1069 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
1070 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
1071 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
1072 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
1073 { "CET", tZONE, HOUR ( 1) }, /* Central European */
1074 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
1075 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
1076 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
1077 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
1078 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
1079 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
1080 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
1081 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
1082 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
1083 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
1084 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
1085 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
1086 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
1087 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
1088 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
1089 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
1090 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
1091 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
1092 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
1093 { NULL, 0, 0 }
1094 };
1095
1096 /* Military time zone table.
1097
1098 Note 'T' is a special case, as it is used as the separator in ISO
1099 8601 date and time of day representation. */
1100 static table const military_table[] =
1101 {
1102 { "A", tZONE, -HOUR ( 1) },
1103 { "B", tZONE, -HOUR ( 2) },
1104 { "C", tZONE, -HOUR ( 3) },
1105 { "D", tZONE, -HOUR ( 4) },
1106 { "E", tZONE, -HOUR ( 5) },
1107 { "F", tZONE, -HOUR ( 6) },
1108 { "G", tZONE, -HOUR ( 7) },
1109 { "H", tZONE, -HOUR ( 8) },
1110 { "I", tZONE, -HOUR ( 9) },
1111 { "K", tZONE, -HOUR (10) },
1112 { "L", tZONE, -HOUR (11) },
1113 { "M", tZONE, -HOUR (12) },
1114 { "N", tZONE, HOUR ( 1) },
1115 { "O", tZONE, HOUR ( 2) },
1116 { "P", tZONE, HOUR ( 3) },
1117 { "Q", tZONE, HOUR ( 4) },
1118 { "R", tZONE, HOUR ( 5) },
1119 { "S", tZONE, HOUR ( 6) },
1120 { "T", 'T', 0 },
1121 { "U", tZONE, HOUR ( 8) },
1122 { "V", tZONE, HOUR ( 9) },
1123 { "W", tZONE, HOUR (10) },
1124 { "X", tZONE, HOUR (11) },
1125 { "Y", tZONE, HOUR (12) },
1126 { "Z", tZONE, HOUR ( 0) },
1127 { NULL, 0, 0 }
1128 };
1129
1130
1131
1132 /* Convert a time zone expressed as HH:MM into an integer count of
1133 minutes. If MM is negative, then S is of the form HHMM and needs
1134 to be picked apart; otherwise, S is of the form HH. As specified in
1135 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
1136 only valid TZ range, and consider first two digits as hours, if no
1137 minutes specified. */
1138
1139 static long int
time_zone_hhmm(parser_control * pc,textint s,long int mm)1140 time_zone_hhmm (parser_control *pc, textint s, long int mm)
1141 {
1142 long int n_minutes;
1143
1144 /* If the length of S is 1 or 2 and no minutes are specified,
1145 interpret it as a number of hours. */
1146 if (s.digits <= 2 && mm < 0)
1147 s.value *= 100;
1148
1149 if (mm < 0)
1150 n_minutes = (s.value / 100) * 60 + s.value % 100;
1151 else
1152 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
1153
1154 /* If the absolute number of minutes is larger than 24 hours,
1155 arrange to reject it by incrementing pc->zones_seen. Thus,
1156 we allow only values in the range UTC-24:00 to UTC+24:00. */
1157 if (24 * 60 < abs (n_minutes))
1158 pc->zones_seen++;
1159
1160 return n_minutes;
1161 }
1162
1163 static int
to_hour(long int hours,int meridian)1164 to_hour (long int hours, int meridian)
1165 {
1166 switch (meridian)
1167 {
1168 default: /* Pacify GCC. */
1169 case MER24:
1170 return 0 <= hours && hours < 24 ? hours : -1;
1171 case MERam:
1172 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
1173 case MERpm:
1174 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
1175 }
1176 }
1177
1178 static long int
to_year(textint textyear,bool debug)1179 to_year (textint textyear, bool debug)
1180 {
1181 long int year = textyear.value;
1182
1183 if (year < 0)
1184 year = -year;
1185
1186 /* XPG4 suggests that years 00-68 map to 2000-2068, and
1187 years 69-99 map to 1969-1999. */
1188 else if (textyear.digits == 2)
1189 {
1190 year += year < 69 ? 2000 : 1900;
1191 if (debug)
1192 dbg_printf (_("warning: adjusting year value %ld to %ld\n"),
1193 textyear.value, year);
1194 }
1195
1196 return year;
1197 }
1198
1199 static table const * _GL_ATTRIBUTE_PURE
lookup_zone(parser_control const * pc,char const * name)1200 lookup_zone (parser_control const *pc, char const *name)
1201 {
1202 table const *tp;
1203
1204 for (tp = universal_time_zone_table; tp->name; tp++)
1205 if (strcmp (name, tp->name) == 0)
1206 return tp;
1207
1208 /* Try local zone abbreviations before those in time_zone_table, as
1209 the local ones are more likely to be right. */
1210 for (tp = pc->local_time_zone_table; tp->name; tp++)
1211 if (strcmp (name, tp->name) == 0)
1212 return tp;
1213
1214 for (tp = time_zone_table; tp->name; tp++)
1215 if (strcmp (name, tp->name) == 0)
1216 return tp;
1217
1218 return NULL;
1219 }
1220
1221 #if ! HAVE_TM_GMTOFF
1222 /* Yield the difference between *A and *B,
1223 measured in seconds, ignoring leap seconds.
1224 The body of this function is taken directly from the GNU C Library;
1225 see src/strftime.c. */
1226 static long int
tm_diff(struct tm const * a,struct tm const * b)1227 tm_diff (struct tm const *a, struct tm const *b)
1228 {
1229 /* Compute intervening leap days correctly even if year is negative.
1230 Take care to avoid int overflow in leap day calculations. */
1231 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
1232 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
1233 int a100 = a4 / 25 - (a4 % 25 < 0);
1234 int b100 = b4 / 25 - (b4 % 25 < 0);
1235 int a400 = SHR (a100, 2);
1236 int b400 = SHR (b100, 2);
1237 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
1238 long int ayear = a->tm_year;
1239 long int years = ayear - b->tm_year;
1240 long int days = (365 * years + intervening_leap_days
1241 + (a->tm_yday - b->tm_yday));
1242 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1243 + (a->tm_min - b->tm_min))
1244 + (a->tm_sec - b->tm_sec));
1245 }
1246 #endif /* ! HAVE_TM_GMTOFF */
1247
1248 static table const *
lookup_word(parser_control const * pc,char * word)1249 lookup_word (parser_control const *pc, char *word)
1250 {
1251 char *p;
1252 char *q;
1253 size_t wordlen;
1254 table const *tp;
1255 bool period_found;
1256 bool abbrev;
1257
1258 /* Make it uppercase. */
1259 for (p = word; *p; p++)
1260 {
1261 unsigned char ch = *p;
1262 *p = c_toupper (ch);
1263 }
1264
1265 for (tp = meridian_table; tp->name; tp++)
1266 if (strcmp (word, tp->name) == 0)
1267 return tp;
1268
1269 /* See if we have an abbreviation for a month. */
1270 wordlen = strlen (word);
1271 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
1272
1273 for (tp = month_and_day_table; tp->name; tp++)
1274 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
1275 return tp;
1276
1277 if ((tp = lookup_zone (pc, word)))
1278 return tp;
1279
1280 if (strcmp (word, dst_table[0].name) == 0)
1281 return dst_table;
1282
1283 for (tp = time_units_table; tp->name; tp++)
1284 if (strcmp (word, tp->name) == 0)
1285 return tp;
1286
1287 /* Strip off any plural and try the units table again. */
1288 if (word[wordlen - 1] == 'S')
1289 {
1290 word[wordlen - 1] = '\0';
1291 for (tp = time_units_table; tp->name; tp++)
1292 if (strcmp (word, tp->name) == 0)
1293 return tp;
1294 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
1295 }
1296
1297 for (tp = relative_time_table; tp->name; tp++)
1298 if (strcmp (word, tp->name) == 0)
1299 return tp;
1300
1301 /* Military time zones. */
1302 if (wordlen == 1)
1303 for (tp = military_table; tp->name; tp++)
1304 if (word[0] == tp->name[0])
1305 return tp;
1306
1307 /* Drop out any periods and try the time zone table again. */
1308 for (period_found = false, p = q = word; (*p = *q); q++)
1309 if (*q == '.')
1310 period_found = true;
1311 else
1312 p++;
1313 if (period_found && (tp = lookup_zone (pc, word)))
1314 return tp;
1315
1316 return NULL;
1317 }
1318
1319 static int
yylex(union YYSTYPE * lvalp,parser_control * pc)1320 yylex (union YYSTYPE *lvalp, parser_control *pc)
1321 {
1322 unsigned char c;
1323 size_t count;
1324
1325 for (;;)
1326 {
1327 while (c = *pc->input, c_isspace (c))
1328 pc->input++;
1329
1330 if (ISDIGIT (c) || c == '-' || c == '+')
1331 {
1332 char const *p;
1333 int sign;
1334 unsigned long int value;
1335 if (c == '-' || c == '+')
1336 {
1337 sign = c == '-' ? -1 : 1;
1338 while (c = *++pc->input, c_isspace (c))
1339 continue;
1340 if (! ISDIGIT (c))
1341 /* skip the '-' sign */
1342 continue;
1343 }
1344 else
1345 sign = 0;
1346 p = pc->input;
1347 for (value = 0; ; value *= 10)
1348 {
1349 unsigned long int value1 = value + (c - '0');
1350 if (value1 < value)
1351 return '?';
1352 value = value1;
1353 c = *++p;
1354 if (! ISDIGIT (c))
1355 break;
1356 if (ULONG_MAX / 10 < value)
1357 return '?';
1358 }
1359 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1360 {
1361 time_t s;
1362 int ns;
1363 int digits;
1364 unsigned long int value1;
1365
1366 /* Check for overflow when converting value to time_t. */
1367 if (sign < 0)
1368 {
1369 s = - value;
1370 if (0 < s)
1371 return '?';
1372 value1 = -s;
1373 }
1374 else
1375 {
1376 s = value;
1377 if (s < 0)
1378 return '?';
1379 value1 = s;
1380 }
1381 if (value != value1)
1382 return '?';
1383
1384 /* Accumulate fraction, to ns precision. */
1385 p++;
1386 ns = *p++ - '0';
1387 for (digits = 2; digits <= LOG10_BILLION; digits++)
1388 {
1389 ns *= 10;
1390 if (ISDIGIT (*p))
1391 ns += *p++ - '0';
1392 }
1393
1394 /* Skip excess digits, truncating toward -Infinity. */
1395 if (sign < 0)
1396 for (; ISDIGIT (*p); p++)
1397 if (*p != '0')
1398 {
1399 ns++;
1400 break;
1401 }
1402 while (ISDIGIT (*p))
1403 p++;
1404
1405 /* Adjust to the timespec convention, which is that
1406 tv_nsec is always a positive offset even if tv_sec is
1407 negative. */
1408 if (sign < 0 && ns)
1409 {
1410 s--;
1411 if (! (s < 0))
1412 return '?';
1413 ns = BILLION - ns;
1414 }
1415
1416 lvalp->timespec.tv_sec = s;
1417 lvalp->timespec.tv_nsec = ns;
1418 pc->input = p;
1419 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1420 }
1421 else
1422 {
1423 lvalp->textintval.negative = sign < 0;
1424 if (sign < 0)
1425 {
1426 lvalp->textintval.value = - value;
1427 if (0 < lvalp->textintval.value)
1428 return '?';
1429 }
1430 else
1431 {
1432 lvalp->textintval.value = value;
1433 if (lvalp->textintval.value < 0)
1434 return '?';
1435 }
1436 lvalp->textintval.digits = p - pc->input;
1437 pc->input = p;
1438 return sign ? tSNUMBER : tUNUMBER;
1439 }
1440 }
1441
1442 if (c_isalpha (c))
1443 {
1444 char buff[20];
1445 char *p = buff;
1446 table const *tp;
1447
1448 do
1449 {
1450 if (p < buff + sizeof buff - 1)
1451 *p++ = c;
1452 c = *++pc->input;
1453 }
1454 while (c_isalpha (c) || c == '.');
1455
1456 *p = '\0';
1457 tp = lookup_word (pc, buff);
1458 if (! tp)
1459 {
1460 if (pc->parse_datetime_debug)
1461 dbg_printf (_("error: unknown word '%s'\n"), buff);
1462 return '?';
1463 }
1464 lvalp->intval = tp->value;
1465 return tp->type;
1466 }
1467
1468 if (c != '(')
1469 return to_uchar (*pc->input++);
1470
1471 count = 0;
1472 do
1473 {
1474 c = *pc->input++;
1475 if (c == '\0')
1476 return c;
1477 if (c == '(')
1478 count++;
1479 else if (c == ')')
1480 count--;
1481 }
1482 while (count != 0);
1483 }
1484 }
1485
1486 /* Do nothing if the parser reports an error. */
1487 static int
yyerror(parser_control const * pc _GL_UNUSED,char const * s _GL_UNUSED)1488 yyerror (parser_control const *pc _GL_UNUSED,
1489 char const *s _GL_UNUSED)
1490 {
1491 return 0;
1492 }
1493
1494 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1495 passing it to mktime, return true if it's OK that mktime returned T.
1496 It's not OK if *TM0 has out-of-range members. */
1497
1498 static bool
mktime_ok(struct tm const * tm0,struct tm const * tm1,time_t t)1499 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1500 {
1501 if (t == (time_t) -1)
1502 {
1503 /* Guard against falsely reporting an error when parsing a time
1504 stamp that happens to equal (time_t) -1, on a host that
1505 supports such a time stamp. */
1506 tm1 = localtime (&t);
1507 if (!tm1)
1508 return false;
1509 }
1510
1511 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1512 | (tm0->tm_min ^ tm1->tm_min)
1513 | (tm0->tm_hour ^ tm1->tm_hour)
1514 | (tm0->tm_mday ^ tm1->tm_mday)
1515 | (tm0->tm_mon ^ tm1->tm_mon)
1516 | (tm0->tm_year ^ tm1->tm_year));
1517 }
1518
1519 /* A reasonable upper bound for the size of ordinary TZ strings.
1520 Use heap allocation if TZ's length exceeds this. */
1521 enum { TZBUFSIZE = 100 };
1522
1523 /* A reasonable upper bound for the buffer used in debug print outs.
1524 see days_to_name(), debug_strftime() and debug_mktime_not_ok() */
1525 enum { DBGBUFSIZE = 100 };
1526
1527 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1528 otherwise. */
1529 static char *
get_tz(char tzbuf[TZBUFSIZE])1530 get_tz (char tzbuf[TZBUFSIZE])
1531 {
1532 char *tz = getenv ("TZ");
1533 if (tz)
1534 {
1535 size_t tzsize = strlen (tz) + 1;
1536 tz = (tzsize <= TZBUFSIZE
1537 ? memcpy (tzbuf, tz, tzsize)
1538 : xmemdup (tz, tzsize));
1539 }
1540 return tz;
1541 }
1542
1543 /* debugging: format a 'struct tm' into a buffer, taking the parser's
1544 timezone information into account (if pc!=NULL). */
1545 static const char*
debug_strfdatetime(const struct tm * tm,const parser_control * pc,char * buf,size_t n)1546 debug_strfdatetime (const struct tm *tm, const parser_control *pc,
1547 char* /*output*/ buf, size_t n)
1548 {
1549 /* TODO:
1550 1. find an optimal way to print date string in a clear and unambiguous
1551 format. Currently, always add '(Y-M-D)' prefix.
1552 Consider '2016y01m10d' or 'year(2016) month(01) day(10)'.
1553
1554 If the user needs debug printing, it means he/she already having
1555 issues with the parsing - better to avoid formats that could
1556 be mis-interpreted (e.g. just YYYY-MM-DD).
1557
1558 2. Can strftime be used instead?
1559 depends if it is portable and can print invalid dates on all systems.
1560
1561 3. Print timezone information ?
1562
1563 4. Print DST information ?
1564
1565 5. Print nanosecond information ?
1566
1567 NOTE:
1568 Printed date/time values might not be valid, e.g. '2016-02-31'
1569 or '2016-19-2016' . These are the values as parsed from the user
1570 string, before validation.
1571 */
1572 int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
1573
1574 /* if parser_control information was provided (for timezone),
1575 and there's enough space in the buffer - add timezone info */
1576 if (pc != NULL && ((n-m)>0))
1577 {
1578 const long int tz = (pc->zones_seen || pc->local_zones_seen)
1579 ? pc->time_zone
1580 : pc->debug_default_input_timezone;
1581 snprintf (&buf[m],n-m," TZ=%+03d:%02d", (int)(tz/60), abs ((int)tz)%60);
1582 }
1583 return buf;
1584 }
1585
1586 static const char*
debug_strfdate(const struct tm * tm,char * buf,size_t n)1587 debug_strfdate (const struct tm *tm, char* /*output*/ buf, size_t n)
1588 {
1589 snprintf (buf,n,"(Y-M-D) %04d-%02d-%02d",
1590 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
1591 return buf;
1592 }
1593
1594 static const char*
debug_strftime(const struct tm * tm,char * buf,size_t n)1595 debug_strftime (const struct tm *tm, char* /*output*/ buf, size_t n)
1596 {
1597 snprintf (buf,n,"%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1598 return buf;
1599 }
1600
1601 /* If 'mktime_ok()' failed, display the failed time values,
1602 and provide possible hints. Example output:
1603
1604 date: error: invalid date/time value:
1605 date: user provided time: '(Y-M-D) 2006-04-02 02:45:00'
1606 date: normalized time: '(Y-M-D) 2006-04-02 03:45:00'
1607 date: __
1608 date: possible reasons:
1609 date: non-existing due to daylight-saving time;
1610 date: numeric values overflow;
1611 date: missing timezone;
1612 */
1613 static void
debug_mktime_not_ok(struct tm const * tm0,struct tm const * tm1,const parser_control * pc,bool time_zone_seen)1614 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1615 const parser_control *pc, bool time_zone_seen)
1616 {
1617 /* TODO: handle t==-1 (as in 'mktime_ok') */
1618 char tmp[DBGBUFSIZE];
1619 int i;
1620 const bool eq_sec = (tm0->tm_sec == tm1->tm_sec);
1621 const bool eq_min = (tm0->tm_min == tm1->tm_min);
1622 const bool eq_hour = (tm0->tm_hour == tm1->tm_hour);
1623 const bool eq_mday = (tm0->tm_mday == tm1->tm_mday);
1624 const bool eq_month = (tm0->tm_mon == tm1->tm_mon);
1625 const bool eq_year = (tm0->tm_year == tm1->tm_year);
1626
1627 const bool dst_shift = eq_sec && eq_min && !eq_hour
1628 && eq_mday && eq_month && eq_year;
1629
1630 if (!pc->parse_datetime_debug)
1631 return;
1632
1633 dbg_printf (_("error: invalid date/time value:\n"));
1634 dbg_printf (_(" user provided time: '%s'\n"),
1635 debug_strfdatetime (tm0, pc, tmp, sizeof (tmp)));
1636 dbg_printf (_(" normalized time: '%s'\n"),
1637 debug_strfdatetime (tm1, pc, tmp, sizeof (tmp)));
1638 /* NOTEs: the format must be aligned with debug_strfdatetime() and the two
1639 DEBUG statements above. this string is not translated. */
1640 i = snprintf (tmp, sizeof(tmp),
1641 " %4s %2s %2s %2s %2s %2s",
1642 eq_year?"":"----", eq_month?"":"--", eq_mday?"":"--",
1643 eq_hour?"":"--", eq_min?"":"--", eq_sec?"":"--");
1644 /* Trim trailing whitespace */
1645 if ((i>0) && (i<sizeof(tmp)))
1646 {
1647 while ((i>0) && (tmp[i-1]==' '))
1648 --i;
1649 tmp[i] = '\0';
1650 }
1651 dbg_printf ("%s\n", tmp);
1652
1653 dbg_printf (_(" possible reasons:\n"));
1654 if (dst_shift)
1655 dbg_printf (_(" non-existing due to daylight-saving time;\n"));
1656 if (!eq_mday && !eq_month)
1657 dbg_printf (_(" invalid day/month combination;\n"));
1658 dbg_printf (_(" numeric values overflow;\n"));
1659 dbg_printf (" %s\n",time_zone_seen?_("incorrect timezone")
1660 :_("missing timezone"));
1661 }
1662
1663
1664 /* Returns the effective local timezone, in minutes. */
1665 static long int
get_effective_timezone(void)1666 get_effective_timezone (void)
1667 {
1668 /* TODO: check for failures */
1669 const time_t z = 0;
1670 time_t lz ;
1671 struct tm *ltm;
1672 ltm = localtime (&z);
1673 lz = timegm (ltm)/60;
1674 return (long int)lz;
1675 }
1676
1677 /* The original interface: run with debug=false */
1678 bool
parse_datetime(struct timespec * result,char const * p,struct timespec const * now)1679 parse_datetime (struct timespec *result, char const *p,
1680 struct timespec const *now)
1681 {
1682 return parse_datetime2 (result, p, now, 0);
1683 }
1684
1685 /* Parse a date/time string, storing the resulting time value into *RESULT.
1686 The string itself is pointed to by P. Return true if successful.
1687 P can be an incomplete or relative time specification; if so, use
1688 *NOW as the basis for the returned time. */
1689 bool
parse_datetime2(struct timespec * result,char const * p,struct timespec const * now,unsigned int flags)1690 parse_datetime2 (struct timespec *result, char const *p,
1691 struct timespec const *now, unsigned int flags)
1692 {
1693 time_t Start;
1694 long int Start_ns;
1695 struct tm const *tmp;
1696 struct tm tm;
1697 struct tm tm0;
1698 parser_control pc;
1699 struct timespec gettime_buffer;
1700 unsigned char c;
1701 bool tz_was_altered = false;
1702 char *tz0 = NULL;
1703 char tz0buf[TZBUFSIZE];
1704 bool ok = true;
1705 char dbg_ord[DBGBUFSIZE];
1706 char dbg_tm[DBGBUFSIZE];
1707 char const *input_sentinel = p + strlen (p);
1708
1709 if (! now)
1710 {
1711 gettime (&gettime_buffer);
1712 now = &gettime_buffer;
1713 }
1714
1715 Start = now->tv_sec;
1716 Start_ns = now->tv_nsec;
1717
1718 tmp = localtime (&now->tv_sec);
1719 if (! tmp)
1720 return false;
1721
1722 while (c = *p, c_isspace (c))
1723 p++;
1724
1725 if (strncmp (p, "TZ=\"", 4) == 0)
1726 {
1727 char const *tzbase = p + 4;
1728 size_t tzsize = 1;
1729 char const *s;
1730
1731 for (s = tzbase; *s; s++, tzsize++)
1732 if (*s == '\\')
1733 {
1734 s++;
1735 if (! (*s == '\\' || *s == '"'))
1736 break;
1737 }
1738 else if (*s == '"')
1739 {
1740 char *z;
1741 char *tz1;
1742 char tz1buf[TZBUFSIZE];
1743 bool large_tz = TZBUFSIZE < tzsize;
1744 bool setenv_ok;
1745 tz0 = get_tz (tz0buf);
1746 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1747 for (s = tzbase; *s != '"'; s++)
1748 *z++ = *(s += *s == '\\');
1749 *z = '\0';
1750 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1751 if (large_tz)
1752 free (tz1);
1753 if (!setenv_ok)
1754 goto fail;
1755 tz_was_altered = true;
1756
1757 p = s + 1;
1758 while (c = *p, c_isspace (c))
1759 p++;
1760
1761 break;
1762 }
1763 }
1764
1765 /* As documented, be careful to treat the empty string just like
1766 a date string of "0". Without this, an empty string would be
1767 declared invalid when parsed during a DST transition. */
1768 if (*p == '\0')
1769 p = "0";
1770
1771 pc.input = p;
1772 pc.year.value = tmp->tm_year;
1773 pc.year.value += TM_YEAR_BASE;
1774 pc.year.digits = 0;
1775 pc.month = tmp->tm_mon + 1;
1776 pc.day = tmp->tm_mday;
1777 pc.hour = tmp->tm_hour;
1778 pc.minutes = tmp->tm_min;
1779 pc.seconds.tv_sec = tmp->tm_sec;
1780 pc.seconds.tv_nsec = Start_ns;
1781 tm.tm_isdst = tmp->tm_isdst;
1782
1783 pc.meridian = MER24;
1784 pc.rel = RELATIVE_TIME_0;
1785 pc.timespec_seen = false;
1786 pc.rels_seen = false;
1787 pc.dates_seen = 0;
1788 pc.days_seen = 0;
1789 pc.times_seen = 0;
1790 pc.local_zones_seen = 0;
1791 pc.dsts_seen = 0;
1792 pc.zones_seen = 0;
1793 pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG)!=0;
1794 pc.debug_dates_seen = 0;
1795 pc.debug_days_seen = 0;
1796 pc.debug_times_seen = 0;
1797 pc.debug_local_zones_seen = 0;
1798 pc.debug_dsts_seen = 0;
1799 pc.debug_zones_seen = 0;
1800 pc.debug_ordinal_day_seen = false;
1801 pc.debug_default_input_timezone = 0;
1802
1803 #if HAVE_STRUCT_TM_TM_ZONE
1804 pc.local_time_zone_table[0].name = tmp->tm_zone;
1805 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1806 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1807 pc.local_time_zone_table[1].name = NULL;
1808
1809 /* Probe the names used in the next three calendar quarters, looking
1810 for a tm_isdst different from the one we already have. */
1811 {
1812 int quarter;
1813 for (quarter = 1; quarter <= 3; quarter++)
1814 {
1815 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1816 struct tm const *probe_tm = localtime (&probe);
1817 if (probe_tm && probe_tm->tm_zone
1818 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1819 {
1820 {
1821 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1822 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1823 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1824 pc.local_time_zone_table[2].name = NULL;
1825 }
1826 break;
1827 }
1828 }
1829 }
1830 #else
1831 #if HAVE_TZNAME
1832 {
1833 # if !HAVE_DECL_TZNAME
1834 extern char *tzname[];
1835 # endif
1836 int i;
1837 for (i = 0; i < 2; i++)
1838 {
1839 pc.local_time_zone_table[i].name = tzname[i];
1840 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1841 pc.local_time_zone_table[i].value = i;
1842 }
1843 pc.local_time_zone_table[i].name = NULL;
1844 }
1845 #else
1846 pc.local_time_zone_table[0].name = NULL;
1847 #endif
1848 #endif
1849
1850 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1851 && ! strcmp (pc.local_time_zone_table[0].name,
1852 pc.local_time_zone_table[1].name))
1853 {
1854 /* This locale uses the same abbreviation for standard and
1855 daylight times. So if we see that abbreviation, we don't
1856 know whether it's daylight time. */
1857 pc.local_time_zone_table[0].value = -1;
1858 pc.local_time_zone_table[1].name = NULL;
1859 }
1860
1861 pc.debug_default_input_timezone = get_effective_timezone ();
1862
1863 if (yyparse (&pc) != 0)
1864 {
1865 if (pc.parse_datetime_debug)
1866 {
1867 if (input_sentinel <= pc.input)
1868 dbg_printf (_("error: parsing failed\n"), pc.input);
1869 else
1870 {
1871 dbg_printf (_("error: parsing failed, stopped at '%s'\n"),
1872 pc.input);
1873 }
1874 }
1875 goto fail;
1876 }
1877
1878 /* determine effective timezone source */
1879 if (pc.parse_datetime_debug)
1880 {
1881 long int tz = pc.debug_default_input_timezone;
1882 const char* tz_env;
1883 const char* tz_src;
1884
1885 if (pc.timespec_seen)
1886 {
1887 tz = 0 ;
1888 tz_src = _("'@timespec' - always UTC0");
1889 }
1890 else if (pc.local_zones_seen || pc.zones_seen)
1891 {
1892 tz = pc.time_zone;
1893 tz_src = _("parsed date/time string");
1894 }
1895 else if ((tz_env = getenv("TZ")))
1896 {
1897 if (tz_was_altered)
1898 {
1899 snprintf (dbg_tm, sizeof(dbg_tm), _("TZ=\"%s\" in date string"),
1900 tz_env);
1901 tz_src = dbg_tm;
1902 }
1903 else if (STREQ(tz_env,"UTC0"))
1904 {
1905 /* Special case: using 'date -u' simply set TZ=UTC0 */
1906 tz_src = _("TZ=UTC0 environment value or -u");
1907 }
1908 else
1909 {
1910 snprintf (dbg_tm, sizeof(dbg_tm),
1911 _("TZ=\"%s\" environment value"), tz_env);
1912 tz_src = dbg_tm;
1913 }
1914 }
1915 else
1916 {
1917 tz_src = _("system default");
1918 }
1919
1920 if (pc.parse_datetime_debug)
1921 dbg_printf (_("input timezone: %+03d:%02d (set from %s)\n"),
1922 (int)(tz/60), abs ((int)tz)%60, tz_src);
1923
1924 }
1925
1926 if (pc.timespec_seen)
1927 *result = pc.seconds;
1928 else
1929 {
1930 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1931 | (pc.local_zones_seen + pc.zones_seen)))
1932 {
1933 if (pc.parse_datetime_debug)
1934 {
1935 if (pc.times_seen > 1)
1936 dbg_printf ("error: seen multiple time parts\n");
1937 if (pc.dates_seen > 1)
1938 dbg_printf ("error: seen multiple date parts\n");
1939 if (pc.days_seen > 1)
1940 dbg_printf ("error: seen multiple days parts\n");
1941 if (pc.dsts_seen > 1)
1942 dbg_printf ("error: seen multiple daylight-saving parts\n");
1943 if ( (pc.local_zones_seen + pc.zones_seen) > 1)
1944 dbg_printf ("error: seen multiple time-zone parts\n");
1945 }
1946 goto fail;
1947 }
1948
1949 tm.tm_year = to_year (pc.year, pc.parse_datetime_debug) - TM_YEAR_BASE;
1950 tm.tm_mon = pc.month - 1;
1951 tm.tm_mday = pc.day;
1952 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1953 {
1954 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1955 if (tm.tm_hour < 0)
1956 {
1957 const char* mrd = (pc.meridian==MERam)?"am":
1958 (pc.meridian==MERpm)?"pm":"";
1959 if (pc.parse_datetime_debug)
1960 dbg_printf (_("error: invalid hour %ld%s\n"), pc.hour, mrd);
1961
1962 goto fail;
1963 }
1964 tm.tm_min = pc.minutes;
1965 tm.tm_sec = pc.seconds.tv_sec;
1966 if (pc.parse_datetime_debug)
1967 dbg_printf (_("using %s time as starting value: '%s'\n"),
1968 (pc.times_seen)?_("specified"):_("current"),
1969 debug_strftime (&tm,dbg_tm,sizeof (dbg_tm)));
1970 }
1971 else
1972 {
1973 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1974 pc.seconds.tv_nsec = 0;
1975 if (pc.parse_datetime_debug)
1976 dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
1977 }
1978
1979 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1980 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1981 tm.tm_isdst = -1;
1982
1983 /* But if the input explicitly specifies local time with or without
1984 DST, give mktime that information. */
1985 if (pc.local_zones_seen)
1986 tm.tm_isdst = pc.local_isdst;
1987
1988 tm0 = tm;
1989
1990 Start = mktime (&tm);
1991
1992 if (! mktime_ok (&tm0, &tm, Start))
1993 {
1994 if (! pc.zones_seen)
1995 {
1996 debug_mktime_not_ok (&tm0, &tm, &pc, pc.zones_seen);
1997
1998 goto fail;
1999 }
2000 else
2001 {
2002 /* Guard against falsely reporting errors near the time_t
2003 boundaries when parsing times in other time zones. For
2004 example, suppose the input string "1969-12-31 23:00:00 -0100",
2005 the current time zone is 8 hours ahead of UTC, and the min
2006 time_t value is 1970-01-01 00:00:00 UTC. Then the min
2007 localtime value is 1970-01-01 08:00:00, and mktime will
2008 therefore fail on 1969-12-31 23:00:00. To work around the
2009 problem, set the time zone to 1 hour behind UTC temporarily
2010 by setting TZ="XXX1:00" and try mktime again. */
2011
2012 long int time_zone = pc.time_zone;
2013 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
2014 long int abs_time_zone_hour = abs_time_zone / 60;
2015 int abs_time_zone_min = abs_time_zone % 60;
2016 char tz1buf[sizeof "XXX+0:00" + TYPE_WIDTH (pc.time_zone) / 3];
2017 if (!tz_was_altered)
2018 tz0 = get_tz (tz0buf);
2019 sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
2020 abs_time_zone_hour, abs_time_zone_min);
2021 if (setenv ("TZ", tz1buf, 1) != 0)
2022 {
2023 /* TODO: was warn () + print errno? */
2024 if (pc.parse_datetime_debug)
2025 dbg_printf (_("error: setenv('TZ','%s') failed\n"), tz1buf);
2026
2027 goto fail;
2028 }
2029 tz_was_altered = true;
2030 tm = tm0;
2031 Start = mktime (&tm);
2032 if (! mktime_ok (&tm0, &tm, Start))
2033 {
2034 debug_mktime_not_ok (&tm0, &tm, &pc, pc.zones_seen);
2035
2036 goto fail;
2037 }
2038 }
2039 }
2040
2041 if (pc.days_seen && ! pc.dates_seen)
2042 {
2043 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
2044 + 7 * (pc.day_ordinal
2045 - (0 < pc.day_ordinal
2046 && tm.tm_wday != pc.day_number)));
2047 tm.tm_isdst = -1;
2048 Start = mktime (&tm);
2049 if (Start == (time_t) -1)
2050 {
2051 if (pc.parse_datetime_debug)
2052 dbg_printf (_("error: day '%s' (day ordinal=%ld number=%d) " \
2053 "resulted in an invalid date: '%s'\n"),
2054 str_days (&pc,dbg_ord,sizeof (dbg_ord)),
2055 pc.day_ordinal,pc.day_number,
2056 debug_strfdatetime (&tm, &pc, dbg_tm,
2057 sizeof (dbg_tm)));
2058
2059 goto fail;
2060 }
2061
2062 if (pc.parse_datetime_debug)
2063 dbg_printf (_("new start date: '%s' is '%s'\n"),
2064 str_days (&pc,dbg_ord,sizeof (dbg_ord)),
2065 debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
2066
2067 }
2068
2069 if (pc.parse_datetime_debug)
2070 {
2071 if (!pc.dates_seen && !pc.days_seen)
2072 dbg_printf (_("using current date as starting value: '%s'\n"),
2073 debug_strfdate (&tm,dbg_tm,sizeof (dbg_tm)));
2074
2075 if (pc.days_seen && pc.dates_seen)
2076 dbg_printf (_("warning: day (%s) ignored when explicit dates " \
2077 "are given\n"),
2078 str_days (&pc,dbg_ord,sizeof (dbg_ord)));
2079
2080 dbg_printf (_("starting date/time: '%s'\n"),
2081 debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
2082 }
2083
2084 /* Add relative date. */
2085 if (pc.rel.year | pc.rel.month | pc.rel.day)
2086 {
2087 if (pc.parse_datetime_debug)
2088 {
2089 if ((pc.rel.year != 0 || pc.rel.month !=0) && tm.tm_mday==1)
2090 dbg_printf (_("warning: when adding relative months/years, " \
2091 "it is recommended to specify the 15th of the " \
2092 "months\n"));
2093
2094 if (pc.rel.day != 0 && tm.tm_hour==0)
2095 dbg_printf (_("warning: when adding relative days, " \
2096 "it is recommended to specify 12:00pm\n"));
2097 }
2098
2099 int year = tm.tm_year + pc.rel.year;
2100 int month = tm.tm_mon + pc.rel.month;
2101 int day = tm.tm_mday + pc.rel.day;
2102 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
2103 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
2104 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
2105 {
2106 /* TODO: what is the actual error? int-value wrap-around? */
2107 if (pc.parse_datetime_debug)
2108 dbg_printf (_("error: %s:%d\n"), __FILE__,__LINE__);
2109
2110 goto fail;
2111 }
2112 tm.tm_year = year;
2113 tm.tm_mon = month;
2114 tm.tm_mday = day;
2115 tm.tm_hour = tm0.tm_hour;
2116 tm.tm_min = tm0.tm_min;
2117 tm.tm_sec = tm0.tm_sec;
2118 tm.tm_isdst = tm0.tm_isdst;
2119 Start = mktime (&tm);
2120 if (Start == (time_t) -1)
2121 {
2122 if (pc.parse_datetime_debug)
2123 dbg_printf(_("error: adding relative date resulted " \
2124 "in an invalid date: '%s'\n"),
2125 debug_strfdatetime (&tm, &pc, dbg_tm,
2126 sizeof (dbg_tm)));
2127
2128 goto fail;
2129 }
2130
2131 if (pc.parse_datetime_debug)
2132 {
2133 dbg_printf (_("after date adjustment " \
2134 "(%+ld years, %+ld months, %+ld days),\n"),
2135 pc.rel.year, pc.rel.month, pc.rel.day);
2136 dbg_printf (_(" new date/time = '%s'\n"),
2137 debug_strfdatetime (&tm, &pc, dbg_tm,
2138 sizeof (dbg_tm)));
2139 }
2140
2141 }
2142
2143 /* The only "output" of this if-block is an updated Start value,
2144 so this block must follow others that clobber Start. */
2145 if (pc.zones_seen)
2146 {
2147 long int delta = pc.time_zone * 60;
2148 time_t t1;
2149 #ifdef HAVE_TM_GMTOFF
2150 delta -= tm.tm_gmtoff;
2151 #else
2152 time_t t = Start;
2153 struct tm const *gmt = gmtime (&t);
2154 if (! gmt)
2155 {
2156 /* TODO: use 'warn(3)' + print errno ? */
2157 if (pc.parse_datetime_debug)
2158 dbg_printf (_("error: gmtime failed for t=%ld\n"),t);
2159
2160 goto fail;
2161 }
2162 delta -= tm_diff (&tm, gmt);
2163 #endif
2164 t1 = Start - delta;
2165 if ((Start < t1) != (delta < 0))
2166 {
2167 if (pc.parse_datetime_debug)
2168 dbg_printf (_("error: timezone %ld caused time_t overflow\n"),
2169 pc.time_zone);
2170
2171 goto fail; /* time_t overflow */
2172 }
2173 Start = t1;
2174 }
2175
2176 if (pc.parse_datetime_debug)
2177 dbg_printf (_("'%s' = %ld epoch-seconds\n"),
2178 debug_strfdatetime (&tm, &pc, dbg_tm, sizeof (dbg_tm)),
2179 Start);
2180
2181 /* Add relative hours, minutes, and seconds. On hosts that support
2182 leap seconds, ignore the possibility of leap seconds; e.g.,
2183 "+ 10 minutes" adds 600 seconds, even if one of them is a
2184 leap second. Typically this is not what the user wants, but it's
2185 too hard to do it the other way, because the time zone indicator
2186 must be applied before relative times, and if mktime is applied
2187 again the time zone will be lost. */
2188 {
2189 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
2190 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2191 time_t t0 = Start;
2192 long int d1 = 60 * 60 * pc.rel.hour;
2193 time_t t1 = t0 + d1;
2194 long int d2 = 60 * pc.rel.minutes;
2195 time_t t2 = t1 + d2;
2196 long_time_t d3 = pc.rel.seconds;
2197 long_time_t t3 = t2 + d3;
2198 long int d4 = (sum_ns - normalized_ns) / BILLION;
2199 long_time_t t4 = t3 + d4;
2200 time_t t5 = t4;
2201
2202 if ((d1 / (60 * 60) ^ pc.rel.hour)
2203 | (d2 / 60 ^ pc.rel.minutes)
2204 | ((t1 < t0) ^ (d1 < 0))
2205 | ((t2 < t1) ^ (d2 < 0))
2206 | ((t3 < t2) ^ (d3 < 0))
2207 | ((t4 < t3) ^ (d4 < 0))
2208 | (t5 != t4))
2209 {
2210 if (pc.parse_datetime_debug)
2211 dbg_printf (_("error: adding relative time caused an " \
2212 "overflow\n"));
2213
2214 goto fail;
2215 }
2216
2217 if (pc.parse_datetime_debug
2218 && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2219 {
2220 dbg_printf (_("after time adjustment (%+ld hours, " \
2221 "%+ld minutes, %+ld seconds, %+ld ns),\n"),
2222 pc.rel.hour,pc.rel.minutes,pc.rel.seconds,pc.rel.ns);
2223 dbg_printf (_(" new time = %ld epoch-seconds\n"),t5);
2224 }
2225
2226 result->tv_sec = t5;
2227 result->tv_nsec = normalized_ns;
2228 }
2229 }
2230
2231 goto done;
2232
2233 fail:
2234 ok = false;
2235 done:
2236 if (tz_was_altered)
2237 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
2238 if (tz0 != tz0buf)
2239 free (tz0);
2240
2241 if (ok && pc.parse_datetime_debug)
2242 {
2243 /* print local timezone AFTER restoring TZ (if tz_was_altered)*/
2244 const long int otz = get_effective_timezone ();
2245 const char* tz_src;
2246 const char* tz_env;
2247
2248 if ((tz_env = getenv("TZ")))
2249 {
2250 /* Special case: using 'date -u' simply set TZ=UTC0 */
2251 if (STREQ(tz_env,"UTC0"))
2252 {
2253 tz_src = _("TZ=UTC0 environment value or -u");
2254 }
2255 else
2256 {
2257 snprintf (dbg_tm, sizeof(dbg_tm),
2258 _("TZ=\"%s\" environment value"), tz_env);
2259 tz_src = dbg_tm;
2260 }
2261 }
2262 else
2263 {
2264 tz_src = _("system default");
2265 }
2266
2267 if (pc.parse_datetime_debug)
2268 {
2269 dbg_printf (_("output timezone: %+03d:%02d (set from %s)\n"),
2270 (int)(otz/60), abs ((int)otz)%60, tz_src);
2271
2272
2273 dbg_printf (_("final: %ld.%09ld (epoch-seconds)\n"),
2274 result->tv_sec,result->tv_nsec);
2275
2276 struct tm const *gmt = gmtime (&result->tv_sec);
2277 dbg_printf (_("final: %s (UTC0)\n"),
2278 debug_strfdatetime (gmt, NULL, dbg_tm, sizeof (dbg_tm)));
2279 struct tm const *lmt = localtime (&result->tv_sec);
2280 dbg_printf (_("final: %s (output timezone TZ=%+03d:%02d)\n"),
2281 debug_strfdatetime (lmt, NULL, dbg_tm, sizeof (dbg_tm)),
2282 (int)(otz/60), abs ((int)otz)%60);
2283 }
2284 }
2285
2286 return ok;
2287 }
2288
2289 #if TEST
2290
2291 int
main(int ac,char ** av)2292 main (int ac, char **av)
2293 {
2294 char buff[BUFSIZ];
2295
2296 printf ("Enter date, or blank line to exit.\n\t> ");
2297 fflush (stdout);
2298
2299 buff[BUFSIZ - 1] = '\0';
2300 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2301 {
2302 struct timespec d;
2303 struct tm const *tm;
2304 if (! parse_datetime (&d, buff, NULL))
2305 printf ("Bad format - couldn't convert.\n");
2306 else if (! (tm = localtime (&d.tv_sec)))
2307 {
2308 long int sec = d.tv_sec;
2309 printf ("localtime (%ld) failed\n", sec);
2310 }
2311 else
2312 {
2313 int ns = d.tv_nsec;
2314 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
2315 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
2316 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
2317 }
2318 printf ("\t> ");
2319 fflush (stdout);
2320 }
2321 return 0;
2322 }
2323 #endif /* TEST */
2324