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