| 1 |
%{
|
| 2 |
/* $Id$
|
| 3 |
**
|
| 4 |
** Originally written by Steven M. Bellovin <smb@research.att.com> while
|
| 5 |
** at the University of North Carolina at Chapel Hill. Later tweaked by
|
| 6 |
** a couple of people on Usenet. Completely overhauled by Rich $alz
|
| 7 |
** <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
|
| 8 |
** Further revised (removed obsolete constructs and cleaned up timezone
|
| 9 |
** names) in August, 1991, by Rich. Paul Eggert <eggert@twinsun.com>
|
| 10 |
** helped in September, 1992.
|
| 11 |
**
|
| 12 |
** This grammar has six shift/reduce conflicts.
|
| 13 |
**
|
| 14 |
** This code is in the public domain and has no copyright.
|
| 15 |
*/
|
| 16 |
|
| 17 |
#include <stdio.h>
|
| 18 |
#include <string.h>
|
| 19 |
|
| 20 |
// #include "config.h"
|
| 21 |
// #include "clibrary.h"
|
| 22 |
#include <ctype.h>
|
| 23 |
|
| 24 |
#if defined(_HPUX_SOURCE)
|
| 25 |
# include <alloca.h>
|
| 26 |
#endif
|
| 27 |
|
| 28 |
#ifdef TM_IN_SYS_TIME
|
| 29 |
# include <sys/time.h>
|
| 30 |
#else
|
| 31 |
# include <time.h>
|
| 32 |
#endif
|
| 33 |
|
| 34 |
// #include "libinn.h"
|
| 35 |
|
| 36 |
/* Used for iterating through arrays. ARRAY_SIZE returns the number of
|
| 37 |
elements in the array (useful for a < upper bound in a for loop) and
|
| 38 |
ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
|
| 39 |
legal to refer to such a pointer as long as it's never dereferenced). */
|
| 40 |
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
| 41 |
#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)])
|
| 42 |
|
| 43 |
/* On some systems, the macros defined by <ctype.h> are only vaild on ASCII
|
| 44 |
characters (those characters that isascii() says are ASCII). This comes
|
| 45 |
into play when applying <ctype.h> macros to eight-bit data. autoconf
|
| 46 |
checks for this with as part of AC_HEADER_STDC, so if autoconf doesn't
|
| 47 |
think our headers are standard, check isascii() first. */
|
| 48 |
#if STDC_HEADERS
|
| 49 |
# define CTYPE(isXXXXX, c) (isXXXXX((unsigned char)(c)))
|
| 50 |
#else
|
| 51 |
# define CTYPE(isXXXXX, c) \
|
| 52 |
(isascii((unsigned char)(c)) && isXXXXX((unsigned char)(c)))
|
| 53 |
#endif
|
| 54 |
|
| 55 |
|
| 56 |
#define yylhs date_yylhs
|
| 57 |
#define yylen date_yylen
|
| 58 |
#define yydefred date_yydefred
|
| 59 |
#define yydgoto date_yydgoto
|
| 60 |
#define yysindex date_yysindex
|
| 61 |
#define yyrindex date_yyrindex
|
| 62 |
#define yygindex date_yygindex
|
| 63 |
#define yytable date_yytable
|
| 64 |
#define yycheck date_yycheck
|
| 65 |
#define yyparse date_parse
|
| 66 |
#define yylex date_lex
|
| 67 |
#define yyerror date_error
|
| 68 |
#define yymaxdepth date_yymaxdepth
|
| 69 |
|
| 70 |
|
| 71 |
static int date_lex(void);
|
| 72 |
|
| 73 |
|
| 74 |
/* See the LeapYears table in Convert. */
|
| 75 |
#define EPOCH 1970
|
| 76 |
#define END_OF_TIME 2038
|
| 77 |
/* Constants for general time calculations. */
|
| 78 |
#define DST_OFFSET 1
|
| 79 |
#define SECSPERDAY (24L * 60L * 60L)
|
| 80 |
/* Readability for TABLE stuff. */
|
| 81 |
#define HOUR(x) (x * 60)
|
| 82 |
|
| 83 |
#define LPAREN '('
|
| 84 |
#define RPAREN ')'
|
| 85 |
#define IS7BIT(x) ((unsigned int)(x) < 0200)
|
| 86 |
|
| 87 |
|
| 88 |
/*
|
| 89 |
** An entry in the lexical lookup table.
|
| 90 |
*/
|
| 91 |
typedef struct _TABLE {
|
| 92 |
const char * name;
|
| 93 |
int type;
|
| 94 |
time_t value;
|
| 95 |
} TABLE;
|
| 96 |
|
| 97 |
/*
|
| 98 |
** Daylight-savings mode: on, off, or not yet known.
|
| 99 |
*/
|
| 100 |
typedef enum _DSTMODE {
|
| 101 |
DSTon, DSToff, DSTmaybe
|
| 102 |
} DSTMODE;
|
| 103 |
|
| 104 |
/*
|
| 105 |
** Meridian: am, pm, or 24-hour style.
|
| 106 |
*/
|
| 107 |
typedef enum _MERIDIAN {
|
| 108 |
MERam, MERpm, MER24
|
| 109 |
} MERIDIAN;
|
| 110 |
|
| 111 |
|
| 112 |
/*
|
| 113 |
** Global variables. We could get rid of most of them by using a yacc
|
| 114 |
** union, but this is more efficient. (This routine predates the
|
| 115 |
** yacc %union construct.)
|
| 116 |
*/
|
| 117 |
static char *yyInput;
|
| 118 |
static DSTMODE yyDSTmode;
|
| 119 |
static int yyHaveDate;
|
| 120 |
static int yyHaveRel;
|
| 121 |
static int yyHaveTime;
|
| 122 |
static time_t yyTimezone;
|
| 123 |
static time_t yyDay;
|
| 124 |
static time_t yyHour;
|
| 125 |
static time_t yyMinutes;
|
| 126 |
static time_t yyMonth;
|
| 127 |
static time_t yySeconds;
|
| 128 |
static time_t yyYear;
|
| 129 |
static MERIDIAN yyMeridian;
|
| 130 |
static time_t yyRelMonth;
|
| 131 |
static time_t yyRelSeconds;
|
| 132 |
|
| 133 |
|
| 134 |
/* extern struct tm *localtime(); */
|
| 135 |
|
| 136 |
static void date_error(const char *s);
|
| 137 |
%}
|
| 138 |
|
| 139 |
%union {
|
| 140 |
time_t Number;
|
| 141 |
enum _MERIDIAN Meridian;
|
| 142 |
}
|
| 143 |
|
| 144 |
%token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
|
| 145 |
%token tUNUMBER tZONE
|
| 146 |
|
| 147 |
%type <Number> tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
|
| 148 |
%type <Number> tSNUMBER tUNUMBER tZONE numzone zone
|
| 149 |
%type <Meridian> tMERIDIAN o_merid
|
| 150 |
|
| 151 |
%%
|
| 152 |
|
| 153 |
spec : /* NULL */
|
| 154 |
| spec item
|
| 155 |
;
|
| 156 |
|
| 157 |
item : time {
|
| 158 |
yyHaveTime++;
|
| 159 |
#if defined(lint)
|
| 160 |
/* I am compulsive about lint natterings... */
|
| 161 |
if (yyHaveTime == -1) {
|
| 162 |
YYERROR;
|
| 163 |
}
|
| 164 |
#endif /* defined(lint) */
|
| 165 |
}
|
| 166 |
| time zone {
|
| 167 |
yyHaveTime++;
|
| 168 |
yyTimezone = $2;
|
| 169 |
}
|
| 170 |
| date {
|
| 171 |
yyHaveDate++;
|
| 172 |
}
|
| 173 |
| rel {
|
| 174 |
yyHaveRel = 1;
|
| 175 |
}
|
| 176 |
;
|
| 177 |
|
| 178 |
time : tUNUMBER o_merid {
|
| 179 |
if ($1 < 100) {
|
| 180 |
yyHour = $1;
|
| 181 |
yyMinutes = 0;
|
| 182 |
}
|
| 183 |
else {
|
| 184 |
yyHour = $1 / 100;
|
| 185 |
yyMinutes = $1 % 100;
|
| 186 |
}
|
| 187 |
yySeconds = 0;
|
| 188 |
yyMeridian = $2;
|
| 189 |
}
|
| 190 |
| tUNUMBER ':' tUNUMBER o_merid {
|
| 191 |
yyHour = $1;
|
| 192 |
yyMinutes = $3;
|
| 193 |
yySeconds = 0;
|
| 194 |
yyMeridian = $4;
|
| 195 |
}
|
| 196 |
| tUNUMBER ':' tUNUMBER numzone {
|
| 197 |
yyHour = $1;
|
| 198 |
yyMinutes = $3;
|
| 199 |
yyTimezone = $4;
|
| 200 |
yyMeridian = MER24;
|
| 201 |
yyDSTmode = DSToff;
|
| 202 |
}
|
| 203 |
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
|
| 204 |
yyHour = $1;
|
| 205 |
yyMinutes = $3;
|
| 206 |
yySeconds = $5;
|
| 207 |
yyMeridian = $6;
|
| 208 |
}
|
| 209 |
| tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
|
| 210 |
yyHour = $1;
|
| 211 |
yyMinutes = $3;
|
| 212 |
yySeconds = $5;
|
| 213 |
yyTimezone = $6;
|
| 214 |
yyMeridian = MER24;
|
| 215 |
yyDSTmode = DSToff;
|
| 216 |
}
|
| 217 |
;
|
| 218 |
|
| 219 |
zone : tZONE {
|
| 220 |
$$ = $1;
|
| 221 |
yyDSTmode = DSToff;
|
| 222 |
}
|
| 223 |
| tDAYZONE {
|
| 224 |
$$ = $1;
|
| 225 |
yyDSTmode = DSTon;
|
| 226 |
}
|
| 227 |
| tZONE numzone {
|
| 228 |
/* Only allow "GMT+300" and "GMT-0800" */
|
| 229 |
if ($1 != 0) {
|
| 230 |
YYABORT;
|
| 231 |
}
|
| 232 |
$$ = $2;
|
| 233 |
yyDSTmode = DSToff;
|
| 234 |
}
|
| 235 |
| numzone {
|
| 236 |
$$ = $1;
|
| 237 |
yyDSTmode = DSToff;
|
| 238 |
}
|
| 239 |
;
|
| 240 |
|
| 241 |
numzone : tSNUMBER {
|
| 242 |
int i;
|
| 243 |
|
| 244 |
/* Unix and GMT and numeric timezones -- a little confusing. */
|
| 245 |
if ($1 < 0) {
|
| 246 |
/* Don't work with negative modulus. */
|
| 247 |
$1 = -$1;
|
| 248 |
if ($1 > 9999 || (i = $1 % 100) >= 60) {
|
| 249 |
YYABORT;
|
| 250 |
}
|
| 251 |
$$ = ($1 / 100) * 60 + i;
|
| 252 |
}
|
| 253 |
else {
|
| 254 |
if ($1 > 9999 || (i = $1 % 100) >= 60) {
|
| 255 |
YYABORT;
|
| 256 |
}
|
| 257 |
$$ = -(($1 / 100) * 60 + i);
|
| 258 |
}
|
| 259 |
}
|
| 260 |
;
|
| 261 |
|
| 262 |
date : tUNUMBER '/' tUNUMBER {
|
| 263 |
yyMonth = $1;
|
| 264 |
yyDay = $3;
|
| 265 |
}
|
| 266 |
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
|
| 267 |
if ($1 > 100) {
|
| 268 |
/* assume YYYY/MM/DD format, so need not to add 1900 */
|
| 269 |
if ($1 > 999) {
|
| 270 |
yyYear = $1;
|
| 271 |
} else {
|
| 272 |
yyYear = 1900 + $1;
|
| 273 |
}
|
| 274 |
yyMonth = $3;
|
| 275 |
yyDay = $5;
|
| 276 |
}
|
| 277 |
else {
|
| 278 |
/* assume MM/DD/YY* format */
|
| 279 |
yyMonth = $1;
|
| 280 |
yyDay = $3;
|
| 281 |
if ($5 > 999) {
|
| 282 |
/* assume year is YYYY format, so need not to add 1900 */
|
| 283 |
yyYear = $5;
|
| 284 |
} else if ($5 < 100) {
|
| 285 |
/* assume year is YY format, so need to add 1900 */
|
| 286 |
yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100;
|
| 287 |
} else {
|
| 288 |
yyYear = 1900 + $5;
|
| 289 |
}
|
| 290 |
}
|
| 291 |
}
|
| 292 |
| tMONTH tUNUMBER {
|
| 293 |
yyMonth = $1;
|
| 294 |
yyDay = $2;
|
| 295 |
}
|
| 296 |
| tMONTH tUNUMBER ',' tUNUMBER {
|
| 297 |
yyMonth = $1;
|
| 298 |
yyDay = $2;
|
| 299 |
if ($4 > 999) {
|
| 300 |
/* assume year is YYYY format, so need not to add 1900 */
|
| 301 |
yyYear = $4;
|
| 302 |
} else if ($4 < 100) {
|
| 303 |
/* assume year is YY format, so need to add 1900 */
|
| 304 |
yyYear = $4 + (yyYear / 100 + (yyYear % 100 - $4) / 50) * 100;
|
| 305 |
} else {
|
| 306 |
yyYear = 1900 + $4;
|
| 307 |
}
|
| 308 |
}
|
| 309 |
| tUNUMBER tMONTH {
|
| 310 |
yyDay = $1;
|
| 311 |
yyMonth = $2;
|
| 312 |
}
|
| 313 |
| tUNUMBER tMONTH tUNUMBER {
|
| 314 |
yyDay = $1;
|
| 315 |
yyMonth = $2;
|
| 316 |
if ($3 > 999) {
|
| 317 |
/* assume year is YYYY format, so need not to add 1900 */
|
| 318 |
yyYear = $3;
|
| 319 |
} else if ($3 < 100) {
|
| 320 |
/* assume year is YY format, so need to add 1900 */
|
| 321 |
yyYear = $3 + (yyYear / 100 + (yyYear % 100 - $3) / 50) * 100;
|
| 322 |
} else {
|
| 323 |
yyYear = 1900 + $3;
|
| 324 |
}
|
| 325 |
}
|
| 326 |
| tDAY ',' tUNUMBER tMONTH tUNUMBER {
|
| 327 |
yyDay = $3;
|
| 328 |
yyMonth = $4;
|
| 329 |
if ($5 > 999) {
|
| 330 |
/* assume year is YYYY format, so need not to add 1900 */
|
| 331 |
yyYear = $5;
|
| 332 |
} else if ($5 < 100) {
|
| 333 |
/* assume year is YY format, so need to add 1900 */
|
| 334 |
yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100;
|
| 335 |
} else {
|
| 336 |
yyYear = 1900 + $5;
|
| 337 |
}
|
| 338 |
}
|
| 339 |
;
|
| 340 |
|
| 341 |
rel : tSNUMBER tSEC_UNIT {
|
| 342 |
yyRelSeconds += $1 * $2;
|
| 343 |
}
|
| 344 |
| tUNUMBER tSEC_UNIT {
|
| 345 |
yyRelSeconds += $1 * $2;
|
| 346 |
}
|
| 347 |
| tSNUMBER tMONTH_UNIT {
|
| 348 |
yyRelMonth += $1 * $2;
|
| 349 |
}
|
| 350 |
| tUNUMBER tMONTH_UNIT {
|
| 351 |
yyRelMonth += $1 * $2;
|
| 352 |
}
|
| 353 |
;
|
| 354 |
|
| 355 |
o_merid : /* NULL */ {
|
| 356 |
$$ = MER24;
|
| 357 |
}
|
| 358 |
| tMERIDIAN {
|
| 359 |
$$ = $1;
|
| 360 |
}
|
| 361 |
;
|
| 362 |
|
| 363 |
%%
|
| 364 |
|
| 365 |
/* Month and day table. */
|
| 366 |
static TABLE MonthDayTable[] = {
|
| 367 |
{ "january", tMONTH, 1 },
|
| 368 |
{ "february", tMONTH, 2 },
|
| 369 |
{ "march", tMONTH, 3 },
|
| 370 |
{ "april", tMONTH, 4 },
|
| 371 |
{ "may", tMONTH, 5 },
|
| 372 |
{ "june", tMONTH, 6 },
|
| 373 |
{ "july", tMONTH, 7 },
|
| 374 |
{ "august", tMONTH, 8 },
|
| 375 |
{ "september", tMONTH, 9 },
|
| 376 |
{ "october", tMONTH, 10 },
|
| 377 |
{ "november", tMONTH, 11 },
|
| 378 |
{ "december", tMONTH, 12 },
|
| 379 |
/* The value of the day isn't used... */
|
| 380 |
{ "sunday", tDAY, 0 },
|
| 381 |
{ "monday", tDAY, 0 },
|
| 382 |
{ "tuesday", tDAY, 0 },
|
| 383 |
{ "wednesday", tDAY, 0 },
|
| 384 |
{ "thursday", tDAY, 0 },
|
| 385 |
{ "friday", tDAY, 0 },
|
| 386 |
{ "saturday", tDAY, 0 },
|
| 387 |
};
|
| 388 |
|
| 389 |
/* Time units table. */
|
| 390 |
static TABLE UnitsTable[] = {
|
| 391 |
{ "year", tMONTH_UNIT, 12 },
|
| 392 |
{ "month", tMONTH_UNIT, 1 },
|
| 393 |
{ "week", tSEC_UNIT, 7 * 24 * 60 * 60 },
|
| 394 |
{ "day", tSEC_UNIT, 1 * 24 * 60 * 60 },
|
| 395 |
{ "hour", tSEC_UNIT, 60 * 60 },
|
| 396 |
{ "minute", tSEC_UNIT, 60 },
|
| 397 |
{ "min", tSEC_UNIT, 60 },
|
| 398 |
{ "second", tSEC_UNIT, 1 },
|
| 399 |
{ "sec", tSEC_UNIT, 1 },
|
| 400 |
};
|
| 401 |
|
| 402 |
/* Timezone table. */
|
| 403 |
static TABLE TimezoneTable[] = {
|
| 404 |
{ "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
|
| 405 |
{ "ut", tZONE, HOUR( 0) }, /* Universal */
|
| 406 |
{ "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */
|
| 407 |
{ "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */
|
| 408 |
{ "z", tZONE, HOUR( 0) }, /* Greenwich Mean */
|
| 409 |
{ "wet", tZONE, HOUR( 0) }, /* Western European */
|
| 410 |
{ "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
|
| 411 |
{ "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */
|
| 412 |
{ "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */
|
| 413 |
{ "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
|
| 414 |
{ "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
|
| 415 |
{ "est", tZONE, HOUR( 5) }, /* Eastern Standard */
|
| 416 |
{ "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
|
| 417 |
{ "cst", tZONE, HOUR( 6) }, /* Central Standard */
|
| 418 |
{ "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
|
| 419 |
{ "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
|
| 420 |
{ "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
|
| 421 |
{ "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
|
| 422 |
{ "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
|
| 423 |
{ "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
|
| 424 |
{ "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
|
| 425 |
{ "akst", tZONE, HOUR( 9) }, /* Alaska Standard */
|
| 426 |
{ "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */
|
| 427 |
{ "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
|
| 428 |
{ "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */
|
| 429 |
{ "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */
|
| 430 |
{ "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */
|
| 431 |
{ "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */
|
| 432 |
{ "mez", tZONE, -HOUR(1) }, /* Middle European */
|
| 433 |
{ "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
|
| 434 |
{ "cet", tZONE, -HOUR(1) }, /* Central European */
|
| 435 |
{ "met", tZONE, -HOUR(1) }, /* Middle European */
|
| 436 |
{ "eet", tZONE, -HOUR(2) }, /* Eastern Europe */
|
| 437 |
{ "msk", tZONE, -HOUR(3) }, /* Moscow Winter */
|
| 438 |
{ "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */
|
| 439 |
{ "wast", tZONE, -HOUR(8) }, /* West Australian Standard */
|
| 440 |
{ "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
|
| 441 |
{ "hkt", tZONE, -HOUR(8) }, /* Hong Kong */
|
| 442 |
{ "cct", tZONE, -HOUR(8) }, /* China Coast */
|
| 443 |
{ "jst", tZONE, -HOUR(9) }, /* Japan Standard */
|
| 444 |
{ "kst", tZONE, -HOUR(9) }, /* Korean Standard */
|
| 445 |
{ "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */
|
| 446 |
{ "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */
|
| 447 |
{ "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */
|
| 448 |
{ "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
|
| 449 |
{ "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
|
| 450 |
{ "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
|
| 451 |
{ "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
|
| 452 |
|
| 453 |
/* For completeness we include the following entries. */
|
| 454 |
#if 0
|
| 455 |
|
| 456 |
/* Duplicate names. Either they conflict with a zone listed above
|
| 457 |
* (which is either more likely to be seen or just been in circulation
|
| 458 |
* longer), or they conflict with another zone in this section and
|
| 459 |
* we could not reasonably choose one over the other. */
|
| 460 |
{ "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */
|
| 461 |
{ "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */
|
| 462 |
{ "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
|
| 463 |
{ "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */
|
| 464 |
{ "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */
|
| 465 |
{ "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */
|
| 466 |
{ "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */
|
| 467 |
{ "cst", tZONE, HOUR( 5) }, /* Chile Standard */
|
| 468 |
{ "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */
|
| 469 |
{ "ast", tZONE, HOUR( 5) }, /* Acre Standard */
|
| 470 |
{ "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */
|
| 471 |
{ "cst", tZONE, HOUR( 5) }, /* Cuba Standard */
|
| 472 |
{ "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */
|
| 473 |
{ "est", tZONE, HOUR( 6) }, /* Easter Island Standard */
|
| 474 |
{ "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */
|
| 475 |
{ "sst", tZONE, HOUR(11) }, /* Samoa Standard */
|
| 476 |
{ "ist", tZONE, -HOUR(2) }, /* Israel Standard */
|
| 477 |
{ "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */
|
| 478 |
{ "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */
|
| 479 |
{ "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */
|
| 480 |
{ "cst", tZONE, -HOUR(8) }, /* China Standard */
|
| 481 |
{ "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */
|
| 482 |
{ "sst", tZONE, -HOUR(8) }, /* Singapore Standard */
|
| 483 |
|
| 484 |
/* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
|
| 485 |
{ "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
|
| 486 |
{ "wat", tZONE, -HOUR(1) }, /* West Africa */
|
| 487 |
{ "at", tZONE, HOUR( 2) }, /* Azores */
|
| 488 |
{ "gst", tZONE, -HOUR(10) }, /* Guam Standard */
|
| 489 |
{ "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */
|
| 490 |
{ "idlw", tZONE, HOUR(12) }, /* International Date Line West */
|
| 491 |
{ "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
|
| 492 |
{ "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
|
| 493 |
{ "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
|
| 494 |
{ "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
|
| 495 |
{ "fwt", tZONE, -HOUR(1) }, /* French Winter */
|
| 496 |
{ "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
|
| 497 |
{ "bt", tZONE, -HOUR(3) }, /* Baghdad */
|
| 498 |
{ "it", tZONE, -(HOUR(3)+30) }, /* Iran */
|
| 499 |
{ "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
|
| 500 |
{ "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
|
| 501 |
{ "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */
|
| 502 |
{ "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
|
| 503 |
{ "nst", tZONE, -HOUR(7) }, /* North Sumatra */
|
| 504 |
{ "sst", tZONE, -HOUR(7) }, /* South Sumatra */
|
| 505 |
{ "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
|
| 506 |
{ "nzt", tZONE, -HOUR(12) }, /* New Zealand */
|
| 507 |
{ "idle", tZONE, -HOUR(12) }, /* International Date Line East */
|
| 508 |
{ "cat", tZONE, HOUR(10) }, /* -- expired 1967 */
|
| 509 |
{ "nt", tZONE, HOUR(11) }, /* -- expired 1967 */
|
| 510 |
{ "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */
|
| 511 |
{ "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */
|
| 512 |
#endif /* 0 */
|
| 513 |
};
|
| 514 |
|
| 515 |
|
| 516 |
|
| 517 |
static void
|
| 518 |
date_error(const char *s)
|
| 519 |
{
|
| 520 |
s = s; /* ARGSUSED */
|
| 521 |
/* NOTREACHED */
|
| 522 |
}
|
| 523 |
|
| 524 |
|
| 525 |
static time_t
|
| 526 |
ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
|
| 527 |
{
|
| 528 |
if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
|
| 529 |
return -1;
|
| 530 |
if (Meridian == MER24) {
|
| 531 |
if (Hours < 0 || Hours > 23)
|
| 532 |
return -1;
|
| 533 |
}
|
| 534 |
else {
|
| 535 |
if (Hours < 1 || Hours > 12)
|
| 536 |
return -1;
|
| 537 |
if (Hours == 12)
|
| 538 |
Hours = 0;
|
| 539 |
if (Meridian == MERpm)
|
| 540 |
Hours += 12;
|
| 541 |
}
|
| 542 |
return (Hours * 60L + Minutes) * 60L + Seconds;
|
| 543 |
}
|
| 544 |
|
| 545 |
|
| 546 |
static time_t
|
| 547 |
Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes,
|
| 548 |
time_t Seconds, MERIDIAN Meridian, DSTMODE dst)
|
| 549 |
{
|
| 550 |
static int DaysNormal[13] = {
|
| 551 |
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
| 552 |
};
|
| 553 |
static int DaysLeap[13] = {
|
| 554 |
0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
| 555 |
};
|
| 556 |
static int LeapYears[] = {
|
| 557 |
1972, 1976, 1980, 1984, 1988, 1992, 1996,
|
| 558 |
2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
|
| 559 |
};
|
| 560 |
int *yp;
|
| 561 |
int *mp;
|
| 562 |
time_t Julian;
|
| 563 |
int i;
|
| 564 |
time_t tod;
|
| 565 |
|
| 566 |
/* Year should not be passed as a relative value, but absolute one.
|
| 567 |
so this should not happen, but just ensure it */
|
| 568 |
if (Year < 0)
|
| 569 |
Year = -Year;
|
| 570 |
if (Year < 100) {
|
| 571 |
Year += 1900;
|
| 572 |
if (Year < EPOCH)
|
| 573 |
Year += 100;
|
| 574 |
}
|
| 575 |
for (mp = DaysNormal, yp = LeapYears; yp < ARRAY_END(LeapYears); yp++)
|
| 576 |
if (Year == *yp) {
|
| 577 |
mp = DaysLeap;
|
| 578 |
break;
|
| 579 |
}
|
| 580 |
if (Year < EPOCH || Year > END_OF_TIME
|
| 581 |
|| Month < 1 || Month > 12
|
| 582 |
/* NOSTRICT *//* conversion from long may lose accuracy */
|
| 583 |
|| Day < 1 || Day > mp[(int)Month])
|
| 584 |
return -1;
|
| 585 |
|
| 586 |
Julian = Day - 1 + (Year - EPOCH) * 365;
|
| 587 |
for (yp = LeapYears; yp < ARRAY_END(LeapYears); yp++, Julian++)
|
| 588 |
if (Year <= *yp)
|
| 589 |
break;
|
| 590 |
for (i = 1; i < Month; i++)
|
| 591 |
Julian += *++mp;
|
| 592 |
Julian *= SECSPERDAY;
|
| 593 |
Julian += yyTimezone * 60L;
|
| 594 |
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
|
| 595 |
return -1;
|
| 596 |
Julian += tod;
|
| 597 |
tod = Julian;
|
| 598 |
if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
|
| 599 |
Julian -= DST_OFFSET * 60 * 60;
|
| 600 |
return Julian;
|
| 601 |
}
|
| 602 |
|
| 603 |
|
| 604 |
static time_t
|
| 605 |
DSTcorrect(time_t Start, time_t Future)
|
| 606 |
{
|
| 607 |
time_t StartDay;
|
| 608 |
time_t FutureDay;
|
| 609 |
|
| 610 |
StartDay = (localtime(&Start)->tm_hour + 1) % 24;
|
| 611 |
FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
|
| 612 |
return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
|
| 613 |
}
|
| 614 |
|
| 615 |
|
| 616 |
static time_t
|
| 617 |
RelativeMonth(time_t Start, time_t RelMonth)
|
| 618 |
{
|
| 619 |
struct tm *tm;
|
| 620 |
time_t Month;
|
| 621 |
time_t Year;
|
| 622 |
|
| 623 |
tm = localtime(&Start);
|
| 624 |
Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
|
| 625 |
Year = Month / 12;
|
| 626 |
Year += 1900;
|
| 627 |
Month = Month % 12 + 1;
|
| 628 |
return DSTcorrect(Start,
|
| 629 |
Convert(Month, (time_t)tm->tm_mday, Year,
|
| 630 |
(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
|
| 631 |
MER24, DSTmaybe));
|
| 632 |
}
|
| 633 |
|
| 634 |
|
| 635 |
static int
|
| 636 |
LookupWord(char *buff, int length)
|
| 637 |
{
|
| 638 |
char *p;
|
| 639 |
const char *q;
|
| 640 |
TABLE *tp;
|
| 641 |
int c;
|
| 642 |
|
| 643 |
p = buff;
|
| 644 |
c = p[0];
|
| 645 |
|
| 646 |
/* See if we have an abbreviation for a month. */
|
| 647 |
if (length == 3 || (length == 4 && p[3] == '.'))
|
| 648 |
for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++) {
|
| 649 |
q = tp->name;
|
| 650 |
if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
|
| 651 |
yylval.Number = tp->value;
|
| 652 |
return tp->type;
|
| 653 |
}
|
| 654 |
}
|
| 655 |
else
|
| 656 |
for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++)
|
| 657 |
if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
|
| 658 |
yylval.Number = tp->value;
|
| 659 |
return tp->type;
|
| 660 |
}
|
| 661 |
|
| 662 |
/* Try for a timezone. */
|
| 663 |
for (tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++)
|
| 664 |
if (c == tp->name[0] && p[1] == tp->name[1]
|
| 665 |
&& strcmp(p, tp->name) == 0) {
|
| 666 |
yylval.Number = tp->value;
|
| 667 |
return tp->type;
|
| 668 |
}
|
| 669 |
|
| 670 |
/* Try the units table. */
|
| 671 |
for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++)
|
| 672 |
if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
|
| 673 |
yylval.Number = tp->value;
|
| 674 |
return tp->type;
|
| 675 |
}
|
| 676 |
|
| 677 |
/* Strip off any plural and try the units table again. */
|
| 678 |
if (--length > 0 && p[length] == 's') {
|
| 679 |
p[length] = '\0';
|
| 680 |
for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++)
|
| 681 |
if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
|
| 682 |
p[length] = 's';
|
| 683 |
yylval.Number = tp->value;
|
| 684 |
return tp->type;
|
| 685 |
}
|
| 686 |
p[length] = 's';
|
| 687 |
}
|
| 688 |
length++;
|
| 689 |
|
| 690 |
/* Drop out any periods. */
|
| 691 |
for (p = buff, q = buff; *q; q++)
|
| 692 |
if (*q != '.')
|
| 693 |
*p++ = *q;
|
| 694 |
*p = '\0';
|
| 695 |
|
| 696 |
/* Try the meridians. */
|
| 697 |
if (buff[1] == 'm' && buff[2] == '\0') {
|
| 698 |
if (buff[0] == 'a') {
|
| 699 |
yylval.Meridian = MERam;
|
| 700 |
return tMERIDIAN;
|
| 701 |
}
|
| 702 |
if (buff[0] == 'p') {
|
| 703 |
yylval.Meridian = MERpm;
|
| 704 |
return tMERIDIAN;
|
| 705 |
}
|
| 706 |
}
|
| 707 |
|
| 708 |
/* If we saw any periods, try the timezones again. */
|
| 709 |
if (p - buff != length) {
|
| 710 |
c = buff[0];
|
| 711 |
for (p = buff, tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++)
|
| 712 |
if (c == tp->name[0] && p[1] == tp->name[1]
|
| 713 |
&& strcmp(p, tp->name) == 0) {
|
| 714 |
yylval.Number = tp->value;
|
| 715 |
return tp->type;
|
| 716 |
}
|
| 717 |
}
|
| 718 |
|
| 719 |
/* Unknown word -- assume GMT timezone. */
|
| 720 |
yylval.Number = 0;
|
| 721 |
return tZONE;
|
| 722 |
}
|
| 723 |
|
| 724 |
|
| 725 |
static int
|
| 726 |
date_lex(void)
|
| 727 |
{
|
| 728 |
char c;
|
| 729 |
char *p;
|
| 730 |
char buff[20];
|
| 731 |
int sign;
|
| 732 |
int i;
|
| 733 |
int nesting;
|
| 734 |
|
| 735 |
for ( ; ; ) {
|
| 736 |
/* Get first character after the whitespace. */
|
| 737 |
for ( ; ; ) {
|
| 738 |
while (CTYPE(isspace, (int)*yyInput))
|
| 739 |
yyInput++;
|
| 740 |
c = *yyInput;
|
| 741 |
|
| 742 |
/* Ignore RFC 822 comments, typically time zone names. */
|
| 743 |
if (c != LPAREN)
|
| 744 |
break;
|
| 745 |
for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
|
| 746 |
if (c == LPAREN)
|
| 747 |
nesting++;
|
| 748 |
else if (!IS7BIT(c) || c == '\0' || c == '\r'
|
| 749 |
|| (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
|
| 750 |
/* Lexical error: bad comment. */
|
| 751 |
return '?';
|
| 752 |
yyInput++;
|
| 753 |
}
|
| 754 |
|
| 755 |
/* A number? */
|
| 756 |
if (CTYPE(isdigit, (int)c) || c == '-' || c == '+') {
|
| 757 |
if (c == '-' || c == '+') {
|
| 758 |
sign = c == '-' ? -1 : 1;
|
| 759 |
yyInput++;
|
| 760 |
if (!CTYPE(isdigit, (int)*yyInput))
|
| 761 |
/* Skip the plus or minus sign. */
|
| 762 |
continue;
|
| 763 |
}
|
| 764 |
else
|
| 765 |
sign = 0;
|
| 766 |
for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, (int)c); )
|
| 767 |
i = 10 * i + c - '0';
|
| 768 |
yyInput--;
|
| 769 |
yylval.Number = sign < 0 ? -i : i;
|
| 770 |
return sign ? tSNUMBER : tUNUMBER;
|
| 771 |
}
|
| 772 |
|
| 773 |
/* A word? */
|
| 774 |
if (CTYPE(isalpha, (int)c)) {
|
| 775 |
for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, (int)c); )
|
| 776 |
if (p < &buff[sizeof buff - 1])
|
| 777 |
*p++ = CTYPE(isupper, (int)c) ? tolower(c) : c;
|
| 778 |
*p = '\0';
|
| 779 |
yyInput--;
|
| 780 |
return LookupWord(buff, p - buff);
|
| 781 |
}
|
| 782 |
|
| 783 |
return *yyInput++;
|
| 784 |
}
|
| 785 |
}
|
| 786 |
|
| 787 |
|
| 788 |
time_t
|
| 789 |
parsedate(char *p)
|
| 790 |
{
|
| 791 |
time_t now;
|
| 792 |
struct tm *tm;
|
| 793 |
time_t Start;
|
| 794 |
|
| 795 |
now = time(NULL);
|
| 796 |
yyInput = p;
|
| 797 |
|
| 798 |
tm = gmtime(&now);
|
| 799 |
yyYear = tm->tm_year + 1900;
|
| 800 |
yyMonth = tm->tm_mon + 1;
|
| 801 |
yyDay = tm->tm_mday;
|
| 802 |
yyTimezone = 0;
|
| 803 |
yyDSTmode = DSTmaybe;
|
| 804 |
yyHour = 0;
|
| 805 |
yyMinutes = 0;
|
| 806 |
yySeconds = 0;
|
| 807 |
yyMeridian = MER24;
|
| 808 |
yyRelSeconds = 0;
|
| 809 |
yyRelMonth = 0;
|
| 810 |
yyHaveDate = 0;
|
| 811 |
yyHaveRel = 0;
|
| 812 |
yyHaveTime = 0;
|
| 813 |
|
| 814 |
if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
|
| 815 |
return -1;
|
| 816 |
|
| 817 |
if (yyHaveDate || yyHaveTime) {
|
| 818 |
Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
|
| 819 |
yyMeridian, yyDSTmode);
|
| 820 |
if (Start < 0)
|
| 821 |
return -1;
|
| 822 |
}
|
| 823 |
else {
|
| 824 |
Start = now;
|
| 825 |
if (!yyHaveRel)
|
| 826 |
Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
|
| 827 |
}
|
| 828 |
|
| 829 |
Start += yyRelSeconds;
|
| 830 |
if (yyRelMonth)
|
| 831 |
Start += RelativeMonth(Start, yyRelMonth);
|
| 832 |
|
| 833 |
/* Have to do *something* with a legitimate -1 so it's distinguishable
|
| 834 |
* from the error return value. (Alternately could set errno on error.) */
|
| 835 |
return Start == -1 ? 0 : Start;
|
| 836 |
}
|
| 837 |
|
| 838 |
|
| 839 |
#if defined(TEST)
|
| 840 |
|
| 841 |
#if YYDEBUG
|
| 842 |
extern int yydebug;
|
| 843 |
#endif /* YYDEBUG */
|
| 844 |
|
| 845 |
/* ARGSUSED */
|
| 846 |
int
|
| 847 |
main(int ac, char *av[])
|
| 848 |
{
|
| 849 |
char buff[128];
|
| 850 |
time_t d;
|
| 851 |
|
| 852 |
#if YYDEBUG
|
| 853 |
yydebug = 1;
|
| 854 |
#endif /* YYDEBUG */
|
| 855 |
|
| 856 |
printf("Enter date, or blank line to exit.\n\t> ");
|
| 857 |
for ( ; ; ) {
|
| 858 |
printf("\t> ");
|
| 859 |
fflush(stdout);
|
| 860 |
if (gets(buff) == NULL || buff[0] == '\n')
|
| 861 |
break;
|
| 862 |
#if YYDEBUG
|
| 863 |
if (strcmp(buff, "yydebug") == 0) {
|
| 864 |
yydebug = !yydebug;
|
| 865 |
printf("yydebug = %s\n", yydebug ? "on" : "off");
|
| 866 |
continue;
|
| 867 |
}
|
| 868 |
#endif /* YYDEBUG */
|
| 869 |
d = parsedate(buff);
|
| 870 |
if (d == -1)
|
| 871 |
printf("Bad format - couldn't convert.\n");
|
| 872 |
else
|
| 873 |
printf("%s", ctime(&d));
|
| 874 |
}
|
| 875 |
|
| 876 |
exit(0);
|
| 877 |
/* NOTREACHED */
|
| 878 |
}
|
| 879 |
#endif /* defined(TEST) */
|