2
/* Parse a string into an internal time stamp.
3
Copyright (C) 1999, 2000, 2002 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)
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. Unlike previous versions, this
26
version is reentrant. */
35
/* Since the code of getdate.y is not included in the Emacs executable
36
itself, there is no need to #define static in this file. Even if
37
the code were included in the Emacs executable, it probably
38
wouldn't do any harm to #undef it here; this will only cause
39
problems if we try to write to a static variable, which I don't
40
think this code needs to do. */
49
# include <stdlib.h> /* for `free'; used by Bison 1.27 */
52
#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
53
# define IN_CTYPE_DOMAIN(c) 1
55
# define IN_CTYPE_DOMAIN(c) isascii (c)
58
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
59
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
60
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
61
#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
63
/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
64
- Its arg may be any int or unsigned int; it need not be an unsigned char.
65
- It's guaranteed to evaluate its argument exactly once.
66
- It's typically faster.
67
POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
68
ISDIGIT_LOCALE unless it's important to use the locale's definition
69
of `digit' even when the host does not conform to POSIX. */
70
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
72
#if STDC_HEADERS || HAVE_STRING_H
76
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
77
# define __attribute__(x)
80
#ifndef ATTRIBUTE_UNUSED
81
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
84
#define EPOCH_YEAR 1970
85
#define TM_YEAR_BASE 1900
87
#define HOUR(x) ((x) * 60)
89
/* An integer value, and the number of digits in its textual
97
/* An entry in the lexical lookup table. */
105
/* Meridian: am, pm, or 24-hour style. */
106
enum { MERam, MERpm, MER24 };
108
/* Information passed to and from the parser. */
111
/* The input string remaining to be parsed. */
114
/* N, if this is the Nth Tuesday. */
117
/* Day of week; Sunday is 0. */
120
/* tm_isdst flag for the local zone. */
123
/* Time zone, in minutes east of UTC. */
126
/* Style used for time. */
129
/* Gregorian year, month, day, hour, minutes, and seconds. */
137
/* Relative year, month, day, hour, minutes, and seconds. */
145
/* Counts of nonterminals of various flavors parsed so far. */
148
int local_zones_seen;
153
/* Table of local time zone abbrevations, terminated by a null entry. */
154
table local_time_zone_table[3];
157
#define PC (* (parser_control *) parm)
158
#define YYLEX_PARAM parm
159
#define YYPARSE_PARAM parm
161
static int yyerror ();
166
/* We want a reentrant parser. */
169
/* This grammar has 13 shift/reduce conflicts. */
180
%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
181
%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE
183
%token <textintval> tSNUMBER tUNUMBER
185
%type <intval> o_merid
198
{ PC.local_zones_seen++; }
218
| tUNUMBER ':' tUNUMBER o_merid
221
PC.minutes = $3.value;
225
| tUNUMBER ':' tUNUMBER tSNUMBER
228
PC.minutes = $3.value;
231
PC.time_zone = $4.value % 100 + ($4.value / 100) * 60;
233
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid
236
PC.minutes = $3.value;
237
PC.seconds = $5.value;
240
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER
243
PC.minutes = $3.value;
244
PC.seconds = $5.value;
247
PC.time_zone = $6.value % 100 + ($6.value / 100) * 60;
253
{ PC.local_isdst = $1; }
255
{ PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
260
{ PC.time_zone = $1; }
262
{ PC.time_zone = $1 + 60; }
264
{ PC.time_zone = $1 + 60; }
280
PC.day_ordinal = $1.value;
286
tUNUMBER '/' tUNUMBER
291
| tUNUMBER '/' tUNUMBER '/' tUNUMBER
293
/* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
294
otherwise as MM/DD/YY.
295
The goal in recognizing YYYY/MM/DD is solely to support legacy
296
machine-generated dates like those in an RCS log listing. If
297
you want portability, use the ISO 8601 format. */
311
| tUNUMBER tSNUMBER tSNUMBER
313
/* ISO 8601 format. YYYY-MM-DD. */
315
PC.month = -$2.value;
318
| tUNUMBER tMONTH tSNUMBER
320
/* e.g. 17-JUN-1992. */
323
PC.year.value = -$3.value;
324
PC.year.digits = $3.digits;
331
| tMONTH tUNUMBER ',' tUNUMBER
342
| tUNUMBER tMONTH tUNUMBER
353
PC.rel_seconds = -PC.rel_seconds;
354
PC.rel_minutes = -PC.rel_minutes;
355
PC.rel_hour = -PC.rel_hour;
356
PC.rel_day = -PC.rel_day;
357
PC.rel_month = -PC.rel_month;
358
PC.rel_year = -PC.rel_year;
365
{ PC.rel_year += $1.value * $2; }
366
| tSNUMBER tYEAR_UNIT
367
{ PC.rel_year += $1.value * $2; }
369
{ PC.rel_year += $1; }
370
| tUNUMBER tMONTH_UNIT
371
{ PC.rel_month += $1.value * $2; }
372
| tSNUMBER tMONTH_UNIT
373
{ PC.rel_month += $1.value * $2; }
375
{ PC.rel_month += $1; }
377
{ PC.rel_day += $1.value * $2; }
379
{ PC.rel_day += $1.value * $2; }
381
{ PC.rel_day += $1; }
382
| tUNUMBER tHOUR_UNIT
383
{ PC.rel_hour += $1.value * $2; }
384
| tSNUMBER tHOUR_UNIT
385
{ PC.rel_hour += $1.value * $2; }
387
{ PC.rel_hour += $1; }
388
| tUNUMBER tMINUTE_UNIT
389
{ PC.rel_minutes += $1.value * $2; }
390
| tSNUMBER tMINUTE_UNIT
391
{ PC.rel_minutes += $1.value * $2; }
393
{ PC.rel_minutes += $1; }
395
{ PC.rel_seconds += $1.value * $2; }
397
{ PC.rel_seconds += $1.value * $2; }
399
{ PC.rel_seconds += $1; }
406
&& ! PC.rels_seen && (PC.times_seen || 2 < $1.digits))
413
PC.day = $1.value % 100;
414
PC.month = ($1.value / 100) % 100;
415
PC.year.value = $1.value / 10000;
416
PC.year.digits = $1.digits - 4;
428
PC.hour = $1.value / 100;
429
PC.minutes = $1.value % 100;
447
/* Include this file down here because bison inserts code above which
448
may define-away `const'. We want the prototype for get_date to have
449
the same signature as the function definition. */
450
#include "modules/getdate.h"
453
struct tm *gmtime ();
456
struct tm *localtime ();
462
static table const meridian_table[] =
464
{ "AM", tMERIDIAN, MERam },
465
{ "A.M.", tMERIDIAN, MERam },
466
{ "PM", tMERIDIAN, MERpm },
467
{ "P.M.", tMERIDIAN, MERpm },
471
static table const dst_table[] =
476
static table const month_and_day_table[] =
478
{ "JANUARY", tMONTH, 1 },
479
{ "FEBRUARY", tMONTH, 2 },
480
{ "MARCH", tMONTH, 3 },
481
{ "APRIL", tMONTH, 4 },
482
{ "MAY", tMONTH, 5 },
483
{ "JUNE", tMONTH, 6 },
484
{ "JULY", tMONTH, 7 },
485
{ "AUGUST", tMONTH, 8 },
486
{ "SEPTEMBER",tMONTH, 9 },
487
{ "SEPT", tMONTH, 9 },
488
{ "OCTOBER", tMONTH, 10 },
489
{ "NOVEMBER", tMONTH, 11 },
490
{ "DECEMBER", tMONTH, 12 },
491
{ "SUNDAY", tDAY, 0 },
492
{ "MONDAY", tDAY, 1 },
493
{ "TUESDAY", tDAY, 2 },
495
{ "WEDNESDAY",tDAY, 3 },
496
{ "WEDNES", tDAY, 3 },
497
{ "THURSDAY", tDAY, 4 },
499
{ "THURS", tDAY, 4 },
500
{ "FRIDAY", tDAY, 5 },
501
{ "SATURDAY", tDAY, 6 },
505
static table const time_units_table[] =
507
{ "YEAR", tYEAR_UNIT, 1 },
508
{ "MONTH", tMONTH_UNIT, 1 },
509
{ "FORTNIGHT",tDAY_UNIT, 14 },
510
{ "WEEK", tDAY_UNIT, 7 },
511
{ "DAY", tDAY_UNIT, 1 },
512
{ "HOUR", tHOUR_UNIT, 1 },
513
{ "MINUTE", tMINUTE_UNIT, 1 },
514
{ "MIN", tMINUTE_UNIT, 1 },
515
{ "SECOND", tSEC_UNIT, 1 },
516
{ "SEC", tSEC_UNIT, 1 },
520
/* Assorted relative-time words. */
521
static table const relative_time_table[] =
523
{ "TOMORROW", tMINUTE_UNIT, 24 * 60 },
524
{ "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
525
{ "TODAY", tMINUTE_UNIT, 0 },
526
{ "NOW", tMINUTE_UNIT, 0 },
527
{ "LAST", tUNUMBER, -1 },
528
{ "THIS", tUNUMBER, 0 },
529
{ "NEXT", tUNUMBER, 1 },
530
{ "FIRST", tUNUMBER, 1 },
531
/*{ "SECOND", tUNUMBER, 2 }, */
532
{ "THIRD", tUNUMBER, 3 },
533
{ "FOURTH", tUNUMBER, 4 },
534
{ "FIFTH", tUNUMBER, 5 },
535
{ "SIXTH", tUNUMBER, 6 },
536
{ "SEVENTH", tUNUMBER, 7 },
537
{ "EIGHTH", tUNUMBER, 8 },
538
{ "NINTH", tUNUMBER, 9 },
539
{ "TENTH", tUNUMBER, 10 },
540
{ "ELEVENTH", tUNUMBER, 11 },
541
{ "TWELFTH", tUNUMBER, 12 },
546
/* The time zone table. This table is necessarily incomplete, as time
547
zone abbreviations are ambiguous; e.g. Australians interpret "EST"
548
as Eastern time in Australia, not as US Eastern Standard Time.
549
You cannot rely on getdate to handle arbitrary time zone
550
abbreviations; use numeric abbreviations like `-0500' instead. */
551
static table const time_zone_table[] =
553
{ "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
554
{ "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
555
{ "UTC", tZONE, HOUR ( 0) },
556
{ "WET", tZONE, HOUR ( 0) }, /* Western European */
557
{ "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
558
{ "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
559
{ "ART", tZONE, -HOUR ( 3) }, /* Argentina */
560
{ "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
561
{ "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
562
{ "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
563
{ "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
564
{ "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
565
{ "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
566
{ "CLT", tZONE, -HOUR ( 4) }, /* Chile */
567
{ "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
568
{ "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
569
{ "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
570
{ "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
571
{ "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
572
{ "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
573
{ "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
574
{ "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
575
{ "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
576
{ "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
577
{ "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
578
{ "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
579
{ "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
580
{ "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
581
{ "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
582
{ "WAT", tZONE, HOUR ( 1) }, /* West Africa */
583
{ "CET", tZONE, HOUR ( 1) }, /* Central European */
584
{ "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
585
{ "MET", tZONE, HOUR ( 1) }, /* Middle European */
586
{ "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
587
{ "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
588
{ "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
589
{ "EET", tZONE, HOUR ( 2) }, /* Eastern European */
590
{ "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
591
{ "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
592
{ "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
593
{ "EAT", tZONE, HOUR ( 3) }, /* East Africa */
594
{ "MSK", tZONE, HOUR ( 3) }, /* Moscow */
595
{ "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
596
{ "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
597
{ "SGT", tZONE, HOUR ( 8) }, /* Singapore */
598
{ "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
599
{ "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
600
{ "GST", tZONE, HOUR (10) }, /* Guam Standard */
601
{ "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
602
{ "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
606
/* Military time zone table. */
607
static table const military_table[] =
609
{ "A", tZONE, -HOUR ( 1) },
610
{ "B", tZONE, -HOUR ( 2) },
611
{ "C", tZONE, -HOUR ( 3) },
612
{ "D", tZONE, -HOUR ( 4) },
613
{ "E", tZONE, -HOUR ( 5) },
614
{ "F", tZONE, -HOUR ( 6) },
615
{ "G", tZONE, -HOUR ( 7) },
616
{ "H", tZONE, -HOUR ( 8) },
617
{ "I", tZONE, -HOUR ( 9) },
618
{ "K", tZONE, -HOUR (10) },
619
{ "L", tZONE, -HOUR (11) },
620
{ "M", tZONE, -HOUR (12) },
621
{ "N", tZONE, HOUR ( 1) },
622
{ "O", tZONE, HOUR ( 2) },
623
{ "P", tZONE, HOUR ( 3) },
624
{ "Q", tZONE, HOUR ( 4) },
625
{ "R", tZONE, HOUR ( 5) },
626
{ "S", tZONE, HOUR ( 6) },
627
{ "T", tZONE, HOUR ( 7) },
628
{ "U", tZONE, HOUR ( 8) },
629
{ "V", tZONE, HOUR ( 9) },
630
{ "W", tZONE, HOUR (10) },
631
{ "X", tZONE, HOUR (11) },
632
{ "Y", tZONE, HOUR (12) },
633
{ "Z", tZONE, HOUR ( 0) },
640
to_hour (int hours, int meridian)
645
return 0 <= hours && hours < 24 ? hours : -1;
647
return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
649
return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
658
to_year (textint textyear)
660
int year = textyear.value;
665
/* XPG4 suggests that years 00-68 map to 2000-2068, and
666
years 69-99 map to 1969-1999. */
667
if (textyear.digits == 2)
668
year += year < 69 ? 2000 : 1900;
674
lookup_zone (parser_control const *pc, char const *name)
678
/* Try local zone abbreviations first; they're more likely to be right. */
679
for (tp = pc->local_time_zone_table; tp->name; tp++)
680
if (strcmp (name, tp->name) == 0)
683
for (tp = time_zone_table; tp->name; tp++)
684
if (strcmp (name, tp->name) == 0)
691
/* Yield the difference between *A and *B,
692
measured in seconds, ignoring leap seconds.
693
The body of this function is taken directly from the GNU C Library;
694
see src/strftime.c. */
696
tm_diff (struct tm const *a, struct tm const *b)
698
/* Compute intervening leap days correctly even if year is negative.
699
Take care to avoid int overflow in leap day calculations,
700
but it's OK to assume that A and B are close to each other. */
701
int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
702
int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
703
int a100 = a4 / 25 - (a4 % 25 < 0);
704
int b100 = b4 / 25 - (b4 % 25 < 0);
705
int a400 = a100 >> 2;
706
int b400 = b100 >> 2;
707
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
708
int years = a->tm_year - b->tm_year;
709
int days = (365 * years + intervening_leap_days
710
+ (a->tm_yday - b->tm_yday));
711
return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
712
+ (a->tm_min - b->tm_min))
713
+ (a->tm_sec - b->tm_sec));
715
#endif /* ! HAVE_TM_GMTOFF */
718
lookup_word (parser_control const *pc, char *word)
727
/* Make it uppercase. */
728
for (p = word; *p; p++)
729
if (ISLOWER ((unsigned char) *p))
730
*p = toupper ((unsigned char) *p);
732
for (tp = meridian_table; tp->name; tp++)
733
if (strcmp (word, tp->name) == 0)
736
/* See if we have an abbreviation for a month. */
737
wordlen = strlen (word);
738
abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
740
for (tp = month_and_day_table; tp->name; tp++)
741
if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
744
if ((tp = lookup_zone (pc, word)))
747
if (strcmp (word, dst_table[0].name) == 0)
750
for (tp = time_units_table; tp->name; tp++)
751
if (strcmp (word, tp->name) == 0)
754
/* Strip off any plural and try the units table again. */
755
if (word[wordlen - 1] == 'S')
757
word[wordlen - 1] = '\0';
758
for (tp = time_units_table; tp->name; tp++)
759
if (strcmp (word, tp->name) == 0)
761
word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
764
for (tp = relative_time_table; tp->name; tp++)
765
if (strcmp (word, tp->name) == 0)
768
/* Military time zones. */
770
for (tp = military_table; tp->name; tp++)
771
if (word[0] == tp->name[0])
774
/* Drop out any periods and try the time zone table again. */
775
for (i = 0, p = q = word; (*p = *q); q++)
780
if (i && (tp = lookup_zone (pc, word)))
787
yylex (YYSTYPE *lvalp, parser_control *pc)
794
while (c = *pc->input, ISSPACE (c))
797
if (ISDIGIT (c) || c == '-' || c == '+')
802
if (c == '-' || c == '+')
804
sign = c == '-' ? -1 : 1;
807
/* skip the '-' sign */
816
value = 10 * value + c - '0';
820
lvalp->textintval.value = sign < 0 ? -value : value;
821
lvalp->textintval.digits = p - pc->input;
823
return sign ? tSNUMBER : tUNUMBER;
834
if (p < buff + sizeof buff - 1)
838
while (ISALPHA (c) || c == '.');
841
tp = lookup_word (pc, buff);
844
lvalp->intval = tp->value;
865
/* Do nothing if the parser reports an error. */
867
yyerror (char *s ATTRIBUTE_UNUSED)
872
/* Parse a date/time string P. Return the corresponding time_t value,
873
or (time_t) -1 if there is an error. P can be an incomplete or
874
relative time specification; if so, use *NOW as the basis for the
877
get_date (const char *p, const time_t *now)
879
time_t Start = now ? *now : time (0);
880
struct tm *tmp = localtime (&Start);
889
pc.year.value = tmp->tm_year + TM_YEAR_BASE;
891
pc.month = tmp->tm_mon + 1;
892
pc.day = tmp->tm_mday;
893
pc.hour = tmp->tm_hour;
894
pc.minutes = tmp->tm_min;
895
pc.seconds = tmp->tm_sec;
896
tm.tm_isdst = tmp->tm_isdst;
909
pc.local_zones_seen = 0;
912
#if HAVE_STRUCT_TM_TM_ZONE
913
pc.local_time_zone_table[0].name = tmp->tm_zone;
914
pc.local_time_zone_table[0].type = tLOCAL_ZONE;
915
pc.local_time_zone_table[0].value = tmp->tm_isdst;
916
pc.local_time_zone_table[1].name = 0;
918
/* Probe the names used in the next three calendar quarters, looking
919
for a tm_isdst different from the one we already have. */
922
for (quarter = 1; quarter <= 3; quarter++)
924
time_t probe = Start + quarter * (90 * 24 * 60 * 60);
925
struct tm *probe_tm = localtime (&probe);
926
if (probe_tm && probe_tm->tm_zone
927
&& probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
930
pc.local_time_zone_table[1].name = probe_tm->tm_zone;
931
pc.local_time_zone_table[1].type = tLOCAL_ZONE;
932
pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
933
pc.local_time_zone_table[2].name = 0;
943
extern char *tzname[];
946
for (i = 0; i < 2; i++)
948
pc.local_time_zone_table[i].name = tzname[i];
949
pc.local_time_zone_table[i].type = tLOCAL_ZONE;
950
pc.local_time_zone_table[i].value = i;
952
pc.local_time_zone_table[i].name = 0;
955
pc.local_time_zone_table[0].name = 0;
959
if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
960
&& ! strcmp (pc.local_time_zone_table[0].name,
961
pc.local_time_zone_table[1].name))
963
/* This locale uses the same abbrevation for standard and
964
daylight times. So if we see that abbreviation, we don't
965
know whether it's daylight time. */
966
pc.local_time_zone_table[0].value = -1;
967
pc.local_time_zone_table[1].name = 0;
970
if (yyparse (&pc) != 0
971
|| 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
972
|| 1 < (pc.local_zones_seen + pc.zones_seen)
973
|| (pc.local_zones_seen && 1 < pc.local_isdst))
976
tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
977
tm.tm_mon = pc.month - 1 + pc.rel_month;
978
tm.tm_mday = pc.day + pc.rel_day;
979
if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
981
tm.tm_hour = to_hour (pc.hour, pc.meridian);
984
tm.tm_min = pc.minutes;
985
tm.tm_sec = pc.seconds;
989
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
992
/* Let mktime deduce tm_isdst if we have an absolute time stamp,
993
or if the relative time stamp mentions days, months, or years. */
994
if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
995
| pc.rel_month | pc.rel_year)
998
/* But if the input explicitly specifies local time with or without
999
DST, give mktime that information. */
1000
if (pc.local_zones_seen)
1001
tm.tm_isdst = pc.local_isdst;
1005
Start = mktime (&tm);
1007
if (Start == (time_t) -1)
1010
/* Guard against falsely reporting errors near the time_t boundaries
1011
when parsing times in other time zones. For example, if the min
1012
time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
1013
of UTC, then the min localtime value is 1970-01-01 08:00:00; if
1014
we apply mktime to 1970-01-01 00:00:00 we will get an error, so
1015
we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
1016
zone by 24 hours to compensate. This algorithm assumes that
1017
there is no DST transition within a day of the time_t boundaries. */
1021
if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
1024
pc.time_zone += 24 * 60;
1029
pc.time_zone -= 24 * 60;
1031
Start = mktime (&tm);
1034
if (Start == (time_t) -1)
1038
if (pc.days_seen && ! pc.dates_seen)
1040
tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1041
+ 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1043
Start = mktime (&tm);
1044
if (Start == (time_t) -1)
1050
int delta = pc.time_zone * 60;
1051
#ifdef HAVE_TM_GMTOFF
1052
delta -= tm.tm_gmtoff;
1054
struct tm *gmt = gmtime (&Start);
1057
delta -= tm_diff (&tm, gmt);
1059
if ((Start < Start - delta) != (delta < 0))
1060
return -1; /* time_t overflow */
1064
/* Add relative hours, minutes, and seconds. Ignore leap seconds;
1065
i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
1066
leap second. Typically this is not what the user wants, but it's
1067
too hard to do it the other way, because the time zone indicator
1068
must be applied before relative times, and if mktime is applied
1069
again the time zone will be lost. */
1072
long d1 = 60 * 60 * (long) pc.rel_hour;
1073
time_t t1 = t0 + d1;
1074
long d2 = 60 * (long) pc.rel_minutes;
1075
time_t t2 = t1 + d2;
1076
int d3 = pc.rel_seconds;
1077
time_t t3 = t2 + d3;
1078
if ((d1 / (60 * 60) ^ pc.rel_hour)
1079
| (d2 / 60 ^ pc.rel_minutes)
1080
| ((t0 + d1 < t0) ^ (d1 < 0))
1081
| ((t1 + d2 < t1) ^ (d2 < 0))
1082
| ((t2 + d3 < t2) ^ (d3 < 0)))
1095
main (int ac, char **av)
1100
printf ("Enter date, or blank line to exit.\n\t> ");
1103
buff[BUFSIZ - 1] = 0;
1104
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1106
d = get_date (buff, 0);
1107
if (d == (time_t) -1)
1108
printf ("Bad format - couldn't convert.\n");
1110
printf ("%s", ctime (&d));
1116
#endif /* defined TEST */