Sat Apr 23 13:02:04 2022 UTC ()
fix date -d 12/01/2022, found by Anon Ymous


(christos)
diff -r1.36 -r1.37 src/lib/libutil/parsedate.y

cvs diff -r1.36 -r1.37 src/lib/libutil/parsedate.y (switch to unified diff)

--- src/lib/libutil/parsedate.y 2020/10/30 22:03:11 1.36
+++ src/lib/libutil/parsedate.y 2022/04/23 13:02:04 1.37
@@ -1,1355 +1,1355 @@ @@ -1,1355 +1,1355 @@
1%{ 1%{
2/* 2/*
3** Originally written by Steven M. Bellovin <smb@research.att.com> while 3** Originally written by Steven M. Bellovin <smb@research.att.com> while
4** at the University of North Carolina at Chapel Hill. Later tweaked by 4** at the University of North Carolina at Chapel Hill. Later tweaked by
5** a couple of people on Usenet. Completely overhauled by Rich $alz 5** a couple of people on Usenet. Completely overhauled by Rich $alz
6** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; 6** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
7** 7**
8** This grammar has 10 shift/reduce conflicts. 8** This grammar has 10 shift/reduce conflicts.
9** 9**
10** This code is in the public domain and has no copyright. 10** This code is in the public domain and has no copyright.
11*/ 11*/
12/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ 12/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13/* SUPPRESS 288 on yyerrlab *//* Label unused */ 13/* SUPPRESS 288 on yyerrlab *//* Label unused */
14 14
15#include <sys/cdefs.h> 15#include <sys/cdefs.h>
16#ifdef __RCSID 16#ifdef __RCSID
17__RCSID("$NetBSD: parsedate.y,v 1.36 2020/10/30 22:03:11 kre Exp $"); 17__RCSID("$NetBSD: parsedate.y,v 1.37 2022/04/23 13:02:04 christos Exp $");
18#endif 18#endif
19 19
20#include <stdio.h> 20#include <stdio.h>
21#include <ctype.h> 21#include <ctype.h>
22#include <errno.h> 22#include <errno.h>
23#include <limits.h> 23#include <limits.h>
24#include <string.h> 24#include <string.h>
25#include <time.h> 25#include <time.h>
26#include <util.h> 26#include <util.h>
27#include <stdlib.h> 27#include <stdlib.h>
28 28
29/* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS 29/* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
30 releases): 30 releases):
31 31
32 We don't want to mess with all the portability hassles of alloca. 32 We don't want to mess with all the portability hassles of alloca.
33 In particular, most (all?) versions of bison will use alloca in 33 In particular, most (all?) versions of bison will use alloca in
34 their parser. If bison works on your system (e.g. it should work 34 their parser. If bison works on your system (e.g. it should work
35 with gcc), then go ahead and use it, but the more general solution 35 with gcc), then go ahead and use it, but the more general solution
36 is to use byacc instead of bison, which should generate a portable 36 is to use byacc instead of bison, which should generate a portable
37 parser. I played with adding "#define alloca dont_use_alloca", to 37 parser. I played with adding "#define alloca dont_use_alloca", to
38 give an error if the parser generator uses alloca (and thus detect 38 give an error if the parser generator uses alloca (and thus detect
39 unportable parsedate.c's), but that seems to cause as many problems 39 unportable parsedate.c's), but that seems to cause as many problems
40 as it solves. */ 40 as it solves. */
41 41
42#define EPOCH 1970 42#define EPOCH 1970
43#define HOUR(x) ((time_t)((x) * 60)) 43#define HOUR(x) ((time_t)((x) * 60))
44#define SECSPERDAY (24L * 60L * 60L) 44#define SECSPERDAY (24L * 60L * 60L)
45 45
46#define MAXREL 16 /* hours mins secs days weeks months years - maybe twice each ...*/ 46#define MAXREL 16 /* hours mins secs days weeks months years - maybe twice each ...*/
47 47
48#define USE_LOCAL_TIME 99999 /* special case for Convert() and yyTimezone */ 48#define USE_LOCAL_TIME 99999 /* special case for Convert() and yyTimezone */
49 49
50/* 50/*
51** An entry in the lexical lookup table. 51** An entry in the lexical lookup table.
52*/ 52*/
53typedef struct _TABLE { 53typedef struct _TABLE {
54 const char *name; 54 const char *name;
55 int type; 55 int type;
56 time_t value; 56 time_t value;
57} TABLE; 57} TABLE;
58 58
59 59
60/* 60/*
61** Daylight-savings mode: on, off, or not yet known. 61** Daylight-savings mode: on, off, or not yet known.
62*/ 62*/
63typedef enum _DSTMODE { 63typedef enum _DSTMODE {
64 DSTon, DSToff, DSTmaybe 64 DSTon, DSToff, DSTmaybe
65} DSTMODE; 65} DSTMODE;
66 66
67/* 67/*
68** Meridian: am, pm, or 24-hour style (plus "noon" and "midnight"). 68** Meridian: am, pm, or 24-hour style (plus "noon" and "midnight").
69*/ 69*/
70typedef enum _MERIDIAN { 70typedef enum _MERIDIAN {
71 MERam, MERpm, MER24, MER_NOON, MER_MN 71 MERam, MERpm, MER24, MER_NOON, MER_MN
72} MERIDIAN; 72} MERIDIAN;
73 73
74 74
75struct dateinfo { 75struct dateinfo {
76 DSTMODE yyDSTmode; /* DST on/off/maybe */ 76 DSTMODE yyDSTmode; /* DST on/off/maybe */
77 time_t yyDayOrdinal; 77 time_t yyDayOrdinal;
78 time_t yyDayNumber; 78 time_t yyDayNumber;
79 int yyHaveDate; 79 int yyHaveDate;
80 int yyHaveFullYear; /* if true, year is not abbreviated. */ 80 int yyHaveFullYear; /* if true, year is not abbreviated. */
81 /* if false, need to call AdjustYear(). */ 81 /* if false, need to call AdjustYear(). */
82 int yyHaveDay; 82 int yyHaveDay;
83 int yyHaveRel; 83 int yyHaveRel;
84 int yyHaveTime; 84 int yyHaveTime;
85 int yyHaveZone; 85 int yyHaveZone;
86 time_t yyTimezone; /* Timezone as minutes ahead/east of UTC */ 86 time_t yyTimezone; /* Timezone as minutes ahead/east of UTC */
87 time_t yyDay; /* Day of month [1-31] */ 87 time_t yyDay; /* Day of month [1-31] */
88 time_t yyHour; /* Hour of day [0-24] or [1-12] */ 88 time_t yyHour; /* Hour of day [0-24] or [1-12] */
89 time_t yyMinutes; /* Minute of hour [0-59] */ 89 time_t yyMinutes; /* Minute of hour [0-59] */
90 time_t yyMonth; /* Month of year [1-12] */ 90 time_t yyMonth; /* Month of year [1-12] */
91 time_t yySeconds; /* Second of minute [0-60] */ 91 time_t yySeconds; /* Second of minute [0-60] */
92 time_t yyYear; /* Year, see also yyHaveFullYear */ 92 time_t yyYear; /* Year, see also yyHaveFullYear */
93 MERIDIAN yyMeridian; /* Interpret yyHour as AM/PM/24 hour clock */ 93 MERIDIAN yyMeridian; /* Interpret yyHour as AM/PM/24 hour clock */
94 struct { 94 struct {
95 time_t yyRelVal; 95 time_t yyRelVal;
96 int yyRelMonth; 96 int yyRelMonth;
97 } yyRel[MAXREL]; 97 } yyRel[MAXREL];
98}; 98};
99 99
100static int RelVal(struct dateinfo *, time_t, time_t, int, int); 100static int RelVal(struct dateinfo *, time_t, time_t, int, int);
101 101
102#define CheckRelVal(a, b, c, d, e) do { \ 102#define CheckRelVal(a, b, c, d, e) do { \
103 if (!RelVal((a), (b), (c), (d), (e))) { \ 103 if (!RelVal((a), (b), (c), (d), (e))) { \
104 YYREJECT; \ 104 YYREJECT; \
105 } \ 105 } \
106 } while (0) 106 } while (0)
107 107
108%} 108%}
109 109
110%union { 110%union {
111 time_t Number; 111 time_t Number;
112 enum _MERIDIAN Meridian; 112 enum _MERIDIAN Meridian;
113} 113}
114 114
115%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT 115%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
116%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN tTIME 116%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN tTIME
117 117
118%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT 118%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
119%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE tTIME 119%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE tTIME
120%type <Meridian> tMERIDIAN 120%type <Meridian> tMERIDIAN
121 121
122%type <Number> at_number 122%type <Number> at_number
123%type <Meridian> o_merid 123%type <Meridian> o_merid
124 124
125%parse-param { struct dateinfo *param } 125%parse-param { struct dateinfo *param }
126%parse-param { const char **yyInput } 126%parse-param { const char **yyInput }
127%lex-param { const char **yyInput } 127%lex-param { const char **yyInput }
128%pure-parser 128%pure-parser
129 129
130%% 130%%
131 131
132spec: 132spec:
133 /* empty */ 133 /* empty */
134 | spec item 134 | spec item
135; 135;
136 136
137item: 137item:
138 time { param->yyHaveTime++; } 138 time { param->yyHaveTime++; }
139 | time_numericzone { param->yyHaveTime++; param->yyHaveZone++; } 139 | time_numericzone { param->yyHaveTime++; param->yyHaveZone++; }
140 | zone { param->yyHaveZone++; } 140 | zone { param->yyHaveZone++; }
141 | date { param->yyHaveDate++; } 141 | date { param->yyHaveDate++; }
142 | day { param->yyHaveDay++; } 142 | day { param->yyHaveDay++; }
143 | rel { param->yyHaveRel++; } 143 | rel { param->yyHaveRel++; }
144 | cvsstamp { param->yyHaveTime++; param->yyHaveDate++; 144 | cvsstamp { param->yyHaveTime++; param->yyHaveDate++;
145 param->yyHaveZone++; } 145 param->yyHaveZone++; }
146 | epochdate { param->yyHaveTime++; param->yyHaveDate++;  146 | epochdate { param->yyHaveTime++; param->yyHaveDate++;
147 param->yyHaveZone++; } 147 param->yyHaveZone++; }
148 | number 148 | number
149; 149;
150 150
151cvsstamp: 151cvsstamp:
152 tUNUMBER '.' tUNUMBER '.' tUNUMBER '.'  152 tUNUMBER '.' tUNUMBER '.' tUNUMBER '.'
153 tUNUMBER '.' tUNUMBER '.' tUNUMBER { 153 tUNUMBER '.' tUNUMBER '.' tUNUMBER {
154 param->yyYear = $1; 154 param->yyYear = $1;
155 if (param->yyYear < 100) { 155 if (param->yyYear < 100) {
156 param->yyYear += 1900; 156 param->yyYear += 1900;
157 } 157 }
158 param->yyHaveFullYear = 1; 158 param->yyHaveFullYear = 1;
159 param->yyMonth = $3; 159 param->yyMonth = $3;
160 param->yyDay = $5; 160 param->yyDay = $5;
161 param->yyHour = $7; 161 param->yyHour = $7;
162 param->yyMinutes = $9; 162 param->yyMinutes = $9;
163 param->yySeconds = $11; 163 param->yySeconds = $11;
164 param->yyDSTmode = DSToff; 164 param->yyDSTmode = DSToff;
165 param->yyTimezone = 0; 165 param->yyTimezone = 0;
166 } 166 }
167; 167;
168 168
169epochdate: 169epochdate:
170 AT_SIGN at_number { 170 AT_SIGN at_number {
171 time_t when = $2; 171 time_t when = $2;
172 struct tm tmbuf; 172 struct tm tmbuf;
173 173
174 if (gmtime_r(&when, &tmbuf) != NULL) { 174 if (gmtime_r(&when, &tmbuf) != NULL) {
175 param->yyYear = tmbuf.tm_year + 1900; 175 param->yyYear = tmbuf.tm_year + 1900;
176 param->yyMonth = tmbuf.tm_mon + 1; 176 param->yyMonth = tmbuf.tm_mon + 1;
177 param->yyDay = tmbuf.tm_mday; 177 param->yyDay = tmbuf.tm_mday;
178 178
179 param->yyHour = tmbuf.tm_hour; 179 param->yyHour = tmbuf.tm_hour;
180 param->yyMinutes = tmbuf.tm_min; 180 param->yyMinutes = tmbuf.tm_min;
181 param->yySeconds = tmbuf.tm_sec; 181 param->yySeconds = tmbuf.tm_sec;
182 } else { 182 } else {
183 param->yyYear = EPOCH; 183 param->yyYear = EPOCH;
184 param->yyMonth = 1; 184 param->yyMonth = 1;
185 param->yyDay = 1; 185 param->yyDay = 1;
186 186
187 param->yyHour = 0; 187 param->yyHour = 0;
188 param->yyMinutes = 0; 188 param->yyMinutes = 0;
189 param->yySeconds = 0; 189 param->yySeconds = 0;
190 } 190 }
191 param->yyHaveFullYear = 1; 191 param->yyHaveFullYear = 1;
192 param->yyDSTmode = DSToff; 192 param->yyDSTmode = DSToff;
193 param->yyTimezone = 0; 193 param->yyTimezone = 0;
194 } 194 }
195; 195;
196 196
197at_number: 197at_number:
198 tUNUMBER 198 tUNUMBER
199 | tSNUMBER 199 | tSNUMBER
200; 200;
201 201
202time: 202time:
203 tUNUMBER tMERIDIAN { 203 tUNUMBER tMERIDIAN {
204 if ($1 > 24) 204 if ($1 > 24)
205 YYREJECT; 205 YYREJECT;
206 param->yyMinutes = 0; 206 param->yyMinutes = 0;
207 param->yySeconds = 0; 207 param->yySeconds = 0;
208 if ($2 == MER_NOON || $2 == MER_MN) { 208 if ($2 == MER_NOON || $2 == MER_MN) {
209 if ($1 == 12) { 209 if ($1 == 12) {
210 switch ($2) { 210 switch ($2) {
211 case MER_NOON: param->yyHour = 12; break; 211 case MER_NOON: param->yyHour = 12; break;
212 case MER_MN : param->yyHour = 0; break; 212 case MER_MN : param->yyHour = 0; break;
213 default: /* impossible */; break; 213 default: /* impossible */; break;
214 } 214 }
215 param->yyMeridian = MER24; 215 param->yyMeridian = MER24;
216 } else 216 } else
217 YYREJECT; 217 YYREJECT;
218 } else { 218 } else {
219 param->yyHour = $1; 219 param->yyHour = $1;
220 param->yyMeridian = $2; 220 param->yyMeridian = $2;
221 } 221 }
222 } 222 }
223 | tUNUMBER ':' tUNUMBER o_merid { 223 | tUNUMBER ':' tUNUMBER o_merid {
224 if ($1 > 24 || $3 >= 60) 224 if ($1 > 24 || $3 >= 60)
225 YYREJECT; 225 YYREJECT;
226 param->yyMinutes = $3; 226 param->yyMinutes = $3;
227 param->yySeconds = 0; 227 param->yySeconds = 0;
228 if ($4 == MER_NOON || $4 == MER_MN) { 228 if ($4 == MER_NOON || $4 == MER_MN) {
229 if ($1 == 12 && $3 == 0) { 229 if ($1 == 12 && $3 == 0) {
230 switch ($4) { 230 switch ($4) {
231 case MER_NOON: param->yyHour = 12; break; 231 case MER_NOON: param->yyHour = 12; break;
232 case MER_MN : param->yyHour = 0; break; 232 case MER_MN : param->yyHour = 0; break;
233 default: /* impossible */; break; 233 default: /* impossible */; break;
234 } 234 }
235 param->yyMeridian = MER24; 235 param->yyMeridian = MER24;
236 } else 236 } else
237 YYREJECT; 237 YYREJECT;
238 } else { 238 } else {
239 param->yyHour = $1; 239 param->yyHour = $1;
240 param->yyMeridian = $4; 240 param->yyMeridian = $4;
241 } 241 }
242 } 242 }
243 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { 243 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
244 if ($1 > 24 || $3 >= 60 || $5 > 60) 244 if ($1 > 24 || $3 >= 60 || $5 > 60)
245 YYREJECT; 245 YYREJECT;
246 param->yyMinutes = $3; 246 param->yyMinutes = $3;
247 param->yySeconds = $5; 247 param->yySeconds = $5;
248 if ($6 == MER_NOON || $6 == MER_MN) { 248 if ($6 == MER_NOON || $6 == MER_MN) {
249 if ($1 == 12 && $3 == 0 && $5 == 0) { 249 if ($1 == 12 && $3 == 0 && $5 == 0) {
250 switch ($6) { 250 switch ($6) {
251 case MER_NOON: param->yyHour = 12; break; 251 case MER_NOON: param->yyHour = 12; break;
252 case MER_MN : param->yyHour = 0; break; 252 case MER_MN : param->yyHour = 0; break;
253 default: /* impossible */; break; 253 default: /* impossible */; break;
254 } 254 }
255 param->yyMeridian = MER24; 255 param->yyMeridian = MER24;
256 } else 256 } else
257 YYREJECT; 257 YYREJECT;
258 } else { 258 } else {
259 param->yyHour = $1; 259 param->yyHour = $1;
260 param->yyMeridian = $6; 260 param->yyMeridian = $6;
261 } 261 }
262 } 262 }
263 | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER { 263 | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
264 if ($1 > 24 || $3 >= 60 || $5 > 60) 264 if ($1 > 24 || $3 >= 60 || $5 > 60)
265 YYREJECT; 265 YYREJECT;
266 param->yyHour = $1; 266 param->yyHour = $1;
267 param->yyMinutes = $3; 267 param->yyMinutes = $3;
268 param->yySeconds = $5; 268 param->yySeconds = $5;
269 param->yyMeridian = MER24; 269 param->yyMeridian = MER24;
270 /* XXX: Do nothing with fractional secs ($7) */ 270 /* XXX: Do nothing with fractional secs ($7) */
271 } 271 }
272 | tUNUMBER ':' tUNUMBER ':' tUNUMBER ',' tUNUMBER { 272 | tUNUMBER ':' tUNUMBER ':' tUNUMBER ',' tUNUMBER {
273 if ($1 > 24 || $3 >= 60 || $5 > 60) 273 if ($1 > 24 || $3 >= 60 || $5 > 60)
274 YYREJECT; 274 YYREJECT;
275 param->yyHour = $1; 275 param->yyHour = $1;
276 param->yyMinutes = $3; 276 param->yyMinutes = $3;
277 param->yySeconds = $5; 277 param->yySeconds = $5;
278 param->yyMeridian = MER24; 278 param->yyMeridian = MER24;
279 /* XXX: Do nothing with fractional seconds ($7) */ 279 /* XXX: Do nothing with fractional seconds ($7) */
280 } 280 }
281 | tTIME { 281 | tTIME {
282 param->yyHour = $1; 282 param->yyHour = $1;
283 param->yyMinutes = 0; 283 param->yyMinutes = 0;
284 param->yySeconds = 0; 284 param->yySeconds = 0;
285 param->yyMeridian = MER24; 285 param->yyMeridian = MER24;
286 /* Tues midnight --> Weds 00:00, midnight Tues -> Tues 00:00 */ 286 /* Tues midnight --> Weds 00:00, midnight Tues -> Tues 00:00 */
287 if ($1 == 0 && param->yyHaveDay) 287 if ($1 == 0 && param->yyHaveDay)
288 param->yyDayNumber++; 288 param->yyDayNumber++;
289 } 289 }
290 | tUNUMBER tTIME { 290 | tUNUMBER tTIME {
291 if ($1 == 12 && ($2 == 0 || $2 == 12)) { 291 if ($1 == 12 && ($2 == 0 || $2 == 12)) {
292 param->yyHour = $2; 292 param->yyHour = $2;
293 param->yyMinutes = 0; 293 param->yyMinutes = 0;
294 param->yySeconds = 0; 294 param->yySeconds = 0;
295 param->yyMeridian = MER24; 295 param->yyMeridian = MER24;
296 } else 296 } else
297 YYREJECT; 297 YYREJECT;
298 } 298 }
299; 299;
300 300
301time_numericzone: 301time_numericzone:
302 tUNUMBER ':' tUNUMBER tSNUMBER { 302 tUNUMBER ':' tUNUMBER tSNUMBER {
303 if ($4 < -(47 * 100 + 59) || $4 > (47 * 100 + 59)) 303 if ($4 < -(47 * 100 + 59) || $4 > (47 * 100 + 59))
304 YYREJECT; 304 YYREJECT;
305 if ($1 > 24 || $3 > 59) 305 if ($1 > 24 || $3 > 59)
306 YYREJECT; 306 YYREJECT;
307 param->yyHour = $1; 307 param->yyHour = $1;
308 param->yyMinutes = $3; 308 param->yyMinutes = $3;
309 param->yyMeridian = MER24; 309 param->yyMeridian = MER24;
310 param->yyDSTmode = DSToff; 310 param->yyDSTmode = DSToff;
311 param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60); 311 param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
312 } 312 }
313 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { 313 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
314 if ($6 < -(47 * 100 + 59) || $6 > (47 * 100 + 59)) 314 if ($6 < -(47 * 100 + 59) || $6 > (47 * 100 + 59))
315 YYREJECT; 315 YYREJECT;
316 if ($1 > 24 || $3 > 59 || $5 > 60) 316 if ($1 > 24 || $3 > 59 || $5 > 60)
317 YYREJECT; 317 YYREJECT;
318 param->yyHour = $1; 318 param->yyHour = $1;
319 param->yyMinutes = $3; 319 param->yyMinutes = $3;
320 param->yySeconds = $5; 320 param->yySeconds = $5;
321 param->yyMeridian = MER24; 321 param->yyMeridian = MER24;
322 param->yyDSTmode = DSToff; 322 param->yyDSTmode = DSToff;
323 param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60); 323 param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
324 } 324 }
325; 325;
326 326
327zone: 327zone:
328 tZONE { param->yyTimezone = $1; param->yyDSTmode = DSToff; } 328 tZONE { param->yyTimezone = $1; param->yyDSTmode = DSToff; }
329 | tDAYZONE { param->yyTimezone = $1; param->yyDSTmode = DSTon; } 329 | tDAYZONE { param->yyTimezone = $1; param->yyDSTmode = DSTon; }
330 | tZONE tDST { param->yyTimezone = $1; param->yyDSTmode = DSTon; } 330 | tZONE tDST { param->yyTimezone = $1; param->yyDSTmode = DSTon; }
331 | tSNUMBER { 331 | tSNUMBER {
332 if (param->yyHaveDate == 0 && param->yyHaveTime == 0) 332 if (param->yyHaveDate == 0 && param->yyHaveTime == 0)
333 YYREJECT; 333 YYREJECT;
334 if ($1 < -(47 * 100 + 59) || $1 > (47 * 100 + 59)) 334 if ($1 < -(47 * 100 + 59) || $1 > (47 * 100 + 59))
335 YYREJECT; 335 YYREJECT;
336 param->yyTimezone = - ($1 % 100 + ($1 / 100) * 60); 336 param->yyTimezone = - ($1 % 100 + ($1 / 100) * 60);
337 param->yyDSTmode = DSTmaybe; 337 param->yyDSTmode = DSTmaybe;
338 } 338 }
339; 339;
340 340
341day: 341day:
342 tDAY { param->yyDayOrdinal = 1; param->yyDayNumber = $1; } 342 tDAY { param->yyDayOrdinal = 1; param->yyDayNumber = $1; }
343 | tDAY ',' { param->yyDayOrdinal = 1; param->yyDayNumber = $1; } 343 | tDAY ',' { param->yyDayOrdinal = 1; param->yyDayNumber = $1; }
344 | tUNUMBER tDAY { param->yyDayOrdinal = $1; param->yyDayNumber = $2; } 344 | tUNUMBER tDAY { param->yyDayOrdinal = $1; param->yyDayNumber = $2; }
345; 345;
346 346
347date: 347date:
348 tUNUMBER '/' tUNUMBER { 348 tUNUMBER '/' tUNUMBER {
349 if ($1 > 12 || $3 > 31 || $1 == 0 || $3 == 0) 349 if ($1 > 12 || $3 > 31 || $1 == 0 || $3 == 0)
350 YYREJECT; 350 YYREJECT;
351 param->yyMonth = $1; 351 param->yyMonth = $1;
352 param->yyDay = $3; 352 param->yyDay = $3;
353 } 353 }
354 | tUNUMBER '/' tUNUMBER '/' tUNUMBER { 354 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
355 if ($1 >= 100) { 355 if ($1 >= 100) {
356 if ($3 > 12 || $5 > 31 || $3 == 0 || $5 == 0) 356 if ($3 > 12 || $5 > 31 || $3 == 0 || $5 == 0)
357 YYREJECT; 357 YYREJECT;
358 param->yyYear = $1; 358 param->yyYear = $1;
359 param->yyMonth = $3; 359 param->yyMonth = $3;
360 param->yyDay = $5; 360 param->yyDay = $5;
361 } else { 361 } else {
362 if ($1 >= 12 || $3 > 31 || $1 == 0 || $3 == 0) 362 if ($1 > 12 || $3 > 31 || $1 == 0 || $3 == 0)
363 YYREJECT; 363 YYREJECT;
364 param->yyMonth = $1; 364 param->yyMonth = $1;
365 param->yyDay = $3; 365 param->yyDay = $3;
366 param->yyYear = $5; 366 param->yyYear = $5;
367 } 367 }
368 } 368 }
369 | tUNUMBER tSNUMBER tSNUMBER { 369 | tUNUMBER tSNUMBER tSNUMBER {
370 /* ISO 8601 format. yyyy-mm-dd. */ 370 /* ISO 8601 format. yyyy-mm-dd. */
371 if ($2 >= 0 || $2 < -12 || $3 >= 0 || $3 < -31) 371 if ($2 >= 0 || $2 < -12 || $3 >= 0 || $3 < -31)
372 YYREJECT; 372 YYREJECT;
373 param->yyYear = $1; 373 param->yyYear = $1;
374 param->yyHaveFullYear = 1; 374 param->yyHaveFullYear = 1;
375 param->yyMonth = -$2; 375 param->yyMonth = -$2;
376 param->yyDay = -$3; 376 param->yyDay = -$3;
377 } 377 }
378 | tUNUMBER tMONTH tSNUMBER { 378 | tUNUMBER tMONTH tSNUMBER {
379 if ($3 > 0 || $1 == 0 || $1 > 31) 379 if ($3 > 0 || $1 == 0 || $1 > 31)
380 YYREJECT; 380 YYREJECT;
381 /* e.g. 17-JUN-1992. */ 381 /* e.g. 17-JUN-1992. */
382 param->yyDay = $1; 382 param->yyDay = $1;
383 param->yyMonth = $2; 383 param->yyMonth = $2;
384 param->yyYear = -$3; 384 param->yyYear = -$3;
385 } 385 }
386 | tMONTH tUNUMBER { 386 | tMONTH tUNUMBER {
387 if ($2 == 0 || $2 > 31) 387 if ($2 == 0 || $2 > 31)
388 YYREJECT; 388 YYREJECT;
389 param->yyMonth = $1; 389 param->yyMonth = $1;
390 param->yyDay = $2; 390 param->yyDay = $2;
391 } 391 }
392 | tMONTH tUNUMBER ',' tUNUMBER { 392 | tMONTH tUNUMBER ',' tUNUMBER {
393 if ($2 == 0 || $2 > 31) 393 if ($2 == 0 || $2 > 31)
394 YYREJECT; 394 YYREJECT;
395 param->yyMonth = $1; 395 param->yyMonth = $1;
396 param->yyDay = $2; 396 param->yyDay = $2;
397 param->yyYear = $4; 397 param->yyYear = $4;
398 } 398 }
399 | tUNUMBER tMONTH { 399 | tUNUMBER tMONTH {
400 if ($1 == 0 || $1 > 31) 400 if ($1 == 0 || $1 > 31)
401 YYREJECT; 401 YYREJECT;
402 param->yyMonth = $2; 402 param->yyMonth = $2;
403 param->yyDay = $1; 403 param->yyDay = $1;
404 } 404 }
405 | tUNUMBER tMONTH tUNUMBER { 405 | tUNUMBER tMONTH tUNUMBER {
406 if ($1 > 31 && $3 > 31) 406 if ($1 > 31 && $3 > 31)
407 YYREJECT; 407 YYREJECT;
408 if ($1 < 35) { 408 if ($1 < 35) {
409 if ($1 == 0) 409 if ($1 == 0)
410 YYREJECT; 410 YYREJECT;
411 param->yyDay = $1; 411 param->yyDay = $1;
412 param->yyYear = $3; 412 param->yyYear = $3;
413 } else { 413 } else {
414 if ($3 == 0) 414 if ($3 == 0)
415 YYREJECT; 415 YYREJECT;
416 param->yyDay = $3; 416 param->yyDay = $3;
417 param->yyYear = $1; 417 param->yyYear = $1;
418 } 418 }
419 param->yyMonth = $2; 419 param->yyMonth = $2;
420 } 420 }
421; 421;
422 422
423rel: 423rel:
424 relunit 424 relunit
425 | relunit tAGO { 425 | relunit tAGO {
426 param->yyRel[param->yyHaveRel].yyRelVal = 426 param->yyRel[param->yyHaveRel].yyRelVal =
427 -param->yyRel[param->yyHaveRel].yyRelVal; 427 -param->yyRel[param->yyHaveRel].yyRelVal;
428 } 428 }
429; 429;
430 430
431relunit: 431relunit:
432 tUNUMBER tMINUTE_UNIT { CheckRelVal(param, $1, $2, 60, 0); } 432 tUNUMBER tMINUTE_UNIT { CheckRelVal(param, $1, $2, 60, 0); }
433 | tSNUMBER tMINUTE_UNIT { CheckRelVal(param, $1, $2, 60, 0); } 433 | tSNUMBER tMINUTE_UNIT { CheckRelVal(param, $1, $2, 60, 0); }
434 | tMINUTE_UNIT { CheckRelVal(param, 1, $1, 60, 0); } 434 | tMINUTE_UNIT { CheckRelVal(param, 1, $1, 60, 0); }
435 | tSNUMBER tSEC_UNIT { CheckRelVal(param, $1, 1, 1, 0); } 435 | tSNUMBER tSEC_UNIT { CheckRelVal(param, $1, 1, 1, 0); }
436 | tUNUMBER tSEC_UNIT { CheckRelVal(param, $1, 1, 1, 0); } 436 | tUNUMBER tSEC_UNIT { CheckRelVal(param, $1, 1, 1, 0); }
437 | tSEC_UNIT { CheckRelVal(param, 1, 1, 1, 0); } 437 | tSEC_UNIT { CheckRelVal(param, 1, 1, 1, 0); }
438 | tSNUMBER tMONTH_UNIT { CheckRelVal(param, $1, $2, 1, 1); } 438 | tSNUMBER tMONTH_UNIT { CheckRelVal(param, $1, $2, 1, 1); }
439 | tUNUMBER tMONTH_UNIT { CheckRelVal(param, $1, $2, 1, 1); } 439 | tUNUMBER tMONTH_UNIT { CheckRelVal(param, $1, $2, 1, 1); }
440 | tMONTH_UNIT { CheckRelVal(param, 1, $1, 1, 1); } 440 | tMONTH_UNIT { CheckRelVal(param, 1, $1, 1, 1); }
441; 441;
442 442
443number: 443number:
444 tUNUMBER { 444 tUNUMBER {
445 if (param->yyHaveTime && param->yyHaveDate && 445 if (param->yyHaveTime && param->yyHaveDate &&
446 !param->yyHaveRel) { 446 !param->yyHaveRel) {
447 param->yyYear = $1; 447 param->yyYear = $1;
448 } else { 448 } else {
449 if ($1 > 10000) { 449 if ($1 > 10000) {
450 param->yyHaveDate++; 450 param->yyHaveDate++;
451 param->yyDay = ($1)%100; 451 param->yyDay = ($1)%100;
452 param->yyMonth = ($1/100)%100; 452 param->yyMonth = ($1/100)%100;
453 param->yyYear = $1/10000; 453 param->yyYear = $1/10000;
454 } 454 }
455 else { 455 else {
456 param->yyHaveTime++; 456 param->yyHaveTime++;
457 if ($1 < 100) { 457 if ($1 < 100) {
458 param->yyHour = $1; 458 param->yyHour = $1;
459 param->yyMinutes = 0; 459 param->yyMinutes = 0;
460 } 460 }
461 else { 461 else {
462 param->yyHour = $1 / 100; 462 param->yyHour = $1 / 100;
463 param->yyMinutes = $1 % 100; 463 param->yyMinutes = $1 % 100;
464 } 464 }
465 param->yySeconds = 0; 465 param->yySeconds = 0;
466 param->yyMeridian = MER24; 466 param->yyMeridian = MER24;
467 } 467 }
468 } 468 }
469 } 469 }
470; 470;
471 471
472o_merid: 472o_merid:
473 /* empty */ { $$ = MER24; } 473 /* empty */ { $$ = MER24; }
474 | tMERIDIAN { $$ = $1; } 474 | tMERIDIAN { $$ = $1; }
475 | tTIME { $$ = $1 == 0 ? MER_MN : MER_NOON; } 475 | tTIME { $$ = $1 == 0 ? MER_MN : MER_NOON; }
476; 476;
477 477
478%% 478%%
479 479
480static short DaysInMonth[12] = { 480static short DaysInMonth[12] = {
481 31, 28, 31, 30, 31, 30, 481 31, 28, 31, 30, 31, 30,
482 31, 31, 30, 31, 30, 31 482 31, 31, 30, 31, 30, 31
483}; 483};
484 484
485/* 485/*
486 * works with tm.tm_year (ie: rel to 1900) 486 * works with tm.tm_year (ie: rel to 1900)
487 */ 487 */
488#define isleap(yr) (((yr) & 3) == 0 && (((yr) % 100) != 0 || \ 488#define isleap(yr) (((yr) & 3) == 0 && (((yr) % 100) != 0 || \
489 ((1900+(yr)) % 400) == 0)) 489 ((1900+(yr)) % 400) == 0))
490 490
491/* Month and day table. */ 491/* Month and day table. */
492static const TABLE MonthDayTable[] = { 492static const TABLE MonthDayTable[] = {
493 { "january", tMONTH, 1 }, 493 { "january", tMONTH, 1 },
494 { "february", tMONTH, 2 }, 494 { "february", tMONTH, 2 },
495 { "march", tMONTH, 3 }, 495 { "march", tMONTH, 3 },
496 { "april", tMONTH, 4 }, 496 { "april", tMONTH, 4 },
497 { "may", tMONTH, 5 }, 497 { "may", tMONTH, 5 },
498 { "june", tMONTH, 6 }, 498 { "june", tMONTH, 6 },
499 { "july", tMONTH, 7 }, 499 { "july", tMONTH, 7 },
500 { "august", tMONTH, 8 }, 500 { "august", tMONTH, 8 },
501 { "september", tMONTH, 9 }, 501 { "september", tMONTH, 9 },
502 { "sept", tMONTH, 9 }, 502 { "sept", tMONTH, 9 },
503 { "october", tMONTH, 10 }, 503 { "october", tMONTH, 10 },
504 { "november", tMONTH, 11 }, 504 { "november", tMONTH, 11 },
505 { "december", tMONTH, 12 }, 505 { "december", tMONTH, 12 },
506 { "sunday", tDAY, 0 }, 506 { "sunday", tDAY, 0 },
507 { "su", tDAY, 0 }, 507 { "su", tDAY, 0 },
508 { "monday", tDAY, 1 }, 508 { "monday", tDAY, 1 },
509 { "mo", tDAY, 1 }, 509 { "mo", tDAY, 1 },
510 { "tuesday", tDAY, 2 }, 510 { "tuesday", tDAY, 2 },
511 { "tues", tDAY, 2 }, 511 { "tues", tDAY, 2 },
512 { "tu", tDAY, 2 }, 512 { "tu", tDAY, 2 },
513 { "wednesday", tDAY, 3 }, 513 { "wednesday", tDAY, 3 },
514 { "wednes", tDAY, 3 }, 514 { "wednes", tDAY, 3 },
515 { "weds", tDAY, 3 }, 515 { "weds", tDAY, 3 },
516 { "we", tDAY, 3 }, 516 { "we", tDAY, 3 },
517 { "thursday", tDAY, 4 }, 517 { "thursday", tDAY, 4 },
518 { "thurs", tDAY, 4 }, 518 { "thurs", tDAY, 4 },
519 { "thur", tDAY, 4 }, 519 { "thur", tDAY, 4 },
520 { "th", tDAY, 4 }, 520 { "th", tDAY, 4 },
521 { "friday", tDAY, 5 }, 521 { "friday", tDAY, 5 },
522 { "fr", tDAY, 5 }, 522 { "fr", tDAY, 5 },
523 { "saturday", tDAY, 6 }, 523 { "saturday", tDAY, 6 },
524 { "sa", tDAY, 6 }, 524 { "sa", tDAY, 6 },
525 { NULL, 0, 0 } 525 { NULL, 0, 0 }
526}; 526};
527 527
528/* Time units table. */ 528/* Time units table. */
529static const TABLE UnitsTable[] = { 529static const TABLE UnitsTable[] = {
530 { "year", tMONTH_UNIT, 12 }, 530 { "year", tMONTH_UNIT, 12 },
531 { "month", tMONTH_UNIT, 1 }, 531 { "month", tMONTH_UNIT, 1 },
532 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, 532 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
533 { "week", tMINUTE_UNIT, 7 * 24 * 60 }, 533 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
534 { "day", tMINUTE_UNIT, 1 * 24 * 60 }, 534 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
535 { "hour", tMINUTE_UNIT, 60 }, 535 { "hour", tMINUTE_UNIT, 60 },
536 { "minute", tMINUTE_UNIT, 1 }, 536 { "minute", tMINUTE_UNIT, 1 },
537 { "min", tMINUTE_UNIT, 1 }, 537 { "min", tMINUTE_UNIT, 1 },
538 { "second", tSEC_UNIT, 1 }, 538 { "second", tSEC_UNIT, 1 },
539 { "sec", tSEC_UNIT, 1 }, 539 { "sec", tSEC_UNIT, 1 },
540 { NULL, 0, 0 } 540 { NULL, 0, 0 }
541}; 541};
542 542
543/* Assorted relative-time words. */ 543/* Assorted relative-time words. */
544static const TABLE OtherTable[] = { 544static const TABLE OtherTable[] = {
545 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, 545 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
546 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, 546 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
547 { "today", tMINUTE_UNIT, 0 }, 547 { "today", tMINUTE_UNIT, 0 },
548 { "now", tMINUTE_UNIT, 0 }, 548 { "now", tMINUTE_UNIT, 0 },
549 { "last", tUNUMBER, -1 }, 549 { "last", tUNUMBER, -1 },
550 { "this", tMINUTE_UNIT, 0 }, 550 { "this", tMINUTE_UNIT, 0 },
551 { "next", tUNUMBER, 2 }, 551 { "next", tUNUMBER, 2 },
552 { "first", tUNUMBER, 1 }, 552 { "first", tUNUMBER, 1 },
553 { "one", tUNUMBER, 1 }, 553 { "one", tUNUMBER, 1 },
554/* { "second", tUNUMBER, 2 }, */ 554/* { "second", tUNUMBER, 2 }, */
555 { "two", tUNUMBER, 2 }, 555 { "two", tUNUMBER, 2 },
556 { "third", tUNUMBER, 3 }, 556 { "third", tUNUMBER, 3 },
557 { "three", tUNUMBER, 3 }, 557 { "three", tUNUMBER, 3 },
558 { "fourth", tUNUMBER, 4 }, 558 { "fourth", tUNUMBER, 4 },
559 { "four", tUNUMBER, 4 }, 559 { "four", tUNUMBER, 4 },
560 { "fifth", tUNUMBER, 5 }, 560 { "fifth", tUNUMBER, 5 },
561 { "five", tUNUMBER, 5 }, 561 { "five", tUNUMBER, 5 },
562 { "sixth", tUNUMBER, 6 }, 562 { "sixth", tUNUMBER, 6 },
563 { "six", tUNUMBER, 6 }, 563 { "six", tUNUMBER, 6 },
564 { "seventh", tUNUMBER, 7 }, 564 { "seventh", tUNUMBER, 7 },
565 { "seven", tUNUMBER, 7 }, 565 { "seven", tUNUMBER, 7 },
566 { "eighth", tUNUMBER, 8 }, 566 { "eighth", tUNUMBER, 8 },
567 { "eight", tUNUMBER, 8 }, 567 { "eight", tUNUMBER, 8 },
568 { "ninth", tUNUMBER, 9 }, 568 { "ninth", tUNUMBER, 9 },
569 { "nine", tUNUMBER, 9 }, 569 { "nine", tUNUMBER, 9 },
570 { "tenth", tUNUMBER, 10 }, 570 { "tenth", tUNUMBER, 10 },
571 { "ten", tUNUMBER, 10 }, 571 { "ten", tUNUMBER, 10 },
572 { "eleventh", tUNUMBER, 11 }, 572 { "eleventh", tUNUMBER, 11 },
573 { "eleven", tUNUMBER, 11 }, 573 { "eleven", tUNUMBER, 11 },
574 { "twelfth", tUNUMBER, 12 }, 574 { "twelfth", tUNUMBER, 12 },
575 { "twelve", tUNUMBER, 12 }, 575 { "twelve", tUNUMBER, 12 },
576 { "ago", tAGO, 1 }, 576 { "ago", tAGO, 1 },
577 { NULL, 0, 0 } 577 { NULL, 0, 0 }
578}; 578};
579 579
580/* The timezone table. */ 580/* The timezone table. */
581/* Some of these are commented out because a time_t can't store a float. */ 581/* Some of these are commented out because a time_t can't store a float. */
582static const TABLE TimezoneTable[] = { 582static const TABLE TimezoneTable[] = {
583 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ 583 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
584 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ 584 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
585 { "utc", tZONE, HOUR( 0) }, 585 { "utc", tZONE, HOUR( 0) },
586 { "wet", tZONE, HOUR( 0) }, /* Western European */ 586 { "wet", tZONE, HOUR( 0) }, /* Western European */
587 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ 587 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
588 { "wat", tZONE, HOUR( 1) }, /* West Africa */ 588 { "wat", tZONE, HOUR( 1) }, /* West Africa */
589 { "at", tZONE, HOUR( 2) }, /* Azores */ 589 { "at", tZONE, HOUR( 2) }, /* Azores */
590#if 0 590#if 0
591 /* For completeness. BST is also British Summer, and GST is 591 /* For completeness. BST is also British Summer, and GST is
592 * also Guam Standard. */ 592 * also Guam Standard. */
593 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ 593 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
594 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ 594 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
595#endif 595#endif
596 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ 596 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
597 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ 597 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
598 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ 598 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
599 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ 599 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
600 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ 600 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
601 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ 601 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
602 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ 602 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
603 { "cst", tZONE, HOUR( 6) }, /* Central Standard */ 603 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
604 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ 604 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
605 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ 605 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
606 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ 606 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
607 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ 607 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
608 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ 608 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
609 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ 609 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
610 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ 610 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
611 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ 611 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
612 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ 612 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
613 { "cat", tZONE, HOUR(10) }, /* Central Alaska */ 613 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
614 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ 614 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
615 { "nt", tZONE, HOUR(11) }, /* Nome */ 615 { "nt", tZONE, HOUR(11) }, /* Nome */
616 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ 616 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
617 { "cet", tZONE, -HOUR(1) }, /* Central European */ 617 { "cet", tZONE, -HOUR(1) }, /* Central European */
618 { "met", tZONE, -HOUR(1) }, /* Middle European */ 618 { "met", tZONE, -HOUR(1) }, /* Middle European */
619 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ 619 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
620 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ 620 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
621 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ 621 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
622 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ 622 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
623 { "fwt", tZONE, -HOUR(1) }, /* French Winter */ 623 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
624 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ 624 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
625 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ 625 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
626 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ 626 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
627 { "it", tZONE, -HOUR(3.5) },/* Iran */ 627 { "it", tZONE, -HOUR(3.5) },/* Iran */
628 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ 628 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
629 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ 629 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
630 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ 630 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
631 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ 631 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
632#if 0 632#if 0
633 /* For completeness. NST is also Newfoundland Stanard, and SST is 633 /* For completeness. NST is also Newfoundland Stanard, and SST is
634 * also Swedish Summer. */ 634 * also Swedish Summer. */
635 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ 635 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
636 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ 636 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
637#endif /* 0 */ 637#endif /* 0 */
638 { "ict", tZONE, -HOUR(7) }, /* Indo China Time (Thai) */ 638 { "ict", tZONE, -HOUR(7) }, /* Indo China Time (Thai) */
639#if 0 /* this one looks to be bogus */ 639#if 0 /* this one looks to be bogus */
640 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ 640 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
641#endif 641#endif
642 { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */ 642 { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */
643 { "awst", tZONE, -HOUR(8) }, /* West Australian Standard */ 643 { "awst", tZONE, -HOUR(8) }, /* West Australian Standard */
644 { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ 644 { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
645 { "awdt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ 645 { "awdt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
646 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ 646 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
647 { "sgt", tZONE, -HOUR(8) }, /* Singapore */ 647 { "sgt", tZONE, -HOUR(8) }, /* Singapore */
648 { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */ 648 { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */
649 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ 649 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
650 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ 650 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
651 { "acst", tZONE, -HOUR(9.5) },/* Central Australian Standard */ 651 { "acst", tZONE, -HOUR(9.5) },/* Central Australian Standard */
652 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ 652 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
653 { "acdt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ 653 { "acdt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
654 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ 654 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
655 { "aest", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ 655 { "aest", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
656 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ 656 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
657 { "aedt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ 657 { "aedt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
658 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ 658 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
659 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ 659 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
660 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ 660 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
661 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ 661 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
662 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ 662 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
663 { NULL, 0, 0 } 663 { NULL, 0, 0 }
664}; 664};
665 665
666/* Military timezone table. */ 666/* Military timezone table. */
667static const TABLE MilitaryTable[] = { 667static const TABLE MilitaryTable[] = {
668 { "a", tZONE, HOUR( 1) }, 668 { "a", tZONE, HOUR( 1) },
669 { "b", tZONE, HOUR( 2) }, 669 { "b", tZONE, HOUR( 2) },
670 { "c", tZONE, HOUR( 3) }, 670 { "c", tZONE, HOUR( 3) },
671 { "d", tZONE, HOUR( 4) }, 671 { "d", tZONE, HOUR( 4) },
672 { "e", tZONE, HOUR( 5) }, 672 { "e", tZONE, HOUR( 5) },
673 { "f", tZONE, HOUR( 6) }, 673 { "f", tZONE, HOUR( 6) },
674 { "g", tZONE, HOUR( 7) }, 674 { "g", tZONE, HOUR( 7) },
675 { "h", tZONE, HOUR( 8) }, 675 { "h", tZONE, HOUR( 8) },
676 { "i", tZONE, HOUR( 9) }, 676 { "i", tZONE, HOUR( 9) },
677 { "k", tZONE, HOUR( 10) }, 677 { "k", tZONE, HOUR( 10) },
678 { "l", tZONE, HOUR( 11) }, 678 { "l", tZONE, HOUR( 11) },
679 { "m", tZONE, HOUR( 12) }, 679 { "m", tZONE, HOUR( 12) },
680 { "n", tZONE, HOUR(- 1) }, 680 { "n", tZONE, HOUR(- 1) },
681 { "o", tZONE, HOUR(- 2) }, 681 { "o", tZONE, HOUR(- 2) },
682 { "p", tZONE, HOUR(- 3) }, 682 { "p", tZONE, HOUR(- 3) },
683 { "q", tZONE, HOUR(- 4) }, 683 { "q", tZONE, HOUR(- 4) },
684 { "r", tZONE, HOUR(- 5) }, 684 { "r", tZONE, HOUR(- 5) },
685 { "s", tZONE, HOUR(- 6) }, 685 { "s", tZONE, HOUR(- 6) },
686 { "t", tZONE, HOUR(- 7) }, 686 { "t", tZONE, HOUR(- 7) },
687 { "u", tZONE, HOUR(- 8) }, 687 { "u", tZONE, HOUR(- 8) },
688 { "v", tZONE, HOUR(- 9) }, 688 { "v", tZONE, HOUR(- 9) },
689 { "w", tZONE, HOUR(-10) }, 689 { "w", tZONE, HOUR(-10) },
690 { "x", tZONE, HOUR(-11) }, 690 { "x", tZONE, HOUR(-11) },
691 { "y", tZONE, HOUR(-12) }, 691 { "y", tZONE, HOUR(-12) },
692 { "z", tZONE, HOUR( 0) }, 692 { "z", tZONE, HOUR( 0) },
693 { NULL, 0, 0 } 693 { NULL, 0, 0 }
694}; 694};
695 695
696static const TABLE TimeNames[] = { 696static const TABLE TimeNames[] = {
697 { "midnight", tTIME, 0 }, 697 { "midnight", tTIME, 0 },
698 { "mn", tTIME, 0 }, 698 { "mn", tTIME, 0 },
699 { "noon", tTIME, 12 }, 699 { "noon", tTIME, 12 },
700 { "midday", tTIME, 12 }, 700 { "midday", tTIME, 12 },
701 { NULL, 0, 0 } 701 { NULL, 0, 0 }
702}; 702};
703 703
704  704
705 705
706/* ARGSUSED */ 706/* ARGSUSED */
707static int 707static int
708yyerror(struct dateinfo *param, const char **inp, const char *s __unused) 708yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
709{ 709{
710 return 0; 710 return 0;
711} 711}
712 712
713/* 713/*
714 * Save a relative value, if it fits 714 * Save a relative value, if it fits
715 */ 715 */
716static int 716static int
717RelVal(struct dateinfo *param, time_t num, time_t unit, int scale, int type) 717RelVal(struct dateinfo *param, time_t num, time_t unit, int scale, int type)
718{ 718{
719 int i; 719 int i;
720 time_t v; 720 time_t v;
721 uintmax_t m; 721 uintmax_t m;
722 int sign = 1; 722 int sign = 1;
723 723
724 if ((i = param->yyHaveRel) >= MAXREL) 724 if ((i = param->yyHaveRel) >= MAXREL)
725 return 0; 725 return 0;
726 726
727 if (num < 0) { 727 if (num < 0) {
728 sign = -sign; 728 sign = -sign;
729 num = -num; 729 num = -num;
730 } 730 }
731 if (unit < 0) { 731 if (unit < 0) {
732 sign = -sign; 732 sign = -sign;
733 unit = -unit; 733 unit = -unit;
734 } 734 }
735 /* scale is always positive */ 735 /* scale is always positive */
736 736
737 m = LLONG_MAX; /* TIME_T_MAX */ 737 m = LLONG_MAX; /* TIME_T_MAX */
738 if (scale > 1) 738 if (scale > 1)
739 m /= scale; 739 m /= scale;
740 if (unit > 1) 740 if (unit > 1)
741 m /= unit; 741 m /= unit;
742 if ((uintmax_t)num > m) 742 if ((uintmax_t)num > m)
743 return 0; 743 return 0;
744 744
745 m = num * unit * scale; 745 m = num * unit * scale;
746 v = (time_t) m; 746 v = (time_t) m;
747 if (v < 0 || (uintmax_t)v != m) 747 if (v < 0 || (uintmax_t)v != m)
748 return 0; 748 return 0;
749 if (sign < 0) 749 if (sign < 0)
750 v = -v; 750 v = -v;
751 751
752 param->yyRel[i].yyRelMonth = type; 752 param->yyRel[i].yyRelMonth = type;
753 param->yyRel[i].yyRelVal = v; 753 param->yyRel[i].yyRelVal = v;
754 754
755 return 1; 755 return 1;
756} 756}
757 757
758/* 758/*
759 * Adjust year from a value that might be abbreviated, to a full value. 759 * Adjust year from a value that might be abbreviated, to a full value.
760 * e.g. convert 70 to 1970. 760 * e.g. convert 70 to 1970.
761 * Input Year is either: 761 * Input Year is either:
762 * - A negative number, which means to use its absolute value (why?) 762 * - A negative number, which means to use its absolute value (why?)
763 * - A number from 0 to 68, which means a year from 2000 to 2068,  763 * - A number from 0 to 68, which means a year from 2000 to 2068,
764 * - A number from 69 to 99, which means a year from 1969 to 1999, or 764 * - A number from 69 to 99, which means a year from 1969 to 1999, or
765 * - The actual year (>=100). 765 * - The actual year (>=100).
766 * Returns the full year. 766 * Returns the full year.
767 */ 767 */
768static time_t 768static time_t
769AdjustYear(time_t Year) 769AdjustYear(time_t Year)
770{ 770{
771 /* XXX Y2K */ 771 /* XXX Y2K */
772 if (Year < 0) 772 if (Year < 0)
773 Year = -Year; 773 Year = -Year;
774 if (Year < 69) /* POSIX compliant, 0..68 is 2000's, 69-99 1900's */ 774 if (Year < 69) /* POSIX compliant, 0..68 is 2000's, 69-99 1900's */
775 Year += 2000; 775 Year += 2000;
776 else if (Year < 100) 776 else if (Year < 100)
777 Year += 1900; 777 Year += 1900;
778 return Year; 778 return Year;
779} 779}
780 780
781static time_t 781static time_t
782Convert( 782Convert(
783 time_t Month, /* month of year [1-12] */ 783 time_t Month, /* month of year [1-12] */
784 time_t Day, /* day of month [1-31] */ 784 time_t Day, /* day of month [1-31] */
785 time_t Year, /* year, not abbreviated in any way */ 785 time_t Year, /* year, not abbreviated in any way */
786 time_t Hours, /* Hour of day [0-24] */ 786 time_t Hours, /* Hour of day [0-24] */
787 time_t Minutes, /* Minute of hour [0-59] */ 787 time_t Minutes, /* Minute of hour [0-59] */
788 time_t Seconds, /* Second of minute [0-60] */ 788 time_t Seconds, /* Second of minute [0-60] */
789 time_t Timezone, /* Timezone as minutes east of UTC, 789 time_t Timezone, /* Timezone as minutes east of UTC,
790 * or USE_LOCAL_TIME special case */ 790 * or USE_LOCAL_TIME special case */
791 MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */ 791 MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */
792 DSTMODE DSTmode /* DST on/off/maybe */ 792 DSTMODE DSTmode /* DST on/off/maybe */
793) 793)
794{ 794{
795 struct tm tm = {.tm_sec = 0}; 795 struct tm tm = {.tm_sec = 0};
796 struct tm otm; 796 struct tm otm;
797 time_t result; 797 time_t result;
798 798
799 tm.tm_sec = Seconds; 799 tm.tm_sec = Seconds;
800 tm.tm_min = Minutes; 800 tm.tm_min = Minutes;
801 tm.tm_hour = ((Hours == 12 && Meridian != MER24) ? 0 : Hours) + 801 tm.tm_hour = ((Hours == 12 && Meridian != MER24) ? 0 : Hours) +
802 (Meridian == MERpm ? 12 : 0); 802 (Meridian == MERpm ? 12 : 0);
803 803
804 tm.tm_mday = Day; 804 tm.tm_mday = Day;
805 tm.tm_mon = Month - 1; 805 tm.tm_mon = Month - 1;
806 tm.tm_year = Year - 1900; 806 tm.tm_year = Year - 1900;
807 if ((time_t)tm.tm_year + 1900 != Year) { 807 if ((time_t)tm.tm_year + 1900 != Year) {
808 errno = EOVERFLOW; 808 errno = EOVERFLOW;
809 return -1; 809 return -1;
810 } 810 }
811 if (Timezone == USE_LOCAL_TIME) { 811 if (Timezone == USE_LOCAL_TIME) {
812 switch (DSTmode) { 812 switch (DSTmode) {
813 case DSTon: tm.tm_isdst = 1; break; 813 case DSTon: tm.tm_isdst = 1; break;
814 case DSToff: tm.tm_isdst = 0; break; 814 case DSToff: tm.tm_isdst = 0; break;
815 default: tm.tm_isdst = -1; break; 815 default: tm.tm_isdst = -1; break;
816 } 816 }
817 otm = tm; 817 otm = tm;
818 result = mktime(&tm); 818 result = mktime(&tm);
819 } else { 819 } else {
820 /* We rely on mktime_z(NULL, ...) working in UTC */ 820 /* We rely on mktime_z(NULL, ...) working in UTC */
821 tm.tm_isdst = 0; /* hence cannot be summer time */ 821 tm.tm_isdst = 0; /* hence cannot be summer time */
822 otm = tm; 822 otm = tm;
823 errno = 0; 823 errno = 0;
824 result = mktime_z(NULL, &tm); 824 result = mktime_z(NULL, &tm);
825 if (result != -1 || errno == 0) { 825 if (result != -1 || errno == 0) {
826 result += Timezone * 60; 826 result += Timezone * 60;
827 if (DSTmode == DSTon) /* if specified sumer time */ 827 if (DSTmode == DSTon) /* if specified sumer time */
828 result -= 3600; /* UTC is 1 hour earlier XXX */ 828 result -= 3600; /* UTC is 1 hour earlier XXX */
829 } 829 }
830 } 830 }
831 831
832#if PARSEDATE_DEBUG 832#if PARSEDATE_DEBUG
833 fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd" 833 fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
834 " mer=%d DST=%d)", 834 " mer=%d DST=%d)",
835 __func__, 835 __func__,
836 (intmax_t)Month, (intmax_t)Day, (intmax_t)Year, 836 (intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
837 (intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds, 837 (intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
838 (intmax_t)Timezone, (int)Meridian, (int)DSTmode); 838 (intmax_t)Timezone, (int)Meridian, (int)DSTmode);
839 fprintf(stderr, " -> %jd", (intmax_t)result); 839 fprintf(stderr, " -> %jd", (intmax_t)result);
840 fprintf(stderr, " %s", ctime(&result)); 840 fprintf(stderr, " %s", ctime(&result));
841#endif 841#endif
842 842
843#define TM_NE(fld) (otm.tm_ ## fld != tm.tm_ ## fld) 843#define TM_NE(fld) (otm.tm_ ## fld != tm.tm_ ## fld)
844 if (TM_NE(year) || TM_NE(mon) || TM_NE(mday) || 844 if (TM_NE(year) || TM_NE(mon) || TM_NE(mday) ||
845 TM_NE(hour) || TM_NE(min) || TM_NE(sec)) { 845 TM_NE(hour) || TM_NE(min) || TM_NE(sec)) {
846 /* mktime() "corrected" our tm, so it must have been invalid */ 846 /* mktime() "corrected" our tm, so it must have been invalid */
847 result = -1; 847 result = -1;
848 errno = EAGAIN; 848 errno = EAGAIN;
849 } 849 }
850#undef TM_NE 850#undef TM_NE
851 851
852 return result; 852 return result;
853} 853}
854 854
855 855
856static time_t 856static time_t
857DSTcorrect( 857DSTcorrect(
858 time_t Start, 858 time_t Start,
859 time_t Future 859 time_t Future
860) 860)
861{ 861{
862 time_t StartDay; 862 time_t StartDay;
863 time_t FutureDay; 863 time_t FutureDay;
864 struct tm tm; 864 struct tm tm;
865 865
866 if (localtime_r(&Start, &tm) == NULL) 866 if (localtime_r(&Start, &tm) == NULL)
867 return -1; 867 return -1;
868 StartDay = (tm.tm_hour + 1) % 24; 868 StartDay = (tm.tm_hour + 1) % 24;
869 869
870 if (localtime_r(&Future, &tm) == NULL) 870 if (localtime_r(&Future, &tm) == NULL)
871 return -1; 871 return -1;
872 FutureDay = (tm.tm_hour + 1) % 24; 872 FutureDay = (tm.tm_hour + 1) % 24;
873 873
874 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; 874 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
875} 875}
876 876
877 877
878static time_t 878static time_t
879RelativeDate( 879RelativeDate(
880 time_t Start, 880 time_t Start,
881 time_t DayOrdinal, 881 time_t DayOrdinal,
882 time_t DayNumber 882 time_t DayNumber
883) 883)
884{ 884{
885 struct tm tm; 885 struct tm tm;
886 time_t now; 886 time_t now;
887 time_t change; 887 time_t change;
888 888
889 now = Start; 889 now = Start;
890 if (localtime_r(&now, &tm) == NULL) 890 if (localtime_r(&now, &tm) == NULL)
891 return -1; 891 return -1;
892 892
893 /* should be using TIME_T_MAX but there is no such thing, so just "know" */ 893 /* should be using TIME_T_MAX but there is no such thing, so just "know" */
894 if (llabs(DayOrdinal) >= LLONG_MAX / (7 * SECSPERDAY)) { 894 if (llabs(DayOrdinal) >= LLONG_MAX / (7 * SECSPERDAY)) {
895 errno = EOVERFLOW; 895 errno = EOVERFLOW;
896 return -1; 896 return -1;
897 } 897 }
898 898
899 change = SECSPERDAY * ((DayNumber - tm.tm_wday + 7) % 7); 899 change = SECSPERDAY * ((DayNumber - tm.tm_wday + 7) % 7);
900 change += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); 900 change += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
901 901
902 /* same here for _MAX and _MIN */ 902 /* same here for _MAX and _MIN */
903 if ((change > 0 && LLONG_MAX - change < now) || 903 if ((change > 0 && LLONG_MAX - change < now) ||
904 (change < 0 && LLONG_MIN - change > now)) { 904 (change < 0 && LLONG_MIN - change > now)) {
905 errno = EOVERFLOW; 905 errno = EOVERFLOW;
906 return -1; 906 return -1;
907 } 907 }
908 908
909 now += change; 909 now += change;
910 return DSTcorrect(Start, now); 910 return DSTcorrect(Start, now);
911} 911}
912 912
913 913
914static time_t 914static time_t
915RelativeMonth( 915RelativeMonth(
916 time_t Start, 916 time_t Start,
917 time_t RelMonth, 917 time_t RelMonth,
918 time_t Timezone 918 time_t Timezone
919) 919)
920{ 920{
921 struct tm tm; 921 struct tm tm;
922 time_t Month; 922 time_t Month;
923 time_t Then; 923 time_t Then;
924 int Day; 924 int Day;
925 925
926 if (RelMonth == 0) 926 if (RelMonth == 0)
927 return 0; 927 return 0;
928 /* 928 /*
929 * It doesn't matter what timezone we use to do this computation, 929 * It doesn't matter what timezone we use to do this computation,
930 * as long as we use the same one to reassemble the time that we 930 * as long as we use the same one to reassemble the time that we
931 * used to disassemble it. So always use localtime and mktime. In 931 * used to disassemble it. So always use localtime and mktime. In
932 * particular, don't use Convert() to reassemble, because it will 932 * particular, don't use Convert() to reassemble, because it will
933 * not only reassemble with the wrong timezone but it will also 933 * not only reassemble with the wrong timezone but it will also
934 * fail if we do e.g. three months from March 31 yielding July 1. 934 * fail if we do e.g. three months from March 31 yielding July 1.
935 */ 935 */
936 (void)Timezone; 936 (void)Timezone;
937 937
938 if (localtime_r(&Start, &tm) == NULL) 938 if (localtime_r(&Start, &tm) == NULL)
939 return -1; 939 return -1;
940 940
941 if (RelMonth >= LLONG_MAX - 12*((time_t)tm.tm_year + 1900) - tm.tm_mon) { 941 if (RelMonth >= LLONG_MAX - 12*((time_t)tm.tm_year + 1900) - tm.tm_mon) {
942 errno = EOVERFLOW; 942 errno = EOVERFLOW;
943 return -1; 943 return -1;
944 } 944 }
945 Month = 12 * (tm.tm_year + 1900) + tm.tm_mon + RelMonth; 945 Month = 12 * (tm.tm_year + 1900) + tm.tm_mon + RelMonth;
946 tm.tm_year = (Month / 12) - 1900; 946 tm.tm_year = (Month / 12) - 1900;
947 /* check for tm_year (an int) overflow */ 947 /* check for tm_year (an int) overflow */
948 if (((time_t)tm.tm_year + 1900) != Month/12) { 948 if (((time_t)tm.tm_year + 1900) != Month/12) {
949 errno = EOVERFLOW; 949 errno = EOVERFLOW;
950 return -1; 950 return -1;
951 } 951 }
952 tm.tm_mon = Month % 12; 952 tm.tm_mon = Month % 12;
953 if (tm.tm_mday > (Day = DaysInMonth[tm.tm_mon] + 953 if (tm.tm_mday > (Day = DaysInMonth[tm.tm_mon] +
954 ((tm.tm_mon==1) ? isleap(tm.tm_year) : 0))) 954 ((tm.tm_mon==1) ? isleap(tm.tm_year) : 0)))
955 tm.tm_mday = Day; 955 tm.tm_mday = Day;
956 errno = 0; 956 errno = 0;
957 Then = mktime(&tm); 957 Then = mktime(&tm);
958 if (Then == -1 && errno != 0) 958 if (Then == -1 && errno != 0)
959 return -1; 959 return -1;
960 return DSTcorrect(Start, Then); 960 return DSTcorrect(Start, Then);
961} 961}
962 962
963 963
964static int 964static int
965LookupWord(YYSTYPE *yylval, char *buff) 965LookupWord(YYSTYPE *yylval, char *buff)
966{ 966{
967 register char *p; 967 register char *p;
968 register char *q; 968 register char *q;
969 register const TABLE *tp; 969 register const TABLE *tp;
970 int i; 970 int i;
971 int abbrev; 971 int abbrev;
972 972
973 /* Make it lowercase. */ 973 /* Make it lowercase. */
974 for (p = buff; *p; p++) 974 for (p = buff; *p; p++)
975 if (isupper((unsigned char)*p)) 975 if (isupper((unsigned char)*p))
976 *p = tolower((unsigned char)*p); 976 *p = tolower((unsigned char)*p);
977 977
978 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { 978 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
979 yylval->Meridian = MERam; 979 yylval->Meridian = MERam;
980 return tMERIDIAN; 980 return tMERIDIAN;
981 } 981 }
982 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { 982 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
983 yylval->Meridian = MERpm; 983 yylval->Meridian = MERpm;
984 return tMERIDIAN; 984 return tMERIDIAN;
985 } 985 }
986 986
987 /* See if we have an abbreviation for a month. */ 987 /* See if we have an abbreviation for a month. */
988 if (strlen(buff) == 3) 988 if (strlen(buff) == 3)
989 abbrev = 1; 989 abbrev = 1;
990 else if (strlen(buff) == 4 && buff[3] == '.') { 990 else if (strlen(buff) == 4 && buff[3] == '.') {
991 abbrev = 1; 991 abbrev = 1;
992 buff[3] = '\0'; 992 buff[3] = '\0';
993 } 993 }
994 else 994 else
995 abbrev = 0; 995 abbrev = 0;
996 996
997 for (tp = MonthDayTable; tp->name; tp++) { 997 for (tp = MonthDayTable; tp->name; tp++) {
998 if (abbrev) { 998 if (abbrev) {
999 if (strncmp(buff, tp->name, 3) == 0) { 999 if (strncmp(buff, tp->name, 3) == 0) {
1000 yylval->Number = tp->value; 1000 yylval->Number = tp->value;
1001 return tp->type; 1001 return tp->type;
1002 } 1002 }
1003 } 1003 }
1004 else if (strcmp(buff, tp->name) == 0) { 1004 else if (strcmp(buff, tp->name) == 0) {
1005 yylval->Number = tp->value; 1005 yylval->Number = tp->value;
1006 return tp->type; 1006 return tp->type;
1007 } 1007 }
1008 } 1008 }
1009 1009
1010 for (tp = TimezoneTable; tp->name; tp++) 1010 for (tp = TimezoneTable; tp->name; tp++)
1011 if (strcmp(buff, tp->name) == 0) { 1011 if (strcmp(buff, tp->name) == 0) {
1012 yylval->Number = tp->value; 1012 yylval->Number = tp->value;
1013 return tp->type; 1013 return tp->type;
1014 } 1014 }
1015 1015
1016 if (strcmp(buff, "dst") == 0)  1016 if (strcmp(buff, "dst") == 0)
1017 return tDST; 1017 return tDST;
1018 1018
1019 for (tp = TimeNames; tp->name; tp++) 1019 for (tp = TimeNames; tp->name; tp++)
1020 if (strcmp(buff, tp->name) == 0) { 1020 if (strcmp(buff, tp->name) == 0) {
1021 yylval->Number = tp->value; 1021 yylval->Number = tp->value;
1022 return tp->type; 1022 return tp->type;
1023 } 1023 }
1024 1024
1025 for (tp = UnitsTable; tp->name; tp++) 1025 for (tp = UnitsTable; tp->name; tp++)
1026 if (strcmp(buff, tp->name) == 0) { 1026 if (strcmp(buff, tp->name) == 0) {
1027 yylval->Number = tp->value; 1027 yylval->Number = tp->value;
1028 return tp->type; 1028 return tp->type;
1029 } 1029 }
1030 1030
1031 /* Strip off any plural and try the units table again. */ 1031 /* Strip off any plural and try the units table again. */
1032 i = strlen(buff) - 1; 1032 i = strlen(buff) - 1;
1033 if (buff[i] == 's') { 1033 if (buff[i] == 's') {
1034 buff[i] = '\0'; 1034 buff[i] = '\0';
1035 for (tp = UnitsTable; tp->name; tp++) 1035 for (tp = UnitsTable; tp->name; tp++)
1036 if (strcmp(buff, tp->name) == 0) { 1036 if (strcmp(buff, tp->name) == 0) {
1037 yylval->Number = tp->value; 1037 yylval->Number = tp->value;
1038 return tp->type; 1038 return tp->type;
1039 } 1039 }
1040 buff[i] = 's'; /* Put back for "this" in OtherTable. */ 1040 buff[i] = 's'; /* Put back for "this" in OtherTable. */
1041 } 1041 }
1042 1042
1043 for (tp = OtherTable; tp->name; tp++) 1043 for (tp = OtherTable; tp->name; tp++)
1044 if (strcmp(buff, tp->name) == 0) { 1044 if (strcmp(buff, tp->name) == 0) {
1045 yylval->Number = tp->value; 1045 yylval->Number = tp->value;
1046 return tp->type; 1046 return tp->type;
1047 } 1047 }
1048 1048
1049 /* Military timezones. */ 1049 /* Military timezones. */
1050 if (buff[1] == '\0' && isalpha((unsigned char)*buff)) { 1050 if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
1051 for (tp = MilitaryTable; tp->name; tp++) 1051 for (tp = MilitaryTable; tp->name; tp++)
1052 if (strcmp(buff, tp->name) == 0) { 1052 if (strcmp(buff, tp->name) == 0) {
1053 yylval->Number = tp->value; 1053 yylval->Number = tp->value;
1054 return tp->type; 1054 return tp->type;
1055 } 1055 }
1056 } 1056 }
1057 1057
1058 /* Drop out any periods and try the timezone table again. */ 1058 /* Drop out any periods and try the timezone table again. */
1059 for (i = 0, p = q = buff; *q; q++) 1059 for (i = 0, p = q = buff; *q; q++)
1060 if (*q != '.') 1060 if (*q != '.')
1061 *p++ = *q; 1061 *p++ = *q;
1062 else 1062 else
1063 i++; 1063 i++;
1064 *p = '\0'; 1064 *p = '\0';
1065 if (i) 1065 if (i)
1066 for (tp = TimezoneTable; tp->name; tp++) 1066 for (tp = TimezoneTable; tp->name; tp++)
1067 if (strcmp(buff, tp->name) == 0) { 1067 if (strcmp(buff, tp->name) == 0) {
1068 yylval->Number = tp->value; 1068 yylval->Number = tp->value;
1069 return tp->type; 1069 return tp->type;
1070 } 1070 }
1071 1071
1072 return tID; 1072 return tID;
1073} 1073}
1074 1074
1075 1075
1076static int 1076static int
1077yylex(YYSTYPE *yylval, const char **yyInput) 1077yylex(YYSTYPE *yylval, const char **yyInput)
1078{ 1078{
1079 register char c; 1079 register char c;
1080 register char *p; 1080 register char *p;
1081 char buff[20]; 1081 char buff[20];
1082 int Count; 1082 int Count;
1083 int sign; 1083 int sign;
1084 const char *inp = *yyInput; 1084 const char *inp = *yyInput;
1085 1085
1086 for ( ; ; ) { 1086 for ( ; ; ) {
1087 while (isspace((unsigned char)*inp)) 1087 while (isspace((unsigned char)*inp))
1088 inp++; 1088 inp++;
1089 1089
1090 if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') { 1090 if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
1091 if (c == '-' || c == '+') { 1091 if (c == '-' || c == '+') {
1092 sign = c == '-' ? -1 : 1; 1092 sign = c == '-' ? -1 : 1;
1093 if (!isdigit((unsigned char)*++inp)) 1093 if (!isdigit((unsigned char)*++inp))
1094 /* skip the '-' sign */ 1094 /* skip the '-' sign */
1095 continue; 1095 continue;
1096 } 1096 }
1097 else 1097 else
1098 sign = 0; 1098 sign = 0;
1099 for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); ) { 1099 for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); ) {
1100 time_t v; 1100 time_t v;
1101 1101
1102 v = yylval->Number; 1102 v = yylval->Number;
1103 if (v > LLONG_MAX/10 || 1103 if (v > LLONG_MAX/10 ||
1104 (v == LLONG_MAX/10 && (v * 10 > LLONG_MAX - (c - '0'))))  1104 (v == LLONG_MAX/10 && (v * 10 > LLONG_MAX - (c - '0'))))
1105 yylval->Number = LLONG_MAX; 1105 yylval->Number = LLONG_MAX;
1106 else 1106 else
1107 yylval->Number = 10 * yylval->Number + c - '0'; 1107 yylval->Number = 10 * yylval->Number + c - '0';
1108 } 1108 }
1109 if (sign < 0) 1109 if (sign < 0)
1110 yylval->Number = -yylval->Number; 1110 yylval->Number = -yylval->Number;
1111 *yyInput = --inp; 1111 *yyInput = --inp;
1112 return sign ? tSNUMBER : tUNUMBER; 1112 return sign ? tSNUMBER : tUNUMBER;
1113 } 1113 }
1114 if (isalpha((unsigned char)c)) { 1114 if (isalpha((unsigned char)c)) {
1115 for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; ) 1115 for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
1116 if (p < &buff[sizeof buff - 1]) 1116 if (p < &buff[sizeof buff - 1])
1117 *p++ = c; 1117 *p++ = c;
1118 *p = '\0'; 1118 *p = '\0';
1119 *yyInput = --inp; 1119 *yyInput = --inp;
1120 return LookupWord(yylval, buff); 1120 return LookupWord(yylval, buff);
1121 } 1121 }
1122 if (c == '@') { 1122 if (c == '@') {
1123 *yyInput = ++inp; 1123 *yyInput = ++inp;
1124 return AT_SIGN; 1124 return AT_SIGN;
1125 } 1125 }
1126 if (c != '(') { 1126 if (c != '(') {
1127 *yyInput = ++inp; 1127 *yyInput = ++inp;
1128 return c; 1128 return c;
1129 } 1129 }
1130 Count = 0; 1130 Count = 0;
1131 do { 1131 do {
1132 c = *inp++; 1132 c = *inp++;
1133 if (c == '\0') 1133 if (c == '\0')
1134 return c; 1134 return c;
1135 if (c == '(') 1135 if (c == '(')
1136 Count++; 1136 Count++;
1137 else if (c == ')') 1137 else if (c == ')')
1138 Count--; 1138 Count--;
1139 } while (Count > 0); 1139 } while (Count > 0);
1140 } 1140 }
1141} 1141}
1142 1142
1143#define TM_YEAR_ORIGIN 1900 1143#define TM_YEAR_ORIGIN 1900
1144 1144
1145time_t 1145time_t
1146parsedate(const char *p, const time_t *now, const int *zone) 1146parsedate(const char *p, const time_t *now, const int *zone)
1147{ 1147{
1148 struct tm local, *tm; 1148 struct tm local, *tm;
1149 time_t nowt; 1149 time_t nowt;
1150 int zonet; 1150 int zonet;
1151 time_t Start; 1151 time_t Start;
1152 time_t tod, rm; 1152 time_t tod, rm;
1153 struct dateinfo param; 1153 struct dateinfo param;
1154 int saved_errno; 1154 int saved_errno;
1155 int i; 1155 int i;
1156  1156
1157 saved_errno = errno; 1157 saved_errno = errno;
1158 errno = 0; 1158 errno = 0;
1159 1159
1160 if (now == NULL) { 1160 if (now == NULL) {
1161 now = &nowt; 1161 now = &nowt;
1162 (void)time(&nowt); 1162 (void)time(&nowt);
1163 } 1163 }
1164 if (zone == NULL) { 1164 if (zone == NULL) {
1165 zone = &zonet; 1165 zone = &zonet;
1166 zonet = USE_LOCAL_TIME; 1166 zonet = USE_LOCAL_TIME;
1167 if ((tm = localtime_r(now, &local)) == NULL) 1167 if ((tm = localtime_r(now, &local)) == NULL)
1168 return -1; 1168 return -1;
1169 } else { 1169 } else {
1170 /* 1170 /*
1171 * Should use the specified zone, not localtime. 1171 * Should use the specified zone, not localtime.
1172 * Fake it using gmtime and arithmetic. 1172 * Fake it using gmtime and arithmetic.
1173 * This is good enough because we use only the year/month/day, 1173 * This is good enough because we use only the year/month/day,
1174 * not other fields of struct tm. 1174 * not other fields of struct tm.
1175 */ 1175 */
1176 time_t fake = *now + (*zone * 60); 1176 time_t fake = *now + (*zone * 60);
1177 if ((tm = gmtime_r(&fake, &local)) == NULL) 1177 if ((tm = gmtime_r(&fake, &local)) == NULL)
1178 return -1; 1178 return -1;
1179 } 1179 }
1180 param.yyYear = tm->tm_year + 1900; 1180 param.yyYear = tm->tm_year + 1900;
1181 param.yyMonth = tm->tm_mon + 1; 1181 param.yyMonth = tm->tm_mon + 1;
1182 param.yyDay = tm->tm_mday; 1182 param.yyDay = tm->tm_mday;
1183 param.yyTimezone = *zone; 1183 param.yyTimezone = *zone;
1184 param.yyDSTmode = DSTmaybe; 1184 param.yyDSTmode = DSTmaybe;
1185 param.yyHour = 0; 1185 param.yyHour = 0;
1186 param.yyMinutes = 0; 1186 param.yyMinutes = 0;
1187 param.yySeconds = 0; 1187 param.yySeconds = 0;
1188 param.yyMeridian = MER24; 1188 param.yyMeridian = MER24;
1189 param.yyHaveDate = 0; 1189 param.yyHaveDate = 0;
1190 param.yyHaveFullYear = 0; 1190 param.yyHaveFullYear = 0;
1191 param.yyHaveDay = 0; 1191 param.yyHaveDay = 0;
1192 param.yyHaveRel = 0; 1192 param.yyHaveRel = 0;
1193 param.yyHaveTime = 0; 1193 param.yyHaveTime = 0;
1194 param.yyHaveZone = 0; 1194 param.yyHaveZone = 0;
1195 1195
1196 /* 1196 /*
1197 * This one is too hard to parse using a grammar (the lexer would 1197 * This one is too hard to parse using a grammar (the lexer would
1198 * confuse the 'T' with the Mil format timezone designator) 1198 * confuse the 'T' with the Mil format timezone designator)
1199 * so handle it as a special case. 1199 * so handle it as a special case.
1200 */ 1200 */
1201 do { 1201 do {
1202 const unsigned char *pp = (const unsigned char *)p; 1202 const unsigned char *pp = (const unsigned char *)p;
1203 char *ep; /* starts as "expected, becomes "end ptr" */ 1203 char *ep; /* starts as "expected, becomes "end ptr" */
1204 static char format[] = "-dd-ddTdd:dd:dd"; 1204 static char format[] = "-dd-ddTdd:dd:dd";
1205 time_t yr; 1205 time_t yr;
1206 1206
1207 while (isdigit(*pp)) 1207 while (isdigit(*pp))
1208 pp++; 1208 pp++;
1209 1209
1210 if (pp == (const unsigned char *)p) 1210 if (pp == (const unsigned char *)p)
1211 break; 1211 break;
1212 1212
1213 for (ep = format; *ep; ep++, pp++) { 1213 for (ep = format; *ep; ep++, pp++) {
1214 switch (*ep) { 1214 switch (*ep) {
1215 case 'd': 1215 case 'd':
1216 if (isdigit(*pp)) 1216 if (isdigit(*pp))
1217 continue; 1217 continue;
1218 break; 1218 break;
1219 case 'T': 1219 case 'T':
1220 if (*pp == 'T' || *pp == 't' || *pp == ' ') 1220 if (*pp == 'T' || *pp == 't' || *pp == ' ')
1221 continue; 1221 continue;
1222 break; 1222 break;
1223 default: 1223 default:
1224 if (*pp == *ep) 1224 if (*pp == *ep)
1225 continue; 1225 continue;
1226 break; 1226 break;
1227 } 1227 }
1228 break; 1228 break;
1229 } 1229 }
1230 if (*ep != '\0') 1230 if (*ep != '\0')
1231 break; 1231 break;
1232 if (*pp == '.' || *pp == ',') { 1232 if (*pp == '.' || *pp == ',') {
1233 if (!isdigit(pp[1])) 1233 if (!isdigit(pp[1]))
1234 break; 1234 break;
1235 while (isdigit(*++pp)) 1235 while (isdigit(*++pp))
1236 continue; 1236 continue;
1237 } 1237 }
1238 if (*pp == 'Z' || *pp == 'z') 1238 if (*pp == 'Z' || *pp == 'z')
1239 pp++; 1239 pp++;
1240 else if (isdigit(*pp)) 1240 else if (isdigit(*pp))
1241 break; 1241 break;
1242 1242
1243 if (*pp != '\0' && !isspace(*pp)) 1243 if (*pp != '\0' && !isspace(*pp))
1244 break; 1244 break;
1245 1245
1246 errno = 0; 1246 errno = 0;
1247 yr = (time_t)strtol(p, &ep, 10); 1247 yr = (time_t)strtol(p, &ep, 10);
1248 if (errno != 0) /* out of range (can be big number) */ 1248 if (errno != 0) /* out of range (can be big number) */
1249 break; /* the ones below are all 2 digits */ 1249 break; /* the ones below are all 2 digits */
1250 1250
1251 /* 1251 /*
1252 * This is good enough to commit to there being an ISO format 1252 * This is good enough to commit to there being an ISO format
1253 * timestamp leading the input string. We permit standard 1253 * timestamp leading the input string. We permit standard
1254 * parsedate() modifiers to follow but not precede this string. 1254 * parsedate() modifiers to follow but not precede this string.
1255 */ 1255 */
1256 param.yyHaveTime = 1; 1256 param.yyHaveTime = 1;
1257 param.yyHaveDate = 1; 1257 param.yyHaveDate = 1;
1258 param.yyHaveFullYear = 1; 1258 param.yyHaveFullYear = 1;
1259 1259
1260 if (pp[-1] == 'Z' || pp[-1] == 'z') { 1260 if (pp[-1] == 'Z' || pp[-1] == 'z') {
1261 param.yyTimezone = 0; 1261 param.yyTimezone = 0;
1262 param.yyHaveZone = 1; 1262 param.yyHaveZone = 1;
1263 } 1263 }
1264 1264
1265 param.yyYear = yr; 1265 param.yyYear = yr;
1266 param.yyMonth = (time_t)strtol(ep + 1, &ep, 10); 1266 param.yyMonth = (time_t)strtol(ep + 1, &ep, 10);
1267 param.yyDay = (time_t)strtol(ep + 1, &ep, 10); 1267 param.yyDay = (time_t)strtol(ep + 1, &ep, 10);
1268 param.yyHour = (time_t)strtol(ep + 1, &ep, 10); 1268 param.yyHour = (time_t)strtol(ep + 1, &ep, 10);
1269 param.yyMinutes = (time_t)strtol(ep + 1, &ep, 10); 1269 param.yyMinutes = (time_t)strtol(ep + 1, &ep, 10);
1270 param.yySeconds = (time_t)strtol(ep + 1, &ep, 10); 1270 param.yySeconds = (time_t)strtol(ep + 1, &ep, 10);
1271 /* ignore any fractional seconds, no way to return them in a time_t */ 1271 /* ignore any fractional seconds, no way to return them in a time_t */
1272 1272
1273 param.yyMeridian = MER24; 1273 param.yyMeridian = MER24;
1274 1274
1275 p = (const char *)pp; 1275 p = (const char *)pp;
1276 } while (0); 1276 } while (0);
1277 1277
1278 if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 || 1278 if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
1279 param.yyHaveDate > 1 || param.yyHaveDay > 1) { 1279 param.yyHaveDate > 1 || param.yyHaveDay > 1) {
1280 errno = EINVAL; 1280 errno = EINVAL;
1281 return -1; 1281 return -1;
1282 } 1282 }
1283 1283
1284 if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) { 1284 if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
1285 if (! param.yyHaveFullYear) { 1285 if (! param.yyHaveFullYear) {
1286 param.yyYear = AdjustYear(param.yyYear); 1286 param.yyYear = AdjustYear(param.yyYear);
1287 param.yyHaveFullYear = 1; 1287 param.yyHaveFullYear = 1;
1288 } 1288 }
1289 errno = 0; 1289 errno = 0;
1290 Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour, 1290 Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
1291 param.yyMinutes, param.yySeconds, param.yyTimezone, 1291 param.yyMinutes, param.yySeconds, param.yyTimezone,
1292 param.yyMeridian, param.yyDSTmode); 1292 param.yyMeridian, param.yyDSTmode);
1293 if (Start == -1 && errno != 0) 1293 if (Start == -1 && errno != 0)
1294 return -1; 1294 return -1;
1295 } 1295 }
1296 else { 1296 else {
1297 Start = *now; 1297 Start = *now;
1298 if (!param.yyHaveRel) 1298 if (!param.yyHaveRel)
1299 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; 1299 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
1300 } 1300 }
1301 1301
1302 if (param.yyHaveRel > MAXREL) { 1302 if (param.yyHaveRel > MAXREL) {
1303 errno = EINVAL; 1303 errno = EINVAL;
1304 return -1; 1304 return -1;
1305 } 1305 }
1306 for (i = 0; i < param.yyHaveRel; i++) { 1306 for (i = 0; i < param.yyHaveRel; i++) {
1307 if (param.yyRel[i].yyRelMonth) { 1307 if (param.yyRel[i].yyRelMonth) {
1308 errno = 0; 1308 errno = 0;
1309 rm = RelativeMonth(Start, param.yyRel[i].yyRelVal, param.yyTimezone); 1309 rm = RelativeMonth(Start, param.yyRel[i].yyRelVal, param.yyTimezone);
1310 if (rm == -1 && errno != 0) 1310 if (rm == -1 && errno != 0)
1311 return -1; 1311 return -1;
1312 Start += rm; 1312 Start += rm;
1313 } else 1313 } else
1314 Start += param.yyRel[i].yyRelVal; 1314 Start += param.yyRel[i].yyRelVal;
1315 } 1315 }
1316 1316
1317 if (param.yyHaveDay && !param.yyHaveDate) { 1317 if (param.yyHaveDay && !param.yyHaveDate) {
1318 errno = 0; 1318 errno = 0;
1319 tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber); 1319 tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
1320 if (tod == -1 && errno != 0) 1320 if (tod == -1 && errno != 0)
1321 return -1; 1321 return -1;
1322 Start += tod; 1322 Start += tod;
1323 } 1323 }
1324 1324
1325 errno = saved_errno; 1325 errno = saved_errno;
1326 return Start; 1326 return Start;
1327} 1327}
1328 1328
1329 1329
1330#if defined(TEST) 1330#if defined(TEST)
1331 1331
1332/* ARGSUSED */ 1332/* ARGSUSED */
1333int 1333int
1334main(int ac, char *av[]) 1334main(int ac, char *av[])
1335{ 1335{
1336 char buff[128]; 1336 char buff[128];
1337 time_t d; 1337 time_t d;
1338 1338
1339 (void)printf("Enter date, or blank line to exit.\n\t> "); 1339 (void)printf("Enter date, or blank line to exit.\n\t> ");
1340 (void)fflush(stdout); 1340 (void)fflush(stdout);
1341 while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') { 1341 while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
1342 errno = 0; 1342 errno = 0;
1343 d = parsedate(buff, NULL, NULL); 1343 d = parsedate(buff, NULL, NULL);
1344 if (d == -1 && errno != 0) 1344 if (d == -1 && errno != 0)
1345 (void)printf("Bad format - couldn't convert: %s\n", 1345 (void)printf("Bad format - couldn't convert: %s\n",
1346 strerror(errno)); 1346 strerror(errno));
1347 else 1347 else
1348 (void)printf("%jd\t%s", (intmax_t)d, ctime(&d)); 1348 (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
1349 (void)printf("\t> "); 1349 (void)printf("\t> ");
1350 (void)fflush(stdout); 1350 (void)fflush(stdout);
1351 } 1351 }
1352 exit(0); 1352 exit(0);
1353 /* NOTREACHED */ 1353 /* NOTREACHED */
1354} 1354}
1355#endif /* defined(TEST) */ 1355#endif /* defined(TEST) */