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