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