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