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, see <http://www.gnu.org/licenses/>. */
18
/* Originally written by Steven M. Bellovin <smb@research.att.com> while
19
at the University of North Carolina at Chapel Hill. Later tweaked by
20
a couple of people on Usenet. Completely overhauled by Rich $alz
21
<rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
23
Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
24
the right thing about local DST. Unlike previous versions, this
25
version is reentrant. */
34
/* Since the code of getdate.y is not included in the Emacs executable
35
itself, there is no need to #define static in this file. Even if
36
the code were included in the Emacs executable, it probably
37
wouldn't do any harm to #undef it here; this will only cause
38
problems if we try to write to a static variable, which I don't
39
think this code needs to do. */
48
# include <stdlib.h> /* for `free'; used by Bison 1.27 */
51
#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
52
# define IN_CTYPE_DOMAIN(c) 1
54
# define IN_CTYPE_DOMAIN(c) isascii (c)
57
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
58
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
59
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
60
#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
62
/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
63
- Its arg may be any int or unsigned int; it need not be an unsigned char.
64
- It's guaranteed to evaluate its argument exactly once.
65
- It's typically faster.
66
POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
67
ISDIGIT_LOCALE unless it's important to use the locale's definition
68
of `digit' even when the host does not conform to POSIX. */
69
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
71
#if STDC_HEADERS || HAVE_STRING_H
75
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
76
# define __attribute__(x)
79
#ifndef ATTRIBUTE_UNUSED
80
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
83
#define EPOCH_YEAR 1970
84
#define TM_YEAR_BASE 1900
86
#define HOUR(x) ((x) * 60)
88
/* An integer value, and the number of digits in its textual
96
/* An entry in the lexical lookup table. */
104
/* Meridian: am, pm, or 24-hour style. */
105
enum { MERam, MERpm, MER24 };
107
/* Information passed to and from the parser. */
110
/* The input string remaining to be parsed. */
113
/* N, if this is the Nth Tuesday. */
116
/* Day of week; Sunday is 0. */
119
/* tm_isdst flag for the local zone. */
122
/* Time zone, in minutes east of UTC. */
125
/* Style used for time. */
128
/* Gregorian year, month, day, hour, minutes, and seconds. */
136
/* Relative year, month, day, hour, minutes, and seconds. */
144
/* Counts of nonterminals of various flavors parsed so far. */
147
int local_zones_seen;
152
/* Table of local time zone abbrevations, terminated by a null entry. */
153
table local_time_zone_table[3];
156
#define PC (* (parser_control *) parm)
157
#define YYLEX_PARAM parm
158
#define YYPARSE_PARAM parm
162
/* We want a reentrant parser. */
165
/* This grammar has 13 shift/reduce conflicts. */
176
static int yyerror(const char *);
177
static int yylex(YYSTYPE *, parser_control *);
183
%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
184
%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE
186
%token <textintval> tSNUMBER tUNUMBER
188
%type <intval> o_merid
201
{ PC.local_zones_seen++; }
221
| tUNUMBER ':' tUNUMBER o_merid
224
PC.minutes = $3.value;
228
| tUNUMBER ':' tUNUMBER tSNUMBER
231
PC.minutes = $3.value;
234
PC.time_zone = $4.value % 100 + ($4.value / 100) * 60;
236
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid
239
PC.minutes = $3.value;
240
PC.seconds = $5.value;
243
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER
246
PC.minutes = $3.value;
247
PC.seconds = $5.value;
250
PC.time_zone = $6.value % 100 + ($6.value / 100) * 60;
256
{ PC.local_isdst = $1; }
258
{ PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
263
{ PC.time_zone = $1; }
265
{ PC.time_zone = $1 + 60; }
267
{ PC.time_zone = $1 + 60; }
283
PC.day_ordinal = $1.value;
289
tUNUMBER '/' tUNUMBER
294
| tUNUMBER '/' tUNUMBER '/' tUNUMBER
296
/* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
297
otherwise as MM/DD/YY.
298
The goal in recognizing YYYY/MM/DD is solely to support legacy
299
machine-generated dates like those in an RCS log listing. If
300
you want portability, use the ISO 8601 format. */
314
| tUNUMBER tSNUMBER tSNUMBER
316
/* ISO 8601 format. YYYY-MM-DD. */
318
PC.month = -$2.value;
321
| tUNUMBER tMONTH tSNUMBER
323
/* e.g. 17-JUN-1992. */
326
PC.year.value = -$3.value;
327
PC.year.digits = $3.digits;
334
| tMONTH tUNUMBER ',' tUNUMBER
345
| tUNUMBER tMONTH tUNUMBER
356
PC.rel_seconds = -PC.rel_seconds;
357
PC.rel_minutes = -PC.rel_minutes;
358
PC.rel_hour = -PC.rel_hour;
359
PC.rel_day = -PC.rel_day;
360
PC.rel_month = -PC.rel_month;
361
PC.rel_year = -PC.rel_year;
368
{ PC.rel_year += $1.value * $2; }
369
| tSNUMBER tYEAR_UNIT
370
{ PC.rel_year += $1.value * $2; }
372
{ PC.rel_year += $1; }
373
| tUNUMBER tMONTH_UNIT
374
{ PC.rel_month += $1.value * $2; }
375
| tSNUMBER tMONTH_UNIT
376
{ PC.rel_month += $1.value * $2; }
378
{ PC.rel_month += $1; }
380
{ PC.rel_day += $1.value * $2; }
382
{ PC.rel_day += $1.value * $2; }
384
{ PC.rel_day += $1; }
385
| tUNUMBER tHOUR_UNIT
386
{ PC.rel_hour += $1.value * $2; }
387
| tSNUMBER tHOUR_UNIT
388
{ PC.rel_hour += $1.value * $2; }
390
{ PC.rel_hour += $1; }
391
| tUNUMBER tMINUTE_UNIT
392
{ PC.rel_minutes += $1.value * $2; }
393
| tSNUMBER tMINUTE_UNIT
394
{ PC.rel_minutes += $1.value * $2; }
396
{ PC.rel_minutes += $1; }
398
{ PC.rel_seconds += $1.value * $2; }
400
{ PC.rel_seconds += $1.value * $2; }
402
{ PC.rel_seconds += $1; }
409
&& ! PC.rels_seen && (PC.times_seen || 2 < $1.digits))
416
PC.day = $1.value % 100;
417
PC.month = ($1.value / 100) % 100;
418
PC.year.value = $1.value / 10000;
419
PC.year.digits = $1.digits - 4;
431
PC.hour = $1.value / 100;
432
PC.minutes = $1.value % 100;
450
/* Include this file down here because bison inserts code above which
451
may define-away `const'. We want the prototype for get_date to have
452
the same signature as the function definition. */
453
#include "modules/getdate.h"
456
struct tm *gmtime (const time_t *);
459
struct tm *localtime (const time_t *);
462
time_t mktime (struct tm *);
465
static table const meridian_table[] =
467
{ "AM", tMERIDIAN, MERam },
468
{ "A.M.", tMERIDIAN, MERam },
469
{ "PM", tMERIDIAN, MERpm },
470
{ "P.M.", tMERIDIAN, MERpm },
474
static table const dst_table[] =
479
static table const month_and_day_table[] =
481
{ "JANUARY", tMONTH, 1 },
482
{ "FEBRUARY", tMONTH, 2 },
483
{ "MARCH", tMONTH, 3 },
484
{ "APRIL", tMONTH, 4 },
485
{ "MAY", tMONTH, 5 },
486
{ "JUNE", tMONTH, 6 },
487
{ "JULY", tMONTH, 7 },
488
{ "AUGUST", tMONTH, 8 },
489
{ "SEPTEMBER",tMONTH, 9 },
490
{ "SEPT", tMONTH, 9 },
491
{ "OCTOBER", tMONTH, 10 },
492
{ "NOVEMBER", tMONTH, 11 },
493
{ "DECEMBER", tMONTH, 12 },
494
{ "SUNDAY", tDAY, 0 },
495
{ "MONDAY", tDAY, 1 },
496
{ "TUESDAY", tDAY, 2 },
498
{ "WEDNESDAY",tDAY, 3 },
499
{ "WEDNES", tDAY, 3 },
500
{ "THURSDAY", tDAY, 4 },
502
{ "THURS", tDAY, 4 },
503
{ "FRIDAY", tDAY, 5 },
504
{ "SATURDAY", tDAY, 6 },
508
static table const time_units_table[] =
510
{ "YEAR", tYEAR_UNIT, 1 },
511
{ "MONTH", tMONTH_UNIT, 1 },
512
{ "FORTNIGHT",tDAY_UNIT, 14 },
513
{ "WEEK", tDAY_UNIT, 7 },
514
{ "DAY", tDAY_UNIT, 1 },
515
{ "HOUR", tHOUR_UNIT, 1 },
516
{ "MINUTE", tMINUTE_UNIT, 1 },
517
{ "MIN", tMINUTE_UNIT, 1 },
518
{ "SECOND", tSEC_UNIT, 1 },
519
{ "SEC", tSEC_UNIT, 1 },
523
/* Assorted relative-time words. */
524
static table const relative_time_table[] =
526
{ "TOMORROW", tMINUTE_UNIT, 24 * 60 },
527
{ "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
528
{ "TODAY", tMINUTE_UNIT, 0 },
529
{ "NOW", tMINUTE_UNIT, 0 },
530
{ "LAST", tUNUMBER, -1 },
531
{ "THIS", tUNUMBER, 0 },
532
{ "NEXT", tUNUMBER, 1 },
533
{ "FIRST", tUNUMBER, 1 },
534
/*{ "SECOND", tUNUMBER, 2 }, */
535
{ "THIRD", tUNUMBER, 3 },
536
{ "FOURTH", tUNUMBER, 4 },
537
{ "FIFTH", tUNUMBER, 5 },
538
{ "SIXTH", tUNUMBER, 6 },
539
{ "SEVENTH", tUNUMBER, 7 },
540
{ "EIGHTH", tUNUMBER, 8 },
541
{ "NINTH", tUNUMBER, 9 },
542
{ "TENTH", tUNUMBER, 10 },
543
{ "ELEVENTH", tUNUMBER, 11 },
544
{ "TWELFTH", tUNUMBER, 12 },
549
/* The time zone table. This table is necessarily incomplete, as time
550
zone abbreviations are ambiguous; e.g. Australians interpret "EST"
551
as Eastern time in Australia, not as US Eastern Standard Time.
552
You cannot rely on getdate to handle arbitrary time zone
553
abbreviations; use numeric abbreviations like `-0500' instead. */
554
static table const time_zone_table[] =
556
{ "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
557
{ "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
558
{ "UTC", tZONE, HOUR ( 0) },
559
{ "WET", tZONE, HOUR ( 0) }, /* Western European */
560
{ "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
561
{ "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
562
{ "ART", tZONE, -HOUR ( 3) }, /* Argentina */
563
{ "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
564
{ "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
565
{ "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
566
{ "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
567
{ "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
568
{ "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
569
{ "CLT", tZONE, -HOUR ( 4) }, /* Chile */
570
{ "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
571
{ "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
572
{ "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
573
{ "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
574
{ "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
575
{ "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
576
{ "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
577
{ "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
578
{ "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
579
{ "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
580
{ "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
581
{ "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
582
{ "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
583
{ "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
584
{ "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
585
{ "WAT", tZONE, HOUR ( 1) }, /* West Africa */
586
{ "CET", tZONE, HOUR ( 1) }, /* Central European */
587
{ "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
588
{ "MET", tZONE, HOUR ( 1) }, /* Middle European */
589
{ "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
590
{ "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
591
{ "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
592
{ "EET", tZONE, HOUR ( 2) }, /* Eastern European */
593
{ "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
594
{ "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
595
{ "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
596
{ "EAT", tZONE, HOUR ( 3) }, /* East Africa */
597
{ "MSK", tZONE, HOUR ( 3) }, /* Moscow */
598
{ "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
599
{ "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
600
{ "SGT", tZONE, HOUR ( 8) }, /* Singapore */
601
{ "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
602
{ "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
603
{ "GST", tZONE, HOUR (10) }, /* Guam Standard */
604
{ "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
605
{ "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
609
/* Military time zone table. */
610
static table const military_table[] =
612
{ "A", tZONE, -HOUR ( 1) },
613
{ "B", tZONE, -HOUR ( 2) },
614
{ "C", tZONE, -HOUR ( 3) },
615
{ "D", tZONE, -HOUR ( 4) },
616
{ "E", tZONE, -HOUR ( 5) },
617
{ "F", tZONE, -HOUR ( 6) },
618
{ "G", tZONE, -HOUR ( 7) },
619
{ "H", tZONE, -HOUR ( 8) },
620
{ "I", tZONE, -HOUR ( 9) },
621
{ "K", tZONE, -HOUR (10) },
622
{ "L", tZONE, -HOUR (11) },
623
{ "M", tZONE, -HOUR (12) },
624
{ "N", tZONE, HOUR ( 1) },
625
{ "O", tZONE, HOUR ( 2) },
626
{ "P", tZONE, HOUR ( 3) },
627
{ "Q", tZONE, HOUR ( 4) },
628
{ "R", tZONE, HOUR ( 5) },
629
{ "S", tZONE, HOUR ( 6) },
630
{ "T", tZONE, HOUR ( 7) },
631
{ "U", tZONE, HOUR ( 8) },
632
{ "V", tZONE, HOUR ( 9) },
633
{ "W", tZONE, HOUR (10) },
634
{ "X", tZONE, HOUR (11) },
635
{ "Y", tZONE, HOUR (12) },
636
{ "Z", tZONE, HOUR ( 0) },
643
to_hour (int hours, int meridian)
648
return 0 <= hours && hours < 24 ? hours : -1;
650
return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
652
return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
661
to_year (textint textyear)
663
int year = textyear.value;
668
/* XPG4 suggests that years 00-68 map to 2000-2068, and
669
years 69-99 map to 1969-1999. */
670
if (textyear.digits == 2)
671
year += year < 69 ? 2000 : 1900;
677
lookup_zone (parser_control const *pc, char const *name)
681
/* Try local zone abbreviations first; they're more likely to be right. */
682
for (tp = pc->local_time_zone_table; tp->name; tp++)
683
if (strcmp (name, tp->name) == 0)
686
for (tp = time_zone_table; tp->name; tp++)
687
if (strcmp (name, tp->name) == 0)
694
/* Yield the difference between *A and *B,
695
measured in seconds, ignoring leap seconds.
696
The body of this function is taken directly from the GNU C Library;
697
see src/strftime.c. */
699
tm_diff (struct tm const *a, struct tm const *b)
701
/* Compute intervening leap days correctly even if year is negative.
702
Take care to avoid int overflow in leap day calculations,
703
but it's OK to assume that A and B are close to each other. */
704
int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
705
int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
706
int a100 = a4 / 25 - (a4 % 25 < 0);
707
int b100 = b4 / 25 - (b4 % 25 < 0);
708
int a400 = a100 >> 2;
709
int b400 = b100 >> 2;
710
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
711
int years = a->tm_year - b->tm_year;
712
int days = (365 * years + intervening_leap_days
713
+ (a->tm_yday - b->tm_yday));
714
return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
715
+ (a->tm_min - b->tm_min))
716
+ (a->tm_sec - b->tm_sec));
718
#endif /* ! HAVE_TM_GMTOFF */
721
lookup_word (parser_control const *pc, char *word)
730
/* Make it uppercase. */
731
for (p = word; *p; p++)
732
if (ISLOWER ((unsigned char) *p))
733
*p = toupper ((unsigned char) *p);
735
for (tp = meridian_table; tp->name; tp++)
736
if (strcmp (word, tp->name) == 0)
739
/* See if we have an abbreviation for a month. */
740
wordlen = strlen (word);
741
abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
743
for (tp = month_and_day_table; tp->name; tp++)
744
if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
747
if ((tp = lookup_zone (pc, word)))
750
if (strcmp (word, dst_table[0].name) == 0)
753
for (tp = time_units_table; tp->name; tp++)
754
if (strcmp (word, tp->name) == 0)
757
/* Strip off any plural and try the units table again. */
758
if (word[wordlen - 1] == 'S')
760
word[wordlen - 1] = '\0';
761
for (tp = time_units_table; tp->name; tp++)
762
if (strcmp (word, tp->name) == 0)
764
word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
767
for (tp = relative_time_table; tp->name; tp++)
768
if (strcmp (word, tp->name) == 0)
771
/* Military time zones. */
773
for (tp = military_table; tp->name; tp++)
774
if (word[0] == tp->name[0])
777
/* Drop out any periods and try the time zone table again. */
778
for (i = 0, p = q = word; (*p = *q); q++)
783
if (i && (tp = lookup_zone (pc, word)))
790
yylex (YYSTYPE *lvalp, parser_control *pc)
797
while (c = *pc->input, ISSPACE (c))
800
if (ISDIGIT (c) || c == '-' || c == '+')
805
if (c == '-' || c == '+')
807
sign = c == '-' ? -1 : 1;
810
/* skip the '-' sign */
819
value = 10 * value + c - '0';
823
lvalp->textintval.value = sign < 0 ? -value : value;
824
lvalp->textintval.digits = p - pc->input;
826
return sign ? tSNUMBER : tUNUMBER;
837
if (p < buff + sizeof buff - 1)
841
while (ISALPHA (c) || c == '.');
844
tp = lookup_word (pc, buff);
847
lvalp->intval = tp->value;
868
/* Do nothing if the parser reports an error. */
870
yyerror (const char *s ATTRIBUTE_UNUSED)
875
/* Parse a date/time string P. Return the corresponding time_t value,
876
or (time_t) -1 if there is an error. P can be an incomplete or
877
relative time specification; if so, use *NOW as the basis for the
880
get_date (const char *p, const time_t *now)
882
time_t Start = now ? *now : time (0);
883
struct tm *tmp = localtime (&Start);
892
pc.year.value = tmp->tm_year + TM_YEAR_BASE;
894
pc.month = tmp->tm_mon + 1;
895
pc.day = tmp->tm_mday;
896
pc.hour = tmp->tm_hour;
897
pc.minutes = tmp->tm_min;
898
pc.seconds = tmp->tm_sec;
899
tm.tm_isdst = tmp->tm_isdst;
912
pc.local_zones_seen = 0;
915
#if HAVE_STRUCT_TM_TM_ZONE
916
pc.local_time_zone_table[0].name = tmp->tm_zone;
917
pc.local_time_zone_table[0].type = tLOCAL_ZONE;
918
pc.local_time_zone_table[0].value = tmp->tm_isdst;
919
pc.local_time_zone_table[1].name = 0;
921
/* Probe the names used in the next three calendar quarters, looking
922
for a tm_isdst different from the one we already have. */
925
for (quarter = 1; quarter <= 3; quarter++)
927
time_t probe = Start + quarter * (90 * 24 * 60 * 60);
928
struct tm *probe_tm = localtime (&probe);
929
if (probe_tm && probe_tm->tm_zone
930
&& probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
933
pc.local_time_zone_table[1].name = probe_tm->tm_zone;
934
pc.local_time_zone_table[1].type = tLOCAL_ZONE;
935
pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
936
pc.local_time_zone_table[2].name = 0;
946
extern char *tzname[];
949
for (i = 0; i < 2; i++)
951
pc.local_time_zone_table[i].name = tzname[i];
952
pc.local_time_zone_table[i].type = tLOCAL_ZONE;
953
pc.local_time_zone_table[i].value = i;
955
pc.local_time_zone_table[i].name = 0;
958
pc.local_time_zone_table[0].name = 0;
962
if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
963
&& ! strcmp (pc.local_time_zone_table[0].name,
964
pc.local_time_zone_table[1].name))
966
/* This locale uses the same abbrevation for standard and
967
daylight times. So if we see that abbreviation, we don't
968
know whether it's daylight time. */
969
pc.local_time_zone_table[0].value = -1;
970
pc.local_time_zone_table[1].name = 0;
973
if (yyparse (&pc) != 0
974
|| 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
975
|| 1 < (pc.local_zones_seen + pc.zones_seen)
976
|| (pc.local_zones_seen && 1 < pc.local_isdst))
979
tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
980
tm.tm_mon = pc.month - 1 + pc.rel_month;
981
tm.tm_mday = pc.day + pc.rel_day;
982
if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
984
tm.tm_hour = to_hour (pc.hour, pc.meridian);
987
tm.tm_min = pc.minutes;
988
tm.tm_sec = pc.seconds;
992
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
995
/* Let mktime deduce tm_isdst if we have an absolute time stamp,
996
or if the relative time stamp mentions days, months, or years. */
997
if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
998
| pc.rel_month | pc.rel_year)
1001
/* But if the input explicitly specifies local time with or without
1002
DST, give mktime that information. */
1003
if (pc.local_zones_seen)
1004
tm.tm_isdst = pc.local_isdst;
1008
Start = mktime (&tm);
1010
if (Start == (time_t) -1)
1013
/* Guard against falsely reporting errors near the time_t boundaries
1014
when parsing times in other time zones. For example, if the min
1015
time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
1016
of UTC, then the min localtime value is 1970-01-01 08:00:00; if
1017
we apply mktime to 1970-01-01 00:00:00 we will get an error, so
1018
we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
1019
zone by 24 hours to compensate. This algorithm assumes that
1020
there is no DST transition within a day of the time_t boundaries. */
1024
if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
1027
pc.time_zone += 24 * 60;
1032
pc.time_zone -= 24 * 60;
1034
Start = mktime (&tm);
1037
if (Start == (time_t) -1)
1041
if (pc.days_seen && ! pc.dates_seen)
1043
tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1044
+ 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1046
Start = mktime (&tm);
1047
if (Start == (time_t) -1)
1053
int delta = pc.time_zone * 60;
1054
#ifdef HAVE_TM_GMTOFF
1055
delta -= tm.tm_gmtoff;
1057
struct tm *gmt = gmtime (&Start);
1060
delta -= tm_diff (&tm, gmt);
1062
if ((Start < Start - delta) != (delta < 0))
1063
return -1; /* time_t overflow */
1067
/* Add relative hours, minutes, and seconds. Ignore leap seconds;
1068
i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
1069
leap second. Typically this is not what the user wants, but it's
1070
too hard to do it the other way, because the time zone indicator
1071
must be applied before relative times, and if mktime is applied
1072
again the time zone will be lost. */
1075
long d1 = 60 * 60 * (long) pc.rel_hour;
1076
time_t t1 = t0 + d1;
1077
long d2 = 60 * (long) pc.rel_minutes;
1078
time_t t2 = t1 + d2;
1079
int d3 = pc.rel_seconds;
1080
time_t t3 = t2 + d3;
1081
if ((d1 / (60 * 60) ^ pc.rel_hour)
1082
| (d2 / 60 ^ pc.rel_minutes)
1083
| ((t0 + d1 < t0) ^ (d1 < 0))
1084
| ((t1 + d2 < t1) ^ (d2 < 0))
1085
| ((t2 + d3 < t2) ^ (d3 < 0)))
1098
main (int ac, char **av)
1103
printf ("Enter date, or blank line to exit.\n\t> ");
1106
buff[BUFSIZ - 1] = 0;
1107
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1109
d = get_date (buff, 0);
1110
if (d == (time_t) -1)
1111
printf ("Bad format - couldn't convert.\n");
1113
printf ("%s", ctime (&d));
1119
#endif /* defined TEST */