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 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*/ 51typedef 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*/ 61typedef enum _DSTMODE { 62 DSTon, DSToff, DSTmaybe 63} DSTMODE; 64 65/* 66** Meridian: am, pm, or 24-hour style (plus "noon" and "midnight"). 67*/ 68typedef enum _MERIDIAN { 69 MERam, MERpm, MER24, MER_NOON, MER_MN 70} MERIDIAN; 71 72 73struct 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 98static 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 130spec: 131 /* empty */ 132 | spec item 133; 134 135item: 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 149cvsstamp: 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 167epochdate: 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 195at_number: 196 tUNUMBER 197 | tSNUMBER 198; 199 200time: 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 299time_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 325zone: 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 339day: 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 345date: 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 421rel: 422 relunit 423 | relunit tAGO { 424 param->yyRel[param->yyHaveRel].yyRelVal = 425 -param->yyRel[param->yyHaveRel].yyRelVal; 426 } 427; 428 429relunit: 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 441number: 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 470o_merid: 471 /* empty */ { $$ = MER24; } 472 | tMERIDIAN { $$ = $1; } 473 | tTIME { $$ = $1 == 0 ? MER_MN : MER_NOON; } 474; 475 476%% 477 478static 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. */ 490static 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. */ 527static 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. */ 542static 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. */ 580static 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. */ 665static 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 694static 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 704/* ARGSUSED */ 705static int 706yyerror(struct dateinfo *param, const char **inp, const char *s __unused) 707{ 708 return 0; 709} 710 711/* 712 * Save a relative value, if it fits 713 */ 714static int 715RelVal(struct dateinfo *param, time_t num, time_t unit, int scale, int type) 716{ 717 int i; 718 time_t v; 719 uintmax_t m; 720 int sign = 1; 721 722 if ((i = param->yyHaveRel) >= MAXREL) 723 return 0; 724 725 if (num < 0) { 726 sign = -sign; 727 num = -num; 728 } 729 if (unit < 0) { 730 sign = -sign; 731 unit = -unit; 732 } 733 /* scale is always positive */ 734 735 m = LLONG_MAX; /* TIME_T_MAX */ 736 if (scale > 1) 737 m /= scale; 738 if (unit > 1) 739 m /= unit; 740 if ((uintmax_t)num > m) 741 return 0; 742 743 m = num * unit * scale; 744 v = (time_t) m; 745 if (v < 0 || (uintmax_t)v != m) 746 return 0; 747 if (sign < 0) 748 v = -v; 749 750 param->yyRel[i].yyRelMonth = type; 751 param->yyRel[i].yyRelVal = v; 752 753 return 1; 754} 755 756/* 757 * Adjust year from a value that might be abbreviated, to a full value. 758 * e.g. convert 70 to 1970. 759 * Input Year is either: 760 * - A negative number, which means to use its absolute value (why?) 761 * - A number from 0 to 68, which means a year from 2000 to 2068, 762 * - A number from 69 to 99, which means a year from 1969 to 1999, or 763 * - The actual year (>=100). 764 * Returns the full year. 765 */ 766static time_t 767AdjustYear(time_t Year) 768{ 769 /* XXX Y2K */ 770 if (Year < 0) 771 Year = -Year; 772 if (Year < 69) /* POSIX compliant, 0..68 is 2000's, 69-99 1900's */ 773 Year += 2000; 774 else if (Year < 100) 775 Year += 1900; 776 return Year; 777} 778 779static time_t 780Convert( 781 time_t Month, /* month of year [1-12] */ 782 time_t Day, /* day of month [1-31] */ 783 time_t Year, /* year, not abbreviated in any way */ 784 time_t Hours, /* Hour of day [0-24] */ 785 time_t Minutes, /* Minute of hour [0-59] */ 786 time_t Seconds, /* Second of minute [0-60] */ 787 time_t Timezone, /* Timezone as minutes east of UTC, 788 * or USE_LOCAL_TIME special case */ 789 MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */ 790 DSTMODE DSTmode /* DST on/off/maybe */ 791) 792{ 793 struct tm tm = {.tm_sec = 0}; 794 struct tm otm; 795 time_t result; 796 797 tm.tm_sec = Seconds; 798 tm.tm_min = Minutes; 799 tm.tm_hour = ((Hours == 12 && Meridian != MER24) ? 0 : Hours) + 800 (Meridian == MERpm ? 12 : 0); 801 802 tm.tm_mday = Day; 803 tm.tm_mon = Month - 1; 804 tm.tm_year = Year - 1900; 805 if ((time_t)tm.tm_year + 1900 != Year) { 806 errno = EOVERFLOW; 807 return -1; 808 } 809 if (Timezone == USE_LOCAL_TIME) { 810 switch (DSTmode) { 811 case DSTon: tm.tm_isdst = 1; break; 812 case DSToff: tm.tm_isdst = 0; break; 813 default: tm.tm_isdst = -1; break; 814 } 815 otm = tm; 816 result = mktime(&tm); 817 } else { 818 /* We rely on mktime_z(NULL, ...) working in UTC */ 819 tm.tm_isdst = 0; /* hence cannot be summer time */ 820 otm = tm; 821 errno = 0; 822 result = mktime_z(NULL, &tm); 823 if (result != -1 || errno == 0) { 824 result += Timezone * 60; 825 if (DSTmode == DSTon) /* if specified sumer time */ 826 result -= 3600; /* UTC is 1 hour earlier XXX */ 827 } 828 } 829 830#if PARSEDATE_DEBUG 831 fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd" 832 " mer=%d DST=%d)", 833 __func__, 834 (intmax_t)Month, (intmax_t)Day, (intmax_t)Year, 835 (intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds, 836 (intmax_t)Timezone, (int)Meridian, (int)DSTmode); 837 fprintf(stderr, " -> %jd", (intmax_t)result); 838 fprintf(stderr, " %s", ctime(&result)); 839#endif 840 841#define TM_NE(fld) (otm.tm_ ## fld != tm.tm_ ## fld) 842 if (TM_NE(year) || TM_NE(mon) || TM_NE(mday) || 843 TM_NE(hour) || TM_NE(min) || TM_NE(sec)) { 844 /* mktime() "corrected" our tm, so it must have been invalid */ 845 result = -1; 846 errno = EAGAIN; 847 } 848#undef TM_NE 849 850 return result; 851} 852 853 854static time_t 855DSTcorrect( 856 time_t Start, 857 time_t Future 858) 859{ 860 time_t StartDay; 861 time_t FutureDay; 862 struct tm tm; 863 864 if (localtime_r(&Start, &tm) == NULL) 865 return -1; 866 StartDay = (tm.tm_hour + 1) % 24; 867 868 if (localtime_r(&Future, &tm) == NULL) 869 return -1; 870 FutureDay = (tm.tm_hour + 1) % 24; 871 872 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; 873} 874 875 876static time_t 877RelativeDate( 878 time_t Start, 879 time_t DayOrdinal, 880 time_t DayNumber 881) 882{ 883 struct tm tm; 884 time_t now; 885 time_t change; 886 887 now = Start; 888 if (localtime_r(&now, &tm) == NULL) 889 return -1; 890 891 /* should be using TIME_T_MAX but there is no such thing, so just "know" */ 892 if (llabs(DayOrdinal) >= LLONG_MAX / (7 * SECSPERDAY)) { 893 errno = EOVERFLOW; 894 return -1; 895 } 896 897 change = SECSPERDAY * ((DayNumber - tm.tm_wday + 7) % 7); 898 change += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); 899 900 /* same here for _MAX and _MIN */ 901 if ((change > 0 && LLONG_MAX - change < now) || 902 (change < 0 && LLONG_MIN - change > now)) { 903 errno = EOVERFLOW; 904 return -1; 905 } 906 907 now += change; 908 return DSTcorrect(Start, now); 909} 910 911 912static time_t 913RelativeMonth( 914 time_t Start, 915 time_t RelMonth, 916 time_t Timezone 917) 918{ 919 struct tm tm; 920 time_t Month; 921 time_t Then; 922 int Day; 923 924 if (RelMonth == 0) 925 return 0; 926 /* 927 * It doesn't matter what timezone we use to do this computation, 928 * as long as we use the same one to reassemble the time that we 929 * used to disassemble it. So always use localtime and mktime. In 930 * particular, don't use Convert() to reassemble, because it will 931 * not only reassemble with the wrong timezone but it will also 932 * fail if we do e.g. three months from March 31 yielding July 1. 933 */ 934 (void)Timezone; 935 936 if (localtime_r(&Start, &tm) == NULL) 937 return -1; 938 939 if (RelMonth >= LLONG_MAX - 12*((time_t)tm.tm_year + 1900) - tm.tm_mon) { 940 errno = EOVERFLOW; 941 return -1; 942 } 943 Month = 12 * (tm.tm_year + 1900) + tm.tm_mon + RelMonth; 944 tm.tm_year = (Month / 12) - 1900; 945 /* check for tm_year (an int) overflow */ 946 if (((time_t)tm.tm_year + 1900) != Month/12) { 947 errno = EOVERFLOW; 948 return -1; 949 } 950 tm.tm_mon = Month % 12; 951 if (tm.tm_mday > (Day = DaysInMonth[tm.tm_mon] + 952 ((tm.tm_mon==1) ? isleap(tm.tm_year) : 0))) 953 tm.tm_mday = Day; 954 errno = 0; 955 Then = mktime(&tm); 956 if (Then == -1 && errno != 0) 957 return -1; 958 return DSTcorrect(Start, Then); 959} 960 961 962static int 963LookupWord(YYSTYPE *yylval, char *buff) 964{ 965 register char *p; 966 register char *q; 967 register const TABLE *tp; 968 int i; 969 int abbrev; 970 971 /* Make it lowercase. */ 972 for (p = buff; *p; p++) 973 if (isupper((unsigned char)*p)) 974 *p = tolower((unsigned char)*p); 975 976 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { 977 yylval->Meridian = MERam; 978 return tMERIDIAN; 979 } 980 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { 981 yylval->Meridian = MERpm; 982 return tMERIDIAN; 983 } 984 985 /* See if we have an abbreviation for a month. */ 986 if (strlen(buff) == 3) 987 abbrev = 1; 988 else if (strlen(buff) == 4 && buff[3] == '.') { 989 abbrev = 1; 990 buff[3] = '\0'; 991 } 992 else 993 abbrev = 0; 994 995 for (tp = MonthDayTable; tp->name; tp++) { 996 if (abbrev) { 997 if (strncmp(buff, tp->name, 3) == 0) { 998 yylval->Number = tp->value; 999 return tp->type; 1000 } 1001 } 1002 else if (strcmp(buff, tp->name) == 0) { 1003 yylval->Number = tp->value; 1004 return tp->type; 1005 } 1006 } 1007 1008 for (tp = TimezoneTable; tp->name; tp++) 1009 if (strcmp(buff, tp->name) == 0) { 1010 yylval->Number = tp->value; 1011 return tp->type; 1012 } 1013 1014 if (strcmp(buff, "dst") == 0) 1015 return tDST; 1016 1017 for (tp = TimeNames; tp->name; tp++) 1018 if (strcmp(buff, tp->name) == 0) { 1019 yylval->Number = tp->value; 1020 return tp->type; 1021 } 1022 1023 for (tp = UnitsTable; tp->name; tp++) 1024 if (strcmp(buff, tp->name) == 0) { 1025 yylval->Number = tp->value; 1026 return tp->type; 1027 } 1028 1029 /* Strip off any plural and try the units table again. */ 1030 i = strlen(buff) - 1; 1031 if (buff[i] == 's') { 1032 buff[i] = '\0'; 1033 for (tp = UnitsTable; tp->name; tp++) 1034 if (strcmp(buff, tp->name) == 0) { 1035 yylval->Number = tp->value; 1036 return tp->type; 1037 } 1038 buff[i] = 's'; /* Put back for "this" in OtherTable. */ 1039 } 1040 1041 for (tp = OtherTable; tp->name; tp++) 1042 if (strcmp(buff, tp->name) == 0) { 1043 yylval->Number = tp->value; 1044 return tp->type; 1045 } 1046 1047 /* Military timezones. */ 1048 if (buff[1] == '\0' && isalpha((unsigned char)*buff)) { 1049 for (tp = MilitaryTable; tp->name; tp++) 1050 if (strcmp(buff, tp->name) == 0) { 1051 yylval->Number = tp->value; 1052 return tp->type; 1053 } 1054 } 1055 1056 /* Drop out any periods and try the timezone table again. */ 1057 for (i = 0, p = q = buff; *q; q++) 1058 if (*q != '.') 1059 *p++ = *q; 1060 else 1061 i++; 1062 *p = '\0'; 1063 if (i) 1064 for (tp = TimezoneTable; tp->name; tp++) 1065 if (strcmp(buff, tp->name) == 0) { 1066 yylval->Number = tp->value; 1067 return tp->type; 1068 } 1069 1070 return tID; 1071} 1072 1073 1074static int 1075yylex(YYSTYPE *yylval, const char **yyInput) 1076{ 1077 register char c; 1078 register char *p; 1079 char buff[20]; 1080 int Count; 1081 int sign; 1082 const char *inp = *yyInput; 1083 1084 for ( ; ; ) { 1085 while (isspace((unsigned char)*inp)) 1086 inp++; 1087 1088 if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') { 1089 if (c == '-' || c == '+') { 1090 sign = c == '-' ? -1 : 1; 1091 if (!isdigit((unsigned char)*++inp)) 1092 /* skip the '-' sign */ 1093 continue; 1094 } 1095 else 1096 sign = 0; 1097 for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); ) { 1098 time_t v; 1099 1100 v = yylval->Number; 1101 if (v > LLONG_MAX/10 || 1102 (v == LLONG_MAX/10 && (v * 10 > LLONG_MAX - (c - '0')))) 1103 yylval->Number = LLONG_MAX; 1104 else 1105 yylval->Number = 10 * yylval->Number + c - '0'; 1106 } 1107 if (sign < 0) 1108 yylval->Number = -yylval->Number; 1109 *yyInput = --inp; 1110 return sign ? tSNUMBER : tUNUMBER; 1111 } 1112 if (isalpha((unsigned char)c)) { 1113 for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; ) 1114 if (p < &buff[sizeof buff - 1]) 1115 *p++ = c; 1116 *p = '\0'; 1117 *yyInput = --inp; 1118 return LookupWord(yylval, buff); 1119 } 1120 if (c == '@') { 1121 *yyInput = ++inp; 1122 return AT_SIGN; 1123 } 1124 if (c != '(') { 1125 *yyInput = ++inp; 1126 return c; 1127 } 1128 Count = 0; 1129 do { 1130 c = *inp++; 1131 if (c == '\0') 1132 return c; 1133 if (c == '(') 1134 Count++; 1135 else if (c == ')') 1136 Count--; 1137 } while (Count > 0); 1138 } 1139} 1140 1141#define TM_YEAR_ORIGIN 1900 1142 1143time_t 1144parsedate(const char *p, const time_t *now, const int *zone) 1145{ 1146 struct tm local, *tm; 1147 time_t nowt; 1148 int zonet; 1149 time_t Start; 1150 time_t tod, rm; 1151 struct dateinfo param; 1152 int saved_errno; 1153 int i; 1154 1155 saved_errno = errno; 1156 errno = 0; 1157 1158 if (now == NULL) { 1159 now = &nowt; 1160 (void)time(&nowt); 1161 } 1162 if (zone == NULL) { 1163 zone = &zonet; 1164 zonet = USE_LOCAL_TIME; 1165 if ((tm = localtime_r(now, &local)) == NULL) 1166 return -1; 1167 } else { 1168 /* 1169 * Should use the specified zone, not localtime. 1170 * Fake it using gmtime and arithmetic. 1171 * This is good enough because we use only the year/month/day, 1172 * not other fields of struct tm. 1173 */ 1174 time_t fake = *now + (*zone * 60); 1175 if ((tm = gmtime_r(&fake, &local)) == NULL) 1176 return -1; 1177 } 1178 param.yyYear = tm->tm_year + 1900; 1179 param.yyMonth = tm->tm_mon + 1; 1180 param.yyDay = tm->tm_mday; 1181 param.yyTimezone = *zone; 1182 param.yyDSTmode = DSTmaybe; 1183 param.yyHour = 0; 1184 param.yyMinutes = 0; 1185 param.yySeconds = 0; 1186 param.yyMeridian = MER24; 1187 param.yyHaveDate = 0; 1188 param.yyHaveFullYear = 0; 1189 param.yyHaveDay = 0; 1190 param.yyHaveRel = 0; 1191 param.yyHaveTime = 0; 1192 param.yyHaveZone = 0; 1193 1194 /* 1195 * This one is too hard to parse using a grammar (the lexer would 1196 * confuse the 'T' with the Mil format timezone designator) 1197 * so handle it as a special case. 1198 */ 1199 do { 1200 const unsigned char *pp = (const unsigned char *)p; 1201 char *ep; /* starts as "expected, becomes "end ptr" */ 1202 static char format[] = "-dd-ddTdd:dd:dd"; 1203 time_t yr; 1204 1205 while (isdigit(*pp)) 1206 pp++; 1207 1208 if (pp == (const unsigned char *)p) 1209 break; 1210 1211 for (ep = format; *ep; ep++, pp++) { 1212 switch (*ep) { 1213 case 'd': 1214 if (isdigit(*pp)) 1215 continue; 1216 break; 1217 case 'T': 1218 if (*pp == 'T' || *pp == 't' || *pp == ' ') 1219 continue; 1220 break; 1221 default: 1222 if (*pp == *ep) 1223 continue; 1224 break; 1225 } 1226 break; 1227 } 1228 if (*ep != '\0') 1229 break; 1230 if (*pp == '.' || *pp == ',') { 1231 if (!isdigit(pp[1])) 1232 break; 1233 while (isdigit(*++pp)) 1234 continue; 1235 } 1236 if (*pp == 'Z' || *pp == 'z') 1237 pp++; 1238 else if (isdigit(*pp)) 1239 break; 1240 1241 if (*pp != '\0' && !isspace(*pp)) 1242 break; 1243 1244 errno = 0; 1245 yr = (time_t)strtol(p, &ep, 10); 1246 if (errno != 0) /* out of range (can be big number) */ 1247 break; /* the ones below are all 2 digits */ 1248 1249 /* 1250 * This is good enough to commit to there being an ISO format 1251 * timestamp leading the input string. We permit standard 1252 * parsedate() modifiers to follow but not precede this string. 1253 */ 1254 param.yyHaveTime = 1; 1255 param.yyHaveDate = 1; 1256 param.yyHaveFullYear = 1; 1257 1258 if (pp[-1] == 'Z' || pp[-1] == 'z') { 1259 param.yyTimezone = 0; 1260 param.yyHaveZone = 1; 1261 } 1262 1263 param.yyYear = yr; 1264 param.yyMonth = (time_t)strtol(ep + 1, &ep, 10); 1265 param.yyDay = (time_t)strtol(ep + 1, &ep, 10); 1266 param.yyHour = (time_t)strtol(ep + 1, &ep, 10); 1267 param.yyMinutes = (time_t)strtol(ep + 1, &ep, 10); 1268 param.yySeconds = (time_t)strtol(ep + 1, &ep, 10); 1269 /* ignore any fractional seconds, no way to return them in a time_t */ 1270 1271 param.yyMeridian = MER24; 1272 1273 p = (const char *)pp; 1274 } while (0); 1275 1276 if (yyparse(¶m, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 || 1277 param.yyHaveDate > 1 || param.yyHaveDay > 1) { 1278 errno = EINVAL; 1279 return -1; 1280 } 1281 1282 if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) { 1283 if (! param.yyHaveFullYear) { 1284 param.yyYear = AdjustYear(param.yyYear); 1285 param.yyHaveFullYear = 1; 1286 } 1287 errno = 0; 1288 Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour, 1289 param.yyMinutes, param.yySeconds, param.yyTimezone, 1290 param.yyMeridian, param.yyDSTmode); 1291 if (Start == -1 && errno != 0) 1292 return -1; 1293 } 1294 else { 1295 Start = *now; 1296 if (!param.yyHaveRel) 1297 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; 1298 } 1299 1300 if (param.yyHaveRel > MAXREL) { 1301 errno = EINVAL; 1302 return -1; 1303 } 1304 for (i = 0; i < param.yyHaveRel; i++) { 1305 if (param.yyRel[i].yyRelMonth) { 1306 errno = 0; 1307 rm = RelativeMonth(Start, param.yyRel[i].yyRelVal, param.yyTimezone); 1308 if (rm == -1 && errno != 0) 1309 return -1; 1310 Start += rm; 1311 } else 1312 Start += param.yyRel[i].yyRelVal; 1313 } 1314 1315 if (param.yyHaveDay && !param.yyHaveDate) { 1316 errno = 0; 1317 tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber); 1318 if (tod == -1 && errno != 0) 1319 return -1; 1320 Start += tod; 1321 } 1322 1323 errno = saved_errno; 1324 return Start; 1325} 1326 1327 1328#if defined(TEST) 1329 1330/* ARGSUSED */ 1331int 1332main(int ac, char *av[]) 1333{ 1334 char buff[128]; 1335 time_t d; 1336 1337 (void)printf("Enter date, or blank line to exit.\n\t> "); 1338 (void)fflush(stdout); 1339 while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') { 1340 errno = 0; 1341 d = parsedate(buff, NULL, NULL); 1342 if (d == -1 && errno != 0) 1343 (void)printf("Bad format - couldn't convert: %s\n", 1344 strerror(errno)); 1345 else 1346 (void)printf("%jd\t%s", (intmax_t)d, ctime(&d)); 1347 (void)printf("\t> "); 1348 (void)fflush(stdout); 1349 } 1350 exit(0); 1351 /* NOTREACHED */ 1352} 1353#endif /* defined(TEST) */ 1354