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 <stdio.h> 16 #include <ctype.h> 17 #include <string.h> 18 #include <time.h> 19 #include <util.h> 20 #include <stdlib.h> 21 22 /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS 23 releases): 24 25 We don't want to mess with all the portability hassles of alloca. 26 In particular, most (all?) versions of bison will use alloca in 27 their parser. If bison works on your system (e.g. it should work 28 with gcc), then go ahead and use it, but the more general solution 29 is to use byacc instead of bison, which should generate a portable 30 parser. I played with adding "#define alloca dont_use_alloca", to 31 give an error if the parser generator uses alloca (and thus detect 32 unportable parsedate.c's), but that seems to cause as many problems 33 as it solves. */ 34 35 #define EPOCH 1970 36 #define HOUR(x) ((time_t)(x) * 60) 37 #define SECSPERDAY (24L * 60L * 60L) 38 39 40 /* 41 ** An entry in the lexical lookup table. 42 */ 43 typedef struct _TABLE { 44 const char *name; 45 int type; 46 time_t value; 47 } TABLE; 48 49 50 /* 51 ** Daylight-savings mode: on, off, or not yet known. 52 */ 53 typedef enum _DSTMODE { 54 DSTon, DSToff, DSTmaybe 55 } DSTMODE; 56 57 /* 58 ** Meridian: am, pm, or 24-hour style. 59 */ 60 typedef enum _MERIDIAN { 61 MERam, MERpm, MER24 62 } MERIDIAN; 63 64 65 /* 66 ** Global variables. We could get rid of most of these by using a good 67 ** union as the yacc stack. (This routine was originally written before 68 ** yacc had the %union construct.) Maybe someday; right now we only use 69 ** the %union very rarely. 70 */ 71 static const char *yyInput; 72 static DSTMODE yyDSTmode; 73 static time_t yyDayOrdinal; 74 static time_t yyDayNumber; 75 static int yyHaveDate; 76 static int yyHaveDay; 77 static int yyHaveRel; 78 static int yyHaveTime; 79 static int yyHaveZone; 80 static time_t yyTimezone; 81 static time_t yyDay; 82 static time_t yyHour; 83 static time_t yyMinutes; 84 static time_t yyMonth; 85 static time_t yySeconds; 86 static time_t yyYear; 87 static MERIDIAN yyMeridian; 88 static time_t yyRelMonth; 89 static time_t yyRelSeconds; 90 91 %} 92 93 %union { 94 time_t Number; 95 enum _MERIDIAN Meridian; 96 } 97 98 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT 99 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN 100 101 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT 102 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE 103 %type <Meridian> tMERIDIAN o_merid 104 105 %% 106 107 spec : /* NULL */ 108 | spec item 109 ; 110 111 item : time { 112 yyHaveTime++; 113 } 114 | zone { 115 yyHaveZone++; 116 } 117 | date { 118 yyHaveDate++; 119 } 120 | day { 121 yyHaveDay++; 122 } 123 | rel { 124 yyHaveRel++; 125 } 126 | cvsstamp { 127 yyHaveTime++; 128 yyHaveDate++; 129 yyHaveZone++; 130 } 131 | epochdate { 132 yyHaveTime++; 133 yyHaveDate++; 134 yyHaveZone++; 135 } 136 | number 137 ; 138 139 cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER { 140 yyYear = $1; 141 if (yyYear < 100) yyYear += 1900; 142 yyMonth = $3; 143 yyDay = $5; 144 yyHour = $7; 145 yyMinutes = $9; 146 yySeconds = $11; 147 yyDSTmode = DSToff; 148 yyTimezone = 0; 149 } 150 ; 151 152 epochdate: AT_SIGN tUNUMBER { 153 time_t when = $2; 154 struct tm tmbuf; 155 if (gmtime_r(&when, &tmbuf) != NULL) { 156 yyYear = tmbuf.tm_year + 1900; 157 yyMonth = tmbuf.tm_mon + 1; 158 yyDay = tmbuf.tm_mday; 159 160 yyHour = tmbuf.tm_hour; 161 yyMinutes = tmbuf.tm_min; 162 yySeconds = tmbuf.tm_sec; 163 } else { 164 yyYear = 1970; 165 yyMonth = 1; 166 yyDay = 1; 167 168 yyHour = 0; 169 yyMinutes = 0; 170 yySeconds = 0; 171 } 172 yyDSTmode = DSToff; 173 yyTimezone = 0; 174 } 175 ; 176 177 time : tUNUMBER tMERIDIAN { 178 yyHour = $1; 179 yyMinutes = 0; 180 yySeconds = 0; 181 yyMeridian = $2; 182 } 183 | tUNUMBER ':' tUNUMBER o_merid { 184 yyHour = $1; 185 yyMinutes = $3; 186 yySeconds = 0; 187 yyMeridian = $4; 188 } 189 | tUNUMBER ':' tUNUMBER tSNUMBER { 190 yyHour = $1; 191 yyMinutes = $3; 192 yyMeridian = MER24; 193 yyDSTmode = DSToff; 194 yyTimezone = - ($4 % 100 + ($4 / 100) * 60); 195 } 196 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { 197 yyHour = $1; 198 yyMinutes = $3; 199 yySeconds = $5; 200 yyMeridian = $6; 201 } 202 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { 203 yyHour = $1; 204 yyMinutes = $3; 205 yySeconds = $5; 206 yyMeridian = MER24; 207 yyDSTmode = DSToff; 208 yyTimezone = - ($6 % 100 + ($6 / 100) * 60); 209 } 210 ; 211 212 zone : tZONE { 213 yyTimezone = $1; 214 yyDSTmode = DSToff; 215 } 216 | tDAYZONE { 217 yyTimezone = $1; 218 yyDSTmode = DSTon; 219 } 220 | 221 tZONE tDST { 222 yyTimezone = $1; 223 yyDSTmode = DSTon; 224 } 225 ; 226 227 day : tDAY { 228 yyDayOrdinal = 1; 229 yyDayNumber = $1; 230 } 231 | tDAY ',' { 232 yyDayOrdinal = 1; 233 yyDayNumber = $1; 234 } 235 | tUNUMBER tDAY { 236 yyDayOrdinal = $1; 237 yyDayNumber = $2; 238 } 239 ; 240 241 date : tUNUMBER '/' tUNUMBER { 242 yyMonth = $1; 243 yyDay = $3; 244 } 245 | tUNUMBER '/' tUNUMBER '/' tUNUMBER { 246 if ($1 >= 100) { 247 yyYear = $1; 248 yyMonth = $3; 249 yyDay = $5; 250 } else { 251 yyMonth = $1; 252 yyDay = $3; 253 yyYear = $5; 254 } 255 } 256 | tUNUMBER tSNUMBER tSNUMBER { 257 /* ISO 8601 format. yyyy-mm-dd. */ 258 yyYear = $1; 259 yyMonth = -$2; 260 yyDay = -$3; 261 } 262 | tUNUMBER tMONTH tSNUMBER { 263 /* e.g. 17-JUN-1992. */ 264 yyDay = $1; 265 yyMonth = $2; 266 yyYear = -$3; 267 } 268 | tMONTH tUNUMBER { 269 yyMonth = $1; 270 yyDay = $2; 271 } 272 | tMONTH tUNUMBER ',' tUNUMBER { 273 yyMonth = $1; 274 yyDay = $2; 275 yyYear = $4; 276 } 277 | tUNUMBER tMONTH { 278 yyMonth = $2; 279 yyDay = $1; 280 } 281 | tUNUMBER tMONTH tUNUMBER { 282 yyMonth = $2; 283 yyDay = $1; 284 yyYear = $3; 285 } 286 ; 287 288 rel : relunit tAGO { 289 yyRelSeconds = -yyRelSeconds; 290 yyRelMonth = -yyRelMonth; 291 } 292 | relunit 293 ; 294 295 relunit : tUNUMBER tMINUTE_UNIT { 296 yyRelSeconds += $1 * $2 * 60L; 297 } 298 | tSNUMBER tMINUTE_UNIT { 299 yyRelSeconds += $1 * $2 * 60L; 300 } 301 | tMINUTE_UNIT { 302 yyRelSeconds += $1 * 60L; 303 } 304 | tSNUMBER tSEC_UNIT { 305 yyRelSeconds += $1; 306 } 307 | tUNUMBER tSEC_UNIT { 308 yyRelSeconds += $1; 309 } 310 | tSEC_UNIT { 311 yyRelSeconds++; 312 } 313 | tSNUMBER tMONTH_UNIT { 314 yyRelMonth += $1 * $2; 315 } 316 | tUNUMBER tMONTH_UNIT { 317 yyRelMonth += $1 * $2; 318 } 319 | tMONTH_UNIT { 320 yyRelMonth += $1; 321 } 322 ; 323 324 number : tUNUMBER { 325 if (yyHaveTime && yyHaveDate && !yyHaveRel) 326 yyYear = $1; 327 else { 328 if($1>10000) { 329 yyHaveDate++; 330 yyDay= ($1)%100; 331 yyMonth= ($1/100)%100; 332 yyYear = $1/10000; 333 } 334 else { 335 yyHaveTime++; 336 if ($1 < 100) { 337 yyHour = $1; 338 yyMinutes = 0; 339 } 340 else { 341 yyHour = $1 / 100; 342 yyMinutes = $1 % 100; 343 } 344 yySeconds = 0; 345 yyMeridian = MER24; 346 } 347 } 348 } 349 ; 350 351 o_merid : /* NULL */ { 352 $$ = MER24; 353 } 354 | tMERIDIAN { 355 $$ = $1; 356 } 357 ; 358 359 %% 360 361 /* Month and day table. */ 362 static TABLE const MonthDayTable[] = { 363 { "january", tMONTH, 1 }, 364 { "february", tMONTH, 2 }, 365 { "march", tMONTH, 3 }, 366 { "april", tMONTH, 4 }, 367 { "may", tMONTH, 5 }, 368 { "june", tMONTH, 6 }, 369 { "july", tMONTH, 7 }, 370 { "august", tMONTH, 8 }, 371 { "september", tMONTH, 9 }, 372 { "sept", tMONTH, 9 }, 373 { "october", tMONTH, 10 }, 374 { "november", tMONTH, 11 }, 375 { "december", tMONTH, 12 }, 376 { "sunday", tDAY, 0 }, 377 { "monday", tDAY, 1 }, 378 { "tuesday", tDAY, 2 }, 379 { "tues", tDAY, 2 }, 380 { "wednesday", tDAY, 3 }, 381 { "wednes", tDAY, 3 }, 382 { "thursday", tDAY, 4 }, 383 { "thur", tDAY, 4 }, 384 { "thurs", tDAY, 4 }, 385 { "friday", tDAY, 5 }, 386 { "saturday", tDAY, 6 }, 387 { NULL, 0, 0 } 388 }; 389 390 /* Time units table. */ 391 static TABLE const UnitsTable[] = { 392 { "year", tMONTH_UNIT, 12 }, 393 { "month", tMONTH_UNIT, 1 }, 394 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, 395 { "week", tMINUTE_UNIT, 7 * 24 * 60 }, 396 { "day", tMINUTE_UNIT, 1 * 24 * 60 }, 397 { "hour", tMINUTE_UNIT, 60 }, 398 { "minute", tMINUTE_UNIT, 1 }, 399 { "min", tMINUTE_UNIT, 1 }, 400 { "second", tSEC_UNIT, 1 }, 401 { "sec", tSEC_UNIT, 1 }, 402 { NULL, 0, 0 } 403 }; 404 405 /* Assorted relative-time words. */ 406 static TABLE const OtherTable[] = { 407 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, 408 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, 409 { "today", tMINUTE_UNIT, 0 }, 410 { "now", tMINUTE_UNIT, 0 }, 411 { "last", tUNUMBER, -1 }, 412 { "this", tMINUTE_UNIT, 0 }, 413 { "next", tUNUMBER, 2 }, 414 { "first", tUNUMBER, 1 }, 415 /* { "second", tUNUMBER, 2 }, */ 416 { "third", tUNUMBER, 3 }, 417 { "fourth", tUNUMBER, 4 }, 418 { "fifth", tUNUMBER, 5 }, 419 { "sixth", tUNUMBER, 6 }, 420 { "seventh", tUNUMBER, 7 }, 421 { "eighth", tUNUMBER, 8 }, 422 { "ninth", tUNUMBER, 9 }, 423 { "tenth", tUNUMBER, 10 }, 424 { "eleventh", tUNUMBER, 11 }, 425 { "twelfth", tUNUMBER, 12 }, 426 { "ago", tAGO, 1 }, 427 { NULL, 0, 0 } 428 }; 429 430 /* The timezone table. */ 431 /* Some of these are commented out because a time_t can't store a float. */ 432 static TABLE const TimezoneTable[] = { 433 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ 434 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ 435 { "utc", tZONE, HOUR( 0) }, 436 { "wet", tZONE, HOUR( 0) }, /* Western European */ 437 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ 438 { "wat", tZONE, HOUR( 1) }, /* West Africa */ 439 { "at", tZONE, HOUR( 2) }, /* Azores */ 440 #if 0 441 /* For completeness. BST is also British Summer, and GST is 442 * also Guam Standard. */ 443 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ 444 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ 445 #endif 446 #if 0 447 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ 448 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ 449 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ 450 #endif 451 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ 452 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ 453 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ 454 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ 455 { "cst", tZONE, HOUR( 6) }, /* Central Standard */ 456 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ 457 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ 458 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ 459 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ 460 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ 461 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ 462 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ 463 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ 464 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ 465 { "cat", tZONE, HOUR(10) }, /* Central Alaska */ 466 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ 467 { "nt", tZONE, HOUR(11) }, /* Nome */ 468 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ 469 { "cet", tZONE, -HOUR(1) }, /* Central European */ 470 { "met", tZONE, -HOUR(1) }, /* Middle European */ 471 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ 472 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ 473 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ 474 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ 475 { "fwt", tZONE, -HOUR(1) }, /* French Winter */ 476 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ 477 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ 478 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ 479 #if 0 480 { "it", tZONE, -HOUR(3.5) },/* Iran */ 481 #endif 482 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ 483 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ 484 #if 0 485 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ 486 #endif 487 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ 488 #if 0 489 /* For completeness. NST is also Newfoundland Stanard, and SST is 490 * also Swedish Summer. */ 491 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ 492 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ 493 #endif /* 0 */ 494 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ 495 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ 496 #if 0 497 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ 498 #endif 499 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ 500 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ 501 #if 0 502 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ 503 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ 504 #endif 505 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ 506 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ 507 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ 508 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ 509 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ 510 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ 511 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ 512 { NULL, 0, 0 } 513 }; 514 515 /* Military timezone table. */ 516 static TABLE const MilitaryTable[] = { 517 { "a", tZONE, HOUR( 1) }, 518 { "b", tZONE, HOUR( 2) }, 519 { "c", tZONE, HOUR( 3) }, 520 { "d", tZONE, HOUR( 4) }, 521 { "e", tZONE, HOUR( 5) }, 522 { "f", tZONE, HOUR( 6) }, 523 { "g", tZONE, HOUR( 7) }, 524 { "h", tZONE, HOUR( 8) }, 525 { "i", tZONE, HOUR( 9) }, 526 { "k", tZONE, HOUR( 10) }, 527 { "l", tZONE, HOUR( 11) }, 528 { "m", tZONE, HOUR( 12) }, 529 { "n", tZONE, HOUR(- 1) }, 530 { "o", tZONE, HOUR(- 2) }, 531 { "p", tZONE, HOUR(- 3) }, 532 { "q", tZONE, HOUR(- 4) }, 533 { "r", tZONE, HOUR(- 5) }, 534 { "s", tZONE, HOUR(- 6) }, 535 { "t", tZONE, HOUR(- 7) }, 536 { "u", tZONE, HOUR(- 8) }, 537 { "v", tZONE, HOUR(- 9) }, 538 { "w", tZONE, HOUR(-10) }, 539 { "x", tZONE, HOUR(-11) }, 540 { "y", tZONE, HOUR(-12) }, 541 { "z", tZONE, HOUR( 0) }, 542 { NULL, 0, 0 } 543 }; 544 545 546 547 548 /* ARGSUSED */ 549 static int 550 yyerror(const char *s __unused) 551 { 552 return 0; 553 } 554 555 556 static time_t 557 ToSeconds( 558 time_t Hours, 559 time_t Minutes, 560 time_t Seconds, 561 MERIDIAN Meridian 562 ) 563 { 564 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) 565 return -1; 566 switch (Meridian) { 567 case MER24: 568 if (Hours < 0 || Hours > 23) 569 return -1; 570 return (Hours * 60L + Minutes) * 60L + Seconds; 571 case MERam: 572 if (Hours < 1 || Hours > 12) 573 return -1; 574 if (Hours == 12) 575 Hours = 0; 576 return (Hours * 60L + Minutes) * 60L + Seconds; 577 case MERpm: 578 if (Hours < 1 || Hours > 12) 579 return -1; 580 if (Hours == 12) 581 Hours = 0; 582 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; 583 default: 584 abort (); 585 } 586 /* NOTREACHED */ 587 } 588 589 590 /* Year is either 591 * A negative number, which means to use its absolute value (why?) 592 * A number from 0 to 99, which means a year from 1900 to 1999, or 593 * The actual year (>=100). */ 594 static time_t 595 Convert( 596 time_t Month, 597 time_t Day, 598 time_t Year, 599 time_t Hours, 600 time_t Minutes, 601 time_t Seconds, 602 MERIDIAN Meridian, 603 DSTMODE DSTmode 604 ) 605 { 606 static int DaysInMonth[12] = { 607 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 608 }; 609 time_t tod; 610 time_t Julian; 611 int i; 612 613 if (Year < 0) 614 Year = -Year; 615 if (Year < 69) 616 Year += 2000; 617 else if (Year < 100) 618 Year += 1900; 619 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) 620 ? 29 : 28; 621 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But 622 I'm too lazy to try to check for time_t overflow in another way. */ 623 if (Year < EPOCH || Year > 2038 624 || Month < 1 || Month > 12 625 /* Lint fluff: "conversion from long may lose accuracy" */ 626 || Day < 1 || Day > DaysInMonth[(int)--Month]) 627 /* FIXME: 628 * It would be nice to set a global error string here. 629 * "February 30 is not a valid date" is much more informative than 630 * "Can't parse date/time: 100 months" when the user input was 631 * "100 months" and addition resolved that to February 30, for 632 * example. See rcs2-7 in src/sanity.sh for more. */ 633 return -1; 634 635 for (Julian = Day - 1, i = 0; i < Month; i++) 636 Julian += DaysInMonth[i]; 637 for (i = EPOCH; i < Year; i++) 638 Julian += 365 + (i % 4 == 0); 639 Julian *= SECSPERDAY; 640 Julian += yyTimezone * 60L; 641 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) 642 return -1; 643 Julian += tod; 644 if (DSTmode == DSTon 645 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) 646 Julian -= 60 * 60; 647 return Julian; 648 } 649 650 651 static time_t 652 DSTcorrect( 653 time_t Start, 654 time_t Future 655 ) 656 { 657 time_t StartDay; 658 time_t FutureDay; 659 660 StartDay = (localtime(&Start)->tm_hour + 1) % 24; 661 FutureDay = (localtime(&Future)->tm_hour + 1) % 24; 662 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; 663 } 664 665 666 static time_t 667 RelativeDate( 668 time_t Start, 669 time_t DayOrdinal, 670 time_t DayNumber 671 ) 672 { 673 struct tm *tm; 674 time_t now; 675 676 now = Start; 677 tm = localtime(&now); 678 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); 679 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); 680 return DSTcorrect(Start, now); 681 } 682 683 684 static time_t 685 RelativeMonth( 686 time_t Start, 687 time_t RelMonth 688 ) 689 { 690 struct tm *tm; 691 time_t Month; 692 time_t Year; 693 694 if (RelMonth == 0) 695 return 0; 696 tm = localtime(&Start); 697 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; 698 Year = Month / 12; 699 Month = Month % 12 + 1; 700 return DSTcorrect(Start, 701 Convert(Month, (time_t)tm->tm_mday, Year, 702 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, 703 MER24, DSTmaybe)); 704 } 705 706 707 static int 708 LookupWord(char *buff) 709 { 710 register char *p; 711 register char *q; 712 register const TABLE *tp; 713 int i; 714 int abbrev; 715 716 /* Make it lowercase. */ 717 for (p = buff; *p; p++) 718 if (isupper((unsigned char)*p)) 719 *p = tolower((unsigned char)*p); 720 721 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { 722 yylval.Meridian = MERam; 723 return tMERIDIAN; 724 } 725 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { 726 yylval.Meridian = MERpm; 727 return tMERIDIAN; 728 } 729 730 /* See if we have an abbreviation for a month. */ 731 if (strlen(buff) == 3) 732 abbrev = 1; 733 else if (strlen(buff) == 4 && buff[3] == '.') { 734 abbrev = 1; 735 buff[3] = '\0'; 736 } 737 else 738 abbrev = 0; 739 740 for (tp = MonthDayTable; tp->name; tp++) { 741 if (abbrev) { 742 if (strncmp(buff, tp->name, 3) == 0) { 743 yylval.Number = tp->value; 744 return tp->type; 745 } 746 } 747 else if (strcmp(buff, tp->name) == 0) { 748 yylval.Number = tp->value; 749 return tp->type; 750 } 751 } 752 753 for (tp = TimezoneTable; tp->name; tp++) 754 if (strcmp(buff, tp->name) == 0) { 755 yylval.Number = tp->value; 756 return tp->type; 757 } 758 759 if (strcmp(buff, "dst") == 0) 760 return tDST; 761 762 for (tp = UnitsTable; tp->name; tp++) 763 if (strcmp(buff, tp->name) == 0) { 764 yylval.Number = tp->value; 765 return tp->type; 766 } 767 768 /* Strip off any plural and try the units table again. */ 769 i = strlen(buff) - 1; 770 if (buff[i] == 's') { 771 buff[i] = '\0'; 772 for (tp = UnitsTable; tp->name; tp++) 773 if (strcmp(buff, tp->name) == 0) { 774 yylval.Number = tp->value; 775 return tp->type; 776 } 777 buff[i] = 's'; /* Put back for "this" in OtherTable. */ 778 } 779 780 for (tp = OtherTable; tp->name; tp++) 781 if (strcmp(buff, tp->name) == 0) { 782 yylval.Number = tp->value; 783 return tp->type; 784 } 785 786 /* Military timezones. */ 787 if (buff[1] == '\0' && isalpha((unsigned char)*buff)) { 788 for (tp = MilitaryTable; tp->name; tp++) 789 if (strcmp(buff, tp->name) == 0) { 790 yylval.Number = tp->value; 791 return tp->type; 792 } 793 } 794 795 /* Drop out any periods and try the timezone table again. */ 796 for (i = 0, p = q = buff; *q; q++) 797 if (*q != '.') 798 *p++ = *q; 799 else 800 i++; 801 *p = '\0'; 802 if (i) 803 for (tp = TimezoneTable; tp->name; tp++) 804 if (strcmp(buff, tp->name) == 0) { 805 yylval.Number = tp->value; 806 return tp->type; 807 } 808 809 return tID; 810 } 811 812 813 static int 814 yylex(void) 815 { 816 register char c; 817 register char *p; 818 char buff[20]; 819 int Count; 820 int sign; 821 822 for ( ; ; ) { 823 while (isspace((unsigned char)*yyInput)) 824 yyInput++; 825 826 if (isdigit((unsigned char)(c = *yyInput)) || c == '-' || c == '+') { 827 if (c == '-' || c == '+') { 828 sign = c == '-' ? -1 : 1; 829 if (!isdigit((unsigned char)*++yyInput)) 830 /* skip the '-' sign */ 831 continue; 832 } 833 else 834 sign = 0; 835 for (yylval.Number = 0; isdigit((unsigned char)(c = *yyInput++)); ) 836 yylval.Number = 10 * yylval.Number + c - '0'; 837 yyInput--; 838 if (sign < 0) 839 yylval.Number = -yylval.Number; 840 return sign ? tSNUMBER : tUNUMBER; 841 } 842 if (isalpha((unsigned char)c)) { 843 for (p = buff; isalpha((unsigned char)(c = *yyInput++)) || c == '.'; ) 844 if (p < &buff[sizeof buff - 1]) 845 *p++ = c; 846 *p = '\0'; 847 yyInput--; 848 return LookupWord(buff); 849 } 850 if (c == '@') { 851 yyInput++; 852 return AT_SIGN; 853 } 854 if (c != '(') 855 return *yyInput++; 856 Count = 0; 857 do { 858 c = *yyInput++; 859 if (c == '\0') 860 return c; 861 if (c == '(') 862 Count++; 863 else if (c == ')') 864 Count--; 865 } while (Count > 0); 866 } 867 } 868 869 #define TM_YEAR_ORIGIN 1900 870 871 /* Yield A - B, measured in seconds. */ 872 static long 873 difftm (struct tm *a, struct tm *b) 874 { 875 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); 876 int by = b->tm_year + (TM_YEAR_ORIGIN - 1); 877 int days = ( 878 /* difference in day of year */ 879 a->tm_yday - b->tm_yday 880 /* + intervening leap days */ 881 + ((ay >> 2) - (by >> 2)) 882 - (ay/100 - by/100) 883 + ((ay/100 >> 2) - (by/100 >> 2)) 884 /* + difference in years * 365 */ 885 + (long)(ay-by) * 365 886 ); 887 return (60*(60*(24*days + (a->tm_hour - b->tm_hour)) 888 + (a->tm_min - b->tm_min)) 889 + (a->tm_sec - b->tm_sec)); 890 } 891 892 time_t 893 parsedate(const char *p, const time_t *now, const int *zone) 894 { 895 struct tm gmt, local, *gmt_ptr, *tm; 896 time_t nowt; 897 int zonet; 898 time_t Start; 899 time_t tod; 900 901 yyInput = p; 902 if (now == NULL || zone == NULL) { 903 now = &nowt; 904 zone = &zonet; 905 (void)time(&nowt); 906 907 gmt_ptr = gmtime_r(now, &gmt); 908 if ((tm = localtime_r(now, &local)) == NULL) 909 return -1; 910 911 if (gmt_ptr != NULL) 912 zonet = difftm(&gmt, &local) / 60; 913 else 914 /* We are on a system like VMS, where the system clock is 915 in local time and the system has no concept of timezones. 916 Hopefully we can fake this out (for the case in which the 917 user specifies no timezone) by just saying the timezone 918 is zero. */ 919 zonet = 0; 920 921 if (local.tm_isdst) 922 zonet += 60; 923 } else { 924 if ((tm = localtime_r(now, &local)) == NULL) 925 return -1; 926 } 927 yyYear = tm->tm_year + 1900; 928 yyMonth = tm->tm_mon + 1; 929 yyDay = tm->tm_mday; 930 yyTimezone = *zone; 931 yyDSTmode = DSTmaybe; 932 yyHour = 0; 933 yyMinutes = 0; 934 yySeconds = 0; 935 yyMeridian = MER24; 936 yyRelSeconds = 0; 937 yyRelMonth = 0; 938 yyHaveDate = 0; 939 yyHaveDay = 0; 940 yyHaveRel = 0; 941 yyHaveTime = 0; 942 yyHaveZone = 0; 943 944 if (yyparse() 945 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) 946 return -1; 947 948 if (yyHaveDate || yyHaveTime || yyHaveDay) { 949 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, 950 yyMeridian, yyDSTmode); 951 if (Start < 0) 952 return -1; 953 } 954 else { 955 Start = *now; 956 if (!yyHaveRel) 957 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; 958 } 959 960 Start += yyRelSeconds; 961 Start += RelativeMonth(Start, yyRelMonth); 962 963 if (yyHaveDay && !yyHaveDate) { 964 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); 965 Start += tod; 966 } 967 968 /* Have to do *something* with a legitimate -1 so it's distinguishable 969 * from the error return value. (Alternately could set errno on error.) */ 970 return Start == -1 ? 0 : Start; 971 } 972 973 974 #if defined(TEST) 975 976 /* ARGSUSED */ 977 int 978 main(ac, av) 979 int ac; 980 char *av[]; 981 { 982 char buff[128]; 983 time_t d; 984 985 (void)printf("Enter date, or blank line to exit.\n\t> "); 986 (void)fflush(stdout); 987 while (gets(buff) && buff[0]) { 988 d = parsedate(buff, NULL, NULL); 989 if (d == -1) 990 (void)printf("Bad format - couldn't convert.\n"); 991 else 992 (void)printf("%s", ctime(&d)); 993 (void)printf("\t> "); 994 (void)fflush(stdout); 995 } 996 exit(0); 997 /* NOTREACHED */ 998 } 999 #endif /* defined(TEST) */ 1000