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