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