1/* 2 * tclGetDate.y -- 3 * 4 * Contains yacc grammar for parsing date and time strings. 5 * The output of this file should be the file tclDate.c which 6 * is used directly in the Tcl sources. 7 * 8 * Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans. 9 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: tclGetDate.y,v 1.18.4.2 2005/11/04 20:15:09 kennykb Exp $ 15 */ 16 17%{ 18/* 19 * tclDate.c -- 20 * 21 * This file is generated from a yacc grammar defined in 22 * the file tclGetDate.y. It should not be edited directly. 23 * 24 * Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans. 25 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 26 * 27 * See the file "license.terms" for information on usage and redistribution 28 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 29 * 30 * SCCSID 31 */ 32 33#include "tclInt.h" 34#include "tclPort.h" 35 36#if defined(MAC_TCL) && !defined(TCL_MAC_USE_MSL_EPOCH) 37# define EPOCH 1904 38# define START_OF_TIME 1904 39# define END_OF_TIME 2039 40#else 41# define EPOCH 1970 42# define START_OF_TIME 1902 43# define END_OF_TIME 2037 44#endif 45 46/* 47 * The offset of tm_year of struct tm returned by localtime, gmtime, etc. 48 * I don't know how universal this is; K&R II, the NetBSD manpages, and 49 * ../compat/strftime.c all agree that tm_year is the year-1900. However, 50 * some systems may have a different value. This #define should be the 51 * same as in ../compat/strftime.c. 52 */ 53#define TM_YEAR_BASE 1900 54 55#define HOUR(x) ((int) (60 * x)) 56#define SECSPERDAY (24L * 60L * 60L) 57#define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0)) 58 59/* 60 * An entry in the lexical lookup table. 61 */ 62typedef struct _TABLE { 63 char *name; 64 int type; 65 time_t value; 66} TABLE; 67 68 69/* 70 * Daylight-savings mode: on, off, or not yet known. 71 */ 72typedef enum _DSTMODE { 73 DSTon, DSToff, DSTmaybe 74} DSTMODE; 75 76/* 77 * Meridian: am, pm, or 24-hour style. 78 */ 79typedef enum _MERIDIAN { 80 MERam, MERpm, MER24 81} MERIDIAN; 82 83 84/* 85 * Global variables. We could get rid of most of these by using a good 86 * union as the yacc stack. (This routine was originally written before 87 * yacc had the %union construct.) Maybe someday; right now we only use 88 * the %union very rarely. 89 */ 90static char *yyInput; 91static DSTMODE yyDSTmode; 92static time_t yyDayOrdinal; 93static time_t yyDayNumber; 94static time_t yyMonthOrdinal; 95static int yyHaveDate; 96static int yyHaveDay; 97static int yyHaveOrdinalMonth; 98static int yyHaveRel; 99static int yyHaveTime; 100static int yyHaveZone; 101static time_t yyTimezone; 102static time_t yyDay; 103static time_t yyHour; 104static time_t yyMinutes; 105static time_t yyMonth; 106static time_t yySeconds; 107static time_t yyYear; 108static MERIDIAN yyMeridian; 109static time_t yyRelMonth; 110static time_t yyRelDay; 111static time_t yyRelSeconds; 112static time_t *yyRelPointer; 113 114/* 115 * Prototypes of internal functions. 116 */ 117static void yyerror _ANSI_ARGS_((char *s)); 118static time_t ToSeconds _ANSI_ARGS_((time_t Hours, time_t Minutes, 119 time_t Seconds, MERIDIAN Meridian)); 120static int Convert _ANSI_ARGS_((time_t Month, time_t Day, time_t Year, 121 time_t Hours, time_t Minutes, time_t Seconds, 122 MERIDIAN Meridia, DSTMODE DSTmode, time_t *TimePtr)); 123static time_t DSTcorrect _ANSI_ARGS_((time_t Start, time_t Future)); 124static time_t NamedDay _ANSI_ARGS_((time_t Start, time_t DayOrdinal, 125 time_t DayNumber)); 126static time_t NamedMonth _ANSI_ARGS_((time_t Start, time_t MonthOrdinal, 127 time_t MonthNumber)); 128static int RelativeMonth _ANSI_ARGS_((time_t Start, time_t RelMonth, 129 time_t *TimePtr)); 130static int RelativeDay _ANSI_ARGS_((time_t Start, time_t RelDay, 131 time_t *TimePtr)); 132static int LookupWord _ANSI_ARGS_((char *buff)); 133static int yylex _ANSI_ARGS_((void)); 134 135int 136yyparse _ANSI_ARGS_((void)); 137%} 138 139%union { 140 time_t Number; 141 enum _MERIDIAN Meridian; 142} 143 144%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT 145%token tSTARDATE tSEC_UNIT tSNUMBER tUNUMBER tZONE tEPOCH tDST tISOBASE 146%token tDAY_UNIT tNEXT 147 148%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT tDST 149%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE tISOBASE tDAY_UNIT 150%type <Number> unit sign tNEXT tSTARDATE 151%type <Meridian> tMERIDIAN o_merid 152 153%% 154 155spec : /* NULL */ 156 | spec item 157 ; 158 159item : time { 160 yyHaveTime++; 161 } 162 | zone { 163 yyHaveZone++; 164 } 165 | date { 166 yyHaveDate++; 167 } 168 | ordMonth { 169 yyHaveOrdinalMonth++; 170 } 171 | day { 172 yyHaveDay++; 173 } 174 | relspec { 175 yyHaveRel++; 176 } 177 | iso { 178 yyHaveTime++; 179 yyHaveDate++; 180 } 181 | trek { 182 yyHaveTime++; 183 yyHaveDate++; 184 yyHaveRel++; 185 } 186 | number 187 ; 188 189time : tUNUMBER tMERIDIAN { 190 yyHour = $1; 191 yyMinutes = 0; 192 yySeconds = 0; 193 yyMeridian = $2; 194 } 195 | tUNUMBER ':' tUNUMBER o_merid { 196 yyHour = $1; 197 yyMinutes = $3; 198 yySeconds = 0; 199 yyMeridian = $4; 200 } 201 | tUNUMBER ':' tUNUMBER '-' tUNUMBER { 202 yyHour = $1; 203 yyMinutes = $3; 204 yyMeridian = MER24; 205 yyDSTmode = DSToff; 206 yyTimezone = ($5 % 100 + ($5 / 100) * 60); 207 } 208 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { 209 yyHour = $1; 210 yyMinutes = $3; 211 yySeconds = $5; 212 yyMeridian = $6; 213 } 214 | tUNUMBER ':' tUNUMBER ':' tUNUMBER '-' tUNUMBER { 215 yyHour = $1; 216 yyMinutes = $3; 217 yySeconds = $5; 218 yyMeridian = MER24; 219 yyDSTmode = DSToff; 220 yyTimezone = ($7 % 100 + ($7 / 100) * 60); 221 } 222 ; 223 224zone : tZONE tDST { 225 yyTimezone = $1; 226 yyDSTmode = DSTon; 227 } 228 | tZONE { 229 yyTimezone = $1; 230 yyDSTmode = DSToff; 231 } 232 | tDAYZONE { 233 yyTimezone = $1; 234 yyDSTmode = DSTon; 235 } 236 ; 237 238day : tDAY { 239 yyDayOrdinal = 1; 240 yyDayNumber = $1; 241 } 242 | tDAY ',' { 243 yyDayOrdinal = 1; 244 yyDayNumber = $1; 245 } 246 | tUNUMBER tDAY { 247 yyDayOrdinal = $1; 248 yyDayNumber = $2; 249 } 250 | sign tUNUMBER tDAY { 251 yyDayOrdinal = $1 * $2; 252 yyDayNumber = $3; 253 } 254 | tNEXT tDAY { 255 yyDayOrdinal = 2; 256 yyDayNumber = $2; 257 } 258 ; 259 260date : tUNUMBER '/' tUNUMBER { 261 yyMonth = $1; 262 yyDay = $3; 263 } 264 | tUNUMBER '/' tUNUMBER '/' tUNUMBER { 265 yyMonth = $1; 266 yyDay = $3; 267 yyYear = $5; 268 } 269 | tISOBASE { 270 yyYear = $1 / 10000; 271 yyMonth = ($1 % 10000)/100; 272 yyDay = $1 % 100; 273 } 274 | tUNUMBER '-' tMONTH '-' tUNUMBER { 275 yyDay = $1; 276 yyMonth = $3; 277 yyYear = $5; 278 } 279 | tUNUMBER '-' tUNUMBER '-' tUNUMBER { 280 yyMonth = $3; 281 yyDay = $5; 282 yyYear = $1; 283 } 284 | tMONTH tUNUMBER { 285 yyMonth = $1; 286 yyDay = $2; 287 } 288 | tMONTH tUNUMBER ',' tUNUMBER { 289 yyMonth = $1; 290 yyDay = $2; 291 yyYear = $4; 292 } 293 | tUNUMBER tMONTH { 294 yyMonth = $2; 295 yyDay = $1; 296 } 297 | tEPOCH { 298 yyMonth = 1; 299 yyDay = 1; 300 yyYear = EPOCH; 301 } 302 | tUNUMBER tMONTH tUNUMBER { 303 yyMonth = $2; 304 yyDay = $1; 305 yyYear = $3; 306 } 307 ; 308 309ordMonth: tNEXT tMONTH { 310 yyMonthOrdinal = 1; 311 yyMonth = $2; 312 } 313 | tNEXT tUNUMBER tMONTH { 314 yyMonthOrdinal = $2; 315 yyMonth = $3; 316 } 317 ; 318 319iso : tISOBASE tZONE tISOBASE { 320 if ($2 != HOUR(- 7)) YYABORT; 321 yyYear = $1 / 10000; 322 yyMonth = ($1 % 10000)/100; 323 yyDay = $1 % 100; 324 yyHour = $3 / 10000; 325 yyMinutes = ($3 % 10000)/100; 326 yySeconds = $3 % 100; 327 } 328 | tISOBASE tZONE tUNUMBER ':' tUNUMBER ':' tUNUMBER { 329 if ($2 != HOUR(- 7)) YYABORT; 330 yyYear = $1 / 10000; 331 yyMonth = ($1 % 10000)/100; 332 yyDay = $1 % 100; 333 yyHour = $3; 334 yyMinutes = $5; 335 yySeconds = $7; 336 } 337 | tISOBASE tISOBASE { 338 yyYear = $1 / 10000; 339 yyMonth = ($1 % 10000)/100; 340 yyDay = $1 % 100; 341 yyHour = $2 / 10000; 342 yyMinutes = ($2 % 10000)/100; 343 yySeconds = $2 % 100; 344 } 345 ; 346 347trek : tSTARDATE tUNUMBER '.' tUNUMBER { 348 /* 349 * Offset computed year by -377 so that the returned years will 350 * be in a range accessible with a 32 bit clock seconds value 351 */ 352 yyYear = $2/1000 + 2323 - 377; 353 yyDay = 1; 354 yyMonth = 1; 355 yyRelDay += (($2%1000)*(365 + IsLeapYear(yyYear)))/1000; 356 yyRelSeconds += $4 * 144 * 60; 357 } 358 ; 359 360relspec : relunits tAGO { 361 yyRelSeconds *= -1; 362 yyRelMonth *= -1; 363 yyRelDay *= -1; 364 } 365 | relunits 366 ; 367relunits : sign tUNUMBER unit { *yyRelPointer += $1 * $2 * $3; } 368 | tUNUMBER unit { *yyRelPointer += $1 * $2; } 369 | tNEXT unit { *yyRelPointer += $2; } 370 | tNEXT tUNUMBER unit { *yyRelPointer += $2 * $3; } 371 | unit { *yyRelPointer += $1; } 372 ; 373sign : '-' { $$ = -1; } 374 | '+' { $$ = 1; } 375 ; 376unit : tSEC_UNIT { $$ = $1; yyRelPointer = &yyRelSeconds; } 377 | tDAY_UNIT { $$ = $1; yyRelPointer = &yyRelDay; } 378 | tMONTH_UNIT { $$ = $1; yyRelPointer = &yyRelMonth; } 379 ; 380 381number : tUNUMBER 382 { 383 if (yyHaveTime && yyHaveDate && !yyHaveRel) { 384 yyYear = $1; 385 } else { 386 yyHaveTime++; 387 if ($1 < 100) { 388 yyHour = $1; 389 yyMinutes = 0; 390 } else { 391 yyHour = $1 / 100; 392 yyMinutes = $1 % 100; 393 } 394 yySeconds = 0; 395 yyMeridian = MER24; 396 } 397 } 398; 399 400o_merid : /* NULL */ { 401 $$ = MER24; 402 } 403 | tMERIDIAN { 404 $$ = $1; 405 } 406 ; 407 408%% 409 410/* 411 * Month and day table. 412 */ 413static TABLE MonthDayTable[] = { 414 { "january", tMONTH, 1 }, 415 { "february", tMONTH, 2 }, 416 { "march", tMONTH, 3 }, 417 { "april", tMONTH, 4 }, 418 { "may", tMONTH, 5 }, 419 { "june", tMONTH, 6 }, 420 { "july", tMONTH, 7 }, 421 { "august", tMONTH, 8 }, 422 { "september", tMONTH, 9 }, 423 { "sept", tMONTH, 9 }, 424 { "october", tMONTH, 10 }, 425 { "november", tMONTH, 11 }, 426 { "december", tMONTH, 12 }, 427 { "sunday", tDAY, 0 }, 428 { "monday", tDAY, 1 }, 429 { "tuesday", tDAY, 2 }, 430 { "tues", tDAY, 2 }, 431 { "wednesday", tDAY, 3 }, 432 { "wednes", tDAY, 3 }, 433 { "thursday", tDAY, 4 }, 434 { "thur", tDAY, 4 }, 435 { "thurs", tDAY, 4 }, 436 { "friday", tDAY, 5 }, 437 { "saturday", tDAY, 6 }, 438 { NULL } 439}; 440 441/* 442 * Time units table. 443 */ 444static TABLE UnitsTable[] = { 445 { "year", tMONTH_UNIT, 12 }, 446 { "month", tMONTH_UNIT, 1 }, 447 { "fortnight", tDAY_UNIT, 14 }, 448 { "week", tDAY_UNIT, 7 }, 449 { "day", tDAY_UNIT, 1 }, 450 { "hour", tSEC_UNIT, 60 * 60 }, 451 { "minute", tSEC_UNIT, 60 }, 452 { "min", tSEC_UNIT, 60 }, 453 { "second", tSEC_UNIT, 1 }, 454 { "sec", tSEC_UNIT, 1 }, 455 { NULL } 456}; 457 458/* 459 * Assorted relative-time words. 460 */ 461static TABLE OtherTable[] = { 462 { "tomorrow", tDAY_UNIT, 1 }, 463 { "yesterday", tDAY_UNIT, -1 }, 464 { "today", tDAY_UNIT, 0 }, 465 { "now", tSEC_UNIT, 0 }, 466 { "last", tUNUMBER, -1 }, 467 { "this", tSEC_UNIT, 0 }, 468 { "next", tNEXT, 1 }, 469#if 0 470 { "first", tUNUMBER, 1 }, 471 { "second", tUNUMBER, 2 }, 472 { "third", tUNUMBER, 3 }, 473 { "fourth", tUNUMBER, 4 }, 474 { "fifth", tUNUMBER, 5 }, 475 { "sixth", tUNUMBER, 6 }, 476 { "seventh", tUNUMBER, 7 }, 477 { "eighth", tUNUMBER, 8 }, 478 { "ninth", tUNUMBER, 9 }, 479 { "tenth", tUNUMBER, 10 }, 480 { "eleventh", tUNUMBER, 11 }, 481 { "twelfth", tUNUMBER, 12 }, 482#endif 483 { "ago", tAGO, 1 }, 484 { "epoch", tEPOCH, 0 }, 485 { "stardate", tSTARDATE, 0}, 486 { NULL } 487}; 488 489/* 490 * The timezone table. (Note: This table was modified to not use any floating 491 * point constants to work around an SGI compiler bug). 492 */ 493static TABLE TimezoneTable[] = { 494 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ 495 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ 496 { "utc", tZONE, HOUR( 0) }, 497 { "uct", tZONE, HOUR( 0) }, /* Universal Coordinated Time */ 498 { "wet", tZONE, HOUR( 0) }, /* Western European */ 499 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ 500 { "wat", tZONE, HOUR( 1) }, /* West Africa */ 501 { "at", tZONE, HOUR( 2) }, /* Azores */ 502#if 0 503 /* For completeness. BST is also British Summer, and GST is 504 * also Guam Standard. */ 505 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ 506 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ 507#endif 508 { "nft", tZONE, HOUR( 7/2) }, /* Newfoundland */ 509 { "nst", tZONE, HOUR( 7/2) }, /* Newfoundland Standard */ 510 { "ndt", tDAYZONE, HOUR( 7/2) }, /* Newfoundland Daylight */ 511 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ 512 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ 513 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ 514 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ 515 { "cst", tZONE, HOUR( 6) }, /* Central Standard */ 516 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ 517 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ 518 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ 519 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ 520 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ 521 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ 522 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ 523 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ 524 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ 525 { "cat", tZONE, HOUR(10) }, /* Central Alaska */ 526 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ 527 { "nt", tZONE, HOUR(11) }, /* Nome */ 528 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ 529 { "cet", tZONE, -HOUR( 1) }, /* Central European */ 530 { "cest", tDAYZONE, -HOUR( 1) }, /* Central European Summer */ 531 { "met", tZONE, -HOUR( 1) }, /* Middle European */ 532 { "mewt", tZONE, -HOUR( 1) }, /* Middle European Winter */ 533 { "mest", tDAYZONE, -HOUR( 1) }, /* Middle European Summer */ 534 { "swt", tZONE, -HOUR( 1) }, /* Swedish Winter */ 535 { "sst", tDAYZONE, -HOUR( 1) }, /* Swedish Summer */ 536 { "fwt", tZONE, -HOUR( 1) }, /* French Winter */ 537 { "fst", tDAYZONE, -HOUR( 1) }, /* French Summer */ 538 { "eet", tZONE, -HOUR( 2) }, /* Eastern Europe, USSR Zone 1 */ 539 { "bt", tZONE, -HOUR( 3) }, /* Baghdad, USSR Zone 2 */ 540 { "it", tZONE, -HOUR( 7/2) }, /* Iran */ 541 { "zp4", tZONE, -HOUR( 4) }, /* USSR Zone 3 */ 542 { "zp5", tZONE, -HOUR( 5) }, /* USSR Zone 4 */ 543 { "ist", tZONE, -HOUR(11/2) }, /* Indian Standard */ 544 { "zp6", tZONE, -HOUR( 6) }, /* USSR Zone 5 */ 545#if 0 546 /* For completeness. NST is also Newfoundland Stanard, nad SST is 547 * also Swedish Summer. */ 548 { "nst", tZONE, -HOUR(13/2) }, /* North Sumatra */ 549 { "sst", tZONE, -HOUR( 7) }, /* South Sumatra, USSR Zone 6 */ 550#endif /* 0 */ 551 { "wast", tZONE, -HOUR( 7) }, /* West Australian Standard */ 552 { "wadt", tDAYZONE, -HOUR( 7) }, /* West Australian Daylight */ 553 { "jt", tZONE, -HOUR(15/2) }, /* Java (3pm in Cronusland!) */ 554 { "cct", tZONE, -HOUR( 8) }, /* China Coast, USSR Zone 7 */ 555 { "jst", tZONE, -HOUR( 9) }, /* Japan Standard, USSR Zone 8 */ 556 { "jdt", tDAYZONE, -HOUR( 9) }, /* Japan Daylight */ 557 { "kst", tZONE, -HOUR( 9) }, /* Korea Standard */ 558 { "kdt", tDAYZONE, -HOUR( 9) }, /* Korea Daylight */ 559 { "cast", tZONE, -HOUR(19/2) }, /* Central Australian Standard */ 560 { "cadt", tDAYZONE, -HOUR(19/2) }, /* Central Australian Daylight */ 561 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ 562 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ 563 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ 564 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ 565 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ 566 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ 567 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ 568 /* ADDED BY Marco Nijdam */ 569 { "dst", tDST, HOUR( 0) }, /* DST on (hour is ignored) */ 570 /* End ADDED */ 571 { NULL } 572}; 573 574/* 575 * Military timezone table. 576 */ 577static 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 } 604}; 605 606 607/* 608 * Dump error messages in the bit bucket. 609 */ 610static void 611yyerror(s) 612 char *s; 613{ 614} 615 616 617static time_t 618ToSeconds(Hours, Minutes, Seconds, Meridian) 619 time_t Hours; 620 time_t Minutes; 621 time_t Seconds; 622 MERIDIAN Meridian; 623{ 624 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) 625 return -1; 626 switch (Meridian) { 627 case MER24: 628 if (Hours < 0 || Hours > 23) 629 return -1; 630 return (Hours * 60L + Minutes) * 60L + Seconds; 631 case MERam: 632 if (Hours < 1 || Hours > 12) 633 return -1; 634 return ((Hours % 12) * 60L + Minutes) * 60L + Seconds; 635 case MERpm: 636 if (Hours < 1 || Hours > 12) 637 return -1; 638 return (((Hours % 12) + 12) * 60L + Minutes) * 60L + Seconds; 639 } 640 return -1; /* Should never be reached */ 641} 642 643/* 644 *----------------------------------------------------------------------------- 645 * 646 * Convert -- 647 * 648 * Convert a {month, day, year, hours, minutes, seconds, meridian, dst} 649 * tuple into a clock seconds value. 650 * 651 * Results: 652 * 0 or -1 indicating success or failure. 653 * 654 * Side effects: 655 * Fills TimePtr with the computed value. 656 * 657 *----------------------------------------------------------------------------- 658 */ 659static int 660Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode, TimePtr) 661 time_t Month; 662 time_t Day; 663 time_t Year; 664 time_t Hours; 665 time_t Minutes; 666 time_t Seconds; 667 MERIDIAN Meridian; 668 DSTMODE DSTmode; 669 time_t *TimePtr; 670{ 671 static int DaysInMonth[12] = { 672 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 673 }; 674 time_t tod; 675 time_t Julian; 676 int i; 677 678 /* Figure out how many days are in February for the given year. 679 * Every year divisible by 4 is a leap year. 680 * But, every year divisible by 100 is not a leap year. 681 * But, every year divisible by 400 is a leap year after all. 682 */ 683 DaysInMonth[1] = IsLeapYear(Year) ? 29 : 28; 684 685 /* Check the inputs for validity */ 686 if (Month < 1 || Month > 12 687 || Year < START_OF_TIME || Year > END_OF_TIME 688 || Day < 1 || Day > DaysInMonth[(int)--Month]) 689 return -1; 690 691 /* Start computing the value. First determine the number of days 692 * represented by the date, then multiply by the number of seconds/day. 693 */ 694 for (Julian = Day - 1, i = 0; i < Month; i++) 695 Julian += DaysInMonth[i]; 696 if (Year >= EPOCH) { 697 for (i = EPOCH; i < Year; i++) 698 Julian += 365 + IsLeapYear(i); 699 } else { 700 for (i = Year; i < EPOCH; i++) 701 Julian -= 365 + IsLeapYear(i); 702 } 703 Julian *= SECSPERDAY; 704 705 /* Add the timezone offset ?? */ 706 Julian += yyTimezone * 60L; 707 708 /* Add the number of seconds represented by the time component */ 709 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) 710 return -1; 711 Julian += tod; 712 713 /* Perform a preliminary DST compensation ?? */ 714 if (DSTmode == DSTon 715 || (DSTmode == DSTmaybe && TclpGetDate((TclpTime_t)&Julian, 0)->tm_isdst)) 716 Julian -= 60 * 60; 717 *TimePtr = Julian; 718 return 0; 719} 720 721 722static time_t 723DSTcorrect(Start, Future) 724 time_t Start; 725 time_t Future; 726{ 727 time_t StartDay; 728 time_t FutureDay; 729 StartDay = (TclpGetDate((TclpTime_t)&Start, 0)->tm_hour + 1) % 24; 730 FutureDay = (TclpGetDate((TclpTime_t)&Future, 0)->tm_hour + 1) % 24; 731 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; 732} 733 734 735static time_t 736NamedDay(Start, DayOrdinal, DayNumber) 737 time_t Start; 738 time_t DayOrdinal; 739 time_t DayNumber; 740{ 741 struct tm *tm; 742 time_t now; 743 744 now = Start; 745 tm = TclpGetDate((TclpTime_t)&now, 0); 746 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); 747 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); 748 return DSTcorrect(Start, now); 749} 750 751static time_t 752NamedMonth(Start, MonthOrdinal, MonthNumber) 753 time_t Start; 754 time_t MonthOrdinal; 755 time_t MonthNumber; 756{ 757 struct tm *tm; 758 time_t now; 759 int result; 760 761 now = Start; 762 tm = TclpGetDate((TclpTime_t)&now, 0); 763 /* To compute the next n'th month, we use this alg: 764 * add n to year value 765 * if currentMonth < requestedMonth decrement year value by 1 (so that 766 * doing next february from january gives us february of the current year) 767 * set day to 1, time to 0 768 */ 769 tm->tm_year += MonthOrdinal; 770 if (tm->tm_mon < MonthNumber - 1) { 771 tm->tm_year--; 772 } 773 result = Convert(MonthNumber, (time_t) 1, tm->tm_year + TM_YEAR_BASE, 774 (time_t) 0, (time_t) 0, (time_t) 0, MER24, DSTmaybe, &now); 775 if (result < 0) { 776 return 0; 777 } 778 return DSTcorrect(Start, now); 779} 780 781static int 782RelativeMonth(Start, RelMonth, TimePtr) 783 time_t Start; 784 time_t RelMonth; 785 time_t *TimePtr; 786{ 787 struct tm *tm; 788 time_t Month; 789 time_t Year; 790 time_t Julian; 791 int result; 792 793 if (RelMonth == 0) { 794 *TimePtr = 0; 795 return 0; 796 } 797 tm = TclpGetDate((TclpTime_t)&Start, 0); 798 Month = 12 * (tm->tm_year + TM_YEAR_BASE) + tm->tm_mon + RelMonth; 799 Year = Month / 12; 800 Month = Month % 12 + 1; 801 result = Convert(Month, (time_t) tm->tm_mday, Year, 802 (time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec, 803 MER24, DSTmaybe, &Julian); 804 805 /* 806 * The Julian time returned above is behind by one day, if "month" 807 * or "year" is used to specify relative time and the GMT flag is true. 808 * This problem occurs only when the current time is closer to 809 * midnight, the difference being not more than its time difference 810 * with GMT. For example, in US/Pacific time zone, the problem occurs 811 * whenever the current time is between midnight to 8:00am or 7:00amDST. 812 * See Bug# 413397 for more details and sample script. 813 * To resolve this bug, we simply add the number of seconds corresponding 814 * to timezone difference with GMT to Julian time, if GMT flag is true. 815 */ 816 817 if (TclDateTimezone == 0) { 818 Julian += TclpGetTimeZone((unsigned long) Start) * 60L; 819 } 820 821 /* 822 * The following iteration takes into account the case were we jump 823 * into a "short month". Far example, "one month from Jan 31" will 824 * fail because there is no Feb 31. The code below will reduce the 825 * day and try converting the date until we succed or the date equals 826 * 28 (which always works unless the date is bad in another way). 827 */ 828 829 while ((result != 0) && (tm->tm_mday > 28)) { 830 tm->tm_mday--; 831 result = Convert(Month, (time_t) tm->tm_mday, Year, 832 (time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec, 833 MER24, DSTmaybe, &Julian); 834 } 835 if (result != 0) { 836 return -1; 837 } 838 *TimePtr = DSTcorrect(Start, Julian); 839 return 0; 840} 841 842 843/* 844 *----------------------------------------------------------------------------- 845 * 846 * RelativeDay -- 847 * 848 * Given a starting time and a number of days before or after, compute the 849 * DST corrected difference between those dates. 850 * 851 * Results: 852 * 1 or -1 indicating success or failure. 853 * 854 * Side effects: 855 * Fills TimePtr with the computed value. 856 * 857 *----------------------------------------------------------------------------- 858 */ 859 860static int 861RelativeDay(Start, RelDay, TimePtr) 862 time_t Start; 863 time_t RelDay; 864 time_t *TimePtr; 865{ 866 time_t new; 867 868 new = Start + (RelDay * 60 * 60 * 24); 869 *TimePtr = DSTcorrect(Start, new); 870 return 1; 871} 872 873static int 874LookupWord(buff) 875 char *buff; 876{ 877 register char *p; 878 register char *q; 879 register TABLE *tp; 880 int i; 881 int abbrev; 882 883 /* 884 * Make it lowercase. 885 */ 886 887 Tcl_UtfToLower(buff); 888 889 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { 890 yylval.Meridian = MERam; 891 return tMERIDIAN; 892 } 893 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { 894 yylval.Meridian = MERpm; 895 return tMERIDIAN; 896 } 897 898 /* 899 * See if we have an abbreviation for a month. 900 */ 901 if (strlen(buff) == 3) { 902 abbrev = 1; 903 } else if (strlen(buff) == 4 && buff[3] == '.') { 904 abbrev = 1; 905 buff[3] = '\0'; 906 } else { 907 abbrev = 0; 908 } 909 910 for (tp = MonthDayTable; tp->name; tp++) { 911 if (abbrev) { 912 if (strncmp(buff, tp->name, 3) == 0) { 913 yylval.Number = tp->value; 914 return tp->type; 915 } 916 } else if (strcmp(buff, tp->name) == 0) { 917 yylval.Number = tp->value; 918 return tp->type; 919 } 920 } 921 922 for (tp = TimezoneTable; tp->name; tp++) { 923 if (strcmp(buff, tp->name) == 0) { 924 yylval.Number = tp->value; 925 return tp->type; 926 } 927 } 928 929 for (tp = UnitsTable; tp->name; tp++) { 930 if (strcmp(buff, tp->name) == 0) { 931 yylval.Number = tp->value; 932 return tp->type; 933 } 934 } 935 936 /* 937 * Strip off any plural and try the units table again. 938 */ 939 i = strlen(buff) - 1; 940 if (buff[i] == 's') { 941 buff[i] = '\0'; 942 for (tp = UnitsTable; tp->name; tp++) { 943 if (strcmp(buff, tp->name) == 0) { 944 yylval.Number = tp->value; 945 return tp->type; 946 } 947 } 948 } 949 950 for (tp = OtherTable; tp->name; tp++) { 951 if (strcmp(buff, tp->name) == 0) { 952 yylval.Number = tp->value; 953 return tp->type; 954 } 955 } 956 957 /* 958 * Military timezones. 959 */ 960 if (buff[1] == '\0' && !(*buff & 0x80) 961 && isalpha(UCHAR(*buff))) { /* INTL: ISO only */ 962 for (tp = MilitaryTable; tp->name; tp++) { 963 if (strcmp(buff, tp->name) == 0) { 964 yylval.Number = tp->value; 965 return tp->type; 966 } 967 } 968 } 969 970 /* 971 * Drop out any periods and try the timezone table again. 972 */ 973 for (i = 0, p = q = buff; *q; q++) 974 if (*q != '.') { 975 *p++ = *q; 976 } else { 977 i++; 978 } 979 *p = '\0'; 980 if (i) { 981 for (tp = TimezoneTable; tp->name; tp++) { 982 if (strcmp(buff, tp->name) == 0) { 983 yylval.Number = tp->value; 984 return tp->type; 985 } 986 } 987 } 988 989 return tID; 990} 991 992 993static int 994yylex() 995{ 996 register char c; 997 register char *p; 998 char buff[20]; 999 int Count; 1000 1001 for ( ; ; ) { 1002 while (isspace(UCHAR(*yyInput))) { 1003 yyInput++; 1004 } 1005 1006 if (isdigit(UCHAR(c = *yyInput))) { /* INTL: digit */ 1007 /* convert the string into a number; count the number of digits */ 1008 Count = 0; 1009 for (yylval.Number = 0; 1010 isdigit(UCHAR(c = *yyInput++)); ) { /* INTL: digit */ 1011 yylval.Number = 10 * yylval.Number + c - '0'; 1012 Count++; 1013 } 1014 yyInput--; 1015 /* A number with 6 or more digits is considered an ISO 8601 base */ 1016 if (Count >= 6) { 1017 return tISOBASE; 1018 } else { 1019 return tUNUMBER; 1020 } 1021 } 1022 if (!(c & 0x80) && isalpha(UCHAR(c))) { /* INTL: ISO only. */ 1023 for (p = buff; isalpha(UCHAR(c = *yyInput++)) /* INTL: ISO only. */ 1024 || c == '.'; ) { 1025 if (p < &buff[sizeof buff - 1]) { 1026 *p++ = c; 1027 } 1028 } 1029 *p = '\0'; 1030 yyInput--; 1031 return LookupWord(buff); 1032 } 1033 if (c != '(') { 1034 return *yyInput++; 1035 } 1036 Count = 0; 1037 do { 1038 c = *yyInput++; 1039 if (c == '\0') { 1040 return c; 1041 } else if (c == '(') { 1042 Count++; 1043 } else if (c == ')') { 1044 Count--; 1045 } 1046 } while (Count > 0); 1047 } 1048} 1049 1050/* 1051 * Specify zone is of -50000 to force GMT. (This allows BST to work). 1052 */ 1053 1054int 1055TclGetDate(p, now, zone, timePtr) 1056 char *p; 1057 Tcl_WideInt now; 1058 long zone; 1059 Tcl_WideInt *timePtr; 1060{ 1061 struct tm *tm; 1062 time_t Start; 1063 time_t Time; 1064 time_t tod; 1065 int thisyear; 1066 1067 yyInput = p; 1068 /* now has to be cast to a time_t for 64bit compliance */ 1069 Start = (time_t) now; 1070 tm = TclpGetDate((TclpTime_t) &Start, (zone == -50000)); 1071 thisyear = tm->tm_year + TM_YEAR_BASE; 1072 yyYear = thisyear; 1073 yyMonth = tm->tm_mon + 1; 1074 yyDay = tm->tm_mday; 1075 yyTimezone = zone; 1076 if (zone == -50000) { 1077 yyDSTmode = DSToff; /* assume GMT */ 1078 yyTimezone = 0; 1079 } else { 1080 yyDSTmode = DSTmaybe; 1081 } 1082 yyHour = 0; 1083 yyMinutes = 0; 1084 yySeconds = 0; 1085 yyMeridian = MER24; 1086 yyRelSeconds = 0; 1087 yyRelMonth = 0; 1088 yyRelDay = 0; 1089 yyRelPointer = NULL; 1090 1091 yyHaveDate = 0; 1092 yyHaveDay = 0; 1093 yyHaveOrdinalMonth = 0; 1094 yyHaveRel = 0; 1095 yyHaveTime = 0; 1096 yyHaveZone = 0; 1097 1098 if (yyparse() || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || 1099 yyHaveDay > 1 || yyHaveOrdinalMonth > 1) { 1100 return -1; 1101 } 1102 1103 if (yyHaveDate || yyHaveTime || yyHaveDay) { 1104 if (TclDateYear < 0) { 1105 TclDateYear = -TclDateYear; 1106 } 1107 /* 1108 * The following line handles years that are specified using 1109 * only two digits. The line of code below implements a policy 1110 * defined by the X/Open workgroup on the millinium rollover. 1111 * Note: some of those dates may not actually be valid on some 1112 * platforms. The POSIX standard startes that the dates 70-99 1113 * shall refer to 1970-1999 and 00-38 shall refer to 2000-2038. 1114 * This later definition should work on all platforms. 1115 */ 1116 1117 if (TclDateYear < 100) { 1118 if (TclDateYear >= 69) { 1119 TclDateYear += 1900; 1120 } else { 1121 TclDateYear += 2000; 1122 } 1123 } 1124 if (Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, 1125 yyMeridian, yyDSTmode, &Start) < 0) { 1126 return -1; 1127 } 1128 } else { 1129 Start = (time_t) now; 1130 if (!yyHaveRel) { 1131 Start -= ((tm->tm_hour * 60L * 60L) + 1132 tm->tm_min * 60L) + tm->tm_sec; 1133 } 1134 } 1135 1136 Start += yyRelSeconds; 1137 if (RelativeMonth(Start, yyRelMonth, &Time) < 0) { 1138 return -1; 1139 } 1140 Start += Time; 1141 1142 if (RelativeDay(Start, yyRelDay, &Time) < 0) { 1143 return -1; 1144 } 1145 Start += Time; 1146 1147 if (yyHaveDay && !yyHaveDate) { 1148 tod = NamedDay(Start, yyDayOrdinal, yyDayNumber); 1149 Start += tod; 1150 } 1151 1152 if (yyHaveOrdinalMonth) { 1153 tod = NamedMonth(Start, yyMonthOrdinal, yyMonth); 1154 Start += tod; 1155 } 1156 1157 *timePtr = Start; 1158 return 0; 1159} 1160