📄 getdate.y
字号:
%{/* Parse a string into an internal time stamp. Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* Originally written by Steven M. Bellovin <smb@research.att.com> while at the University of North Carolina at Chapel Hill. Later tweaked by a couple of people on Usenet. Completely overhauled by Rich $alz <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do the right thing about local DST. Unlike previous versions, this version is reentrant. */#ifdef HAVE_CONFIG_H# include <config.h># ifdef HAVE_ALLOCA_H# include <alloca.h># endif#endif/* Since the code of getdate.y is not included in the Emacs executable itself, there is no need to #define static in this file. Even if the code were included in the Emacs executable, it probably wouldn't do any harm to #undef it here; this will only cause problems if we try to write to a static variable, which I don't think this code needs to do. */#ifdef emacs# undef static#endif#include <ctype.h>#if HAVE_STDLIB_H# include <stdlib.h> /* for `free'; used by Bison 1.27 */#endif#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)# define IN_CTYPE_DOMAIN(c) 1#else# define IN_CTYPE_DOMAIN(c) isascii (c)#endif#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char. - It's guaranteed to evaluate its argument exactly once. - It's typically faster. POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless it's important to use the locale's definition of `digit' even when the host does not conform to POSIX. */#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)#if STDC_HEADERS || HAVE_STRING_H# include <string.h>#endif#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__# define __attribute__(x)#endif#ifndef ATTRIBUTE_UNUSED# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))#endif#define EPOCH_YEAR 1970#define TM_YEAR_BASE 1900#define HOUR(x) ((x) * 60)/* An integer value, and the number of digits in its textual representation. */typedef struct{ int value; int digits;} textint;/* An entry in the lexical lookup table. */typedef struct{ char const *name; int type; int value;} table;/* Meridian: am, pm, or 24-hour style. */enum { MERam, MERpm, MER24 };/* Information passed to and from the parser. */typedef struct{ /* The input string remaining to be parsed. */ const char *input; /* N, if this is the Nth Tuesday. */ int day_ordinal; /* Day of week; Sunday is 0. */ int day_number; /* tm_isdst flag for the local zone. */ int local_isdst; /* Time zone, in minutes east of UTC. */ int time_zone; /* Style used for time. */ int meridian; /* Gregorian year, month, day, hour, minutes, and seconds. */ textint year; int month; int day; int hour; int minutes; int seconds; /* Relative year, month, day, hour, minutes, and seconds. */ int rel_year; int rel_month; int rel_day; int rel_hour; int rel_minutes; int rel_seconds; /* Counts of nonterminals of various flavors parsed so far. */ int dates_seen; int days_seen; int local_zones_seen; int rels_seen; int times_seen; int zones_seen; /* Table of local time zone abbrevations, terminated by a null entry. */ table local_time_zone_table[3];} parser_control;#define PC (* (parser_control *) parm)#define YYLEX_PARAM parm#define YYPARSE_PARAM parmstatic int yyerror ();static int yylex ();%}/* We want a reentrant parser. */%pure_parser/* This grammar has 13 shift/reduce conflicts. */%expect 13%union{ int intval; textint textintval;}%token tAGO tDST%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE%token <textintval> tSNUMBER tUNUMBER%type <intval> o_merid%%spec: /* empty */ | spec item ;item: time { PC.times_seen++; } | local_zone { PC.local_zones_seen++; } | zone { PC.zones_seen++; } | date { PC.dates_seen++; } | day { PC.days_seen++; } | rel { PC.rels_seen++; } | number ;time: tUNUMBER tMERIDIAN { PC.hour = $1.value; PC.minutes = 0; PC.seconds = 0; PC.meridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { PC.hour = $1.value; PC.minutes = $3.value; PC.seconds = 0; PC.meridian = $4; } | tUNUMBER ':' tUNUMBER tSNUMBER { PC.hour = $1.value; PC.minutes = $3.value; PC.meridian = MER24; PC.zones_seen++; PC.time_zone = $4.value % 100 + ($4.value / 100) * 60; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { PC.hour = $1.value; PC.minutes = $3.value; PC.seconds = $5.value; PC.meridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { PC.hour = $1.value; PC.minutes = $3.value; PC.seconds = $5.value; PC.meridian = MER24; PC.zones_seen++; PC.time_zone = $6.value % 100 + ($6.value / 100) * 60; } ;local_zone: tLOCAL_ZONE { PC.local_isdst = $1; } | tLOCAL_ZONE tDST { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; } ;zone: tZONE { PC.time_zone = $1; } | tDAYZONE { PC.time_zone = $1 + 60; } | tZONE tDST { PC.time_zone = $1 + 60; } ;day: tDAY { PC.day_ordinal = 1; PC.day_number = $1; } | tDAY ',' { PC.day_ordinal = 1; PC.day_number = $1; } | tUNUMBER tDAY { PC.day_ordinal = $1.value; PC.day_number = $2; } ;date: tUNUMBER '/' tUNUMBER { PC.month = $1.value; PC.day = $3.value; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { /* Interpret as YYYY/MM/DD if the first value has 4 or more digits, otherwise as MM/DD/YY. The goal in recognizing YYYY/MM/DD is solely to support legacy machine-generated dates like those in an RCS log listing. If you want portability, use the ISO 8601 format. */ if (4 <= $1.digits) { PC.year = $1; PC.month = $3.value; PC.day = $5.value; } else { PC.month = $1.value; PC.day = $3.value; PC.year = $5; } } | tUNUMBER tSNUMBER tSNUMBER { /* ISO 8601 format. YYYY-MM-DD. */ PC.year = $1; PC.month = -$2.value; PC.day = -$3.value; } | tUNUMBER tMONTH tSNUMBER { /* e.g. 17-JUN-1992. */ PC.day = $1.value; PC.month = $2; PC.year.value = -$3.value; PC.year.digits = $3.digits; } | tMONTH tUNUMBER { PC.month = $1; PC.day = $2.value; } | tMONTH tUNUMBER ',' tUNUMBER { PC.month = $1; PC.day = $2.value; PC.year = $4; } | tUNUMBER tMONTH { PC.day = $1.value; PC.month = $2; } | tUNUMBER tMONTH tUNUMBER { PC.day = $1.value; PC.month = $2; PC.year = $3; } ;rel: relunit tAGO { PC.rel_seconds = -PC.rel_seconds; PC.rel_minutes = -PC.rel_minutes; PC.rel_hour = -PC.rel_hour; PC.rel_day = -PC.rel_day; PC.rel_month = -PC.rel_month; PC.rel_year = -PC.rel_year; } | relunit ;relunit: tUNUMBER tYEAR_UNIT { PC.rel_year += $1.value * $2; } | tSNUMBER tYEAR_UNIT { PC.rel_year += $1.value * $2; } | tYEAR_UNIT { PC.rel_year += $1; } | tUNUMBER tMONTH_UNIT { PC.rel_month += $1.value * $2; } | tSNUMBER tMONTH_UNIT { PC.rel_month += $1.value * $2; } | tMONTH_UNIT { PC.rel_month += $1; } | tUNUMBER tDAY_UNIT { PC.rel_day += $1.value * $2; } | tSNUMBER tDAY_UNIT { PC.rel_day += $1.value * $2; } | tDAY_UNIT { PC.rel_day += $1; } | tUNUMBER tHOUR_UNIT { PC.rel_hour += $1.value * $2; } | tSNUMBER tHOUR_UNIT { PC.rel_hour += $1.value * $2; } | tHOUR_UNIT { PC.rel_hour += $1; } | tUNUMBER tMINUTE_UNIT { PC.rel_minutes += $1.value * $2; } | tSNUMBER tMINUTE_UNIT { PC.rel_minutes += $1.value * $2; } | tMINUTE_UNIT { PC.rel_minutes += $1; } | tUNUMBER tSEC_UNIT { PC.rel_seconds += $1.value * $2; } | tSNUMBER tSEC_UNIT { PC.rel_seconds += $1.value * $2; } | tSEC_UNIT { PC.rel_seconds += $1; } ;number: tUNUMBER { if (PC.dates_seen && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits)) PC.year = $1; else { if (4 < $1.digits) { PC.dates_seen++; PC.day = $1.value % 100; PC.month = ($1.value / 100) % 100; PC.year.value = $1.value / 10000; PC.year.digits = $1.digits - 4; } else { PC.times_seen++; if ($1.digits <= 2) { PC.hour = $1.value; PC.minutes = 0; } else { PC.hour = $1.value / 100; PC.minutes = $1.value % 100; } PC.seconds = 0; PC.meridian = MER24; } } } ;o_merid: /* empty */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ;%%/* Include this file down here because bison inserts code above which may define-away `const'. We want the prototype for get_date to have the same signature as the function definition. */#include "getdate.h"#include "unlocked-io.h"#ifndef gmtimestruct tm *gmtime ();#endif#ifndef localtimestruct tm *localtime ();#endif#ifndef mktimetime_t mktime ();#endifstatic table const meridian_table[] ={ { "AM", tMERIDIAN, MERam }, { "A.M.", tMERIDIAN, MERam }, { "PM", tMERIDIAN, MERpm }, { "P.M.", tMERIDIAN, MERpm }, { 0, 0, 0 }};static table const dst_table[] ={ { "DST", tDST, 0 }};static table const month_and_day_table[] ={ { "JANUARY", tMONTH, 1 }, { "FEBRUARY", tMONTH, 2 }, { "MARCH", tMONTH, 3 }, { "APRIL", tMONTH, 4 }, { "MAY", tMONTH, 5 }, { "JUNE", tMONTH, 6 }, { "JULY", tMONTH, 7 }, { "AUGUST", tMONTH, 8 }, { "SEPTEMBER",tMONTH, 9 }, { "SEPT", tMONTH, 9 }, { "OCTOBER", tMONTH, 10 }, { "NOVEMBER", tMONTH, 11 }, { "DECEMBER", tMONTH, 12 }, { "SUNDAY", tDAY, 0 }, { "MONDAY", tDAY, 1 }, { "TUESDAY", tDAY, 2 }, { "TUES", tDAY, 2 }, { "WEDNESDAY",tDAY, 3 }, { "WEDNES", tDAY, 3 }, { "THURSDAY", tDAY, 4 }, { "THUR", tDAY, 4 }, { "THURS", tDAY, 4 }, { "FRIDAY", tDAY, 5 }, { "SATURDAY", tDAY, 6 }, { 0, 0, 0 }};static table const time_units_table[] ={ { "YEAR", tYEAR_UNIT, 1 }, { "MONTH", tMONTH_UNIT, 1 }, { "FORTNIGHT",tDAY_UNIT, 14 }, { "WEEK", tDAY_UNIT, 7 }, { "DAY", tDAY_UNIT, 1 }, { "HOUR", tHOUR_UNIT, 1 }, { "MINUTE", tMINUTE_UNIT, 1 }, { "MIN", tMINUTE_UNIT, 1 }, { "SECOND", tSEC_UNIT, 1 }, { "SEC", tSEC_UNIT, 1 }, { 0, 0, 0 }};/* Assorted relative-time words. */static table const relative_time_table[] ={ { "TOMORROW", tMINUTE_UNIT, 24 * 60 }, { "YESTERDAY",tMINUTE_UNIT, - (24 * 60) }, { "TODAY", tMINUTE_UNIT, 0 }, { "NOW", tMINUTE_UNIT, 0 }, { "LAST", tUNUMBER, -1 }, { "THIS", tUNUMBER, 0 }, { "NEXT", tUNUMBER, 1 }, { "FIRST", tUNUMBER, 1 },/*{ "SECOND", tUNUMBER, 2 }, */ { "THIRD", tUNUMBER, 3 }, { "FOURTH", tUNUMBER, 4 }, { "FIFTH", tUNUMBER, 5 }, { "SIXTH", tUNUMBER, 6 }, { "SEVENTH", tUNUMBER, 7 }, { "EIGHTH", tUNUMBER, 8 }, { "NINTH", tUNUMBER, 9 }, { "TENTH", tUNUMBER, 10 }, { "ELEVENTH", tUNUMBER, 11 }, { "TWELFTH", tUNUMBER, 12 }, { "AGO", tAGO, 1 }, { 0, 0, 0 }};/* The time zone table. This table is necessarily incomplete, as time zone abbreviations are ambiguous; e.g. Australians interpret "EST" as Eastern time in Australia, not as US Eastern Standard Time. You cannot rely on getdate to handle arbitrary time zone abbreviations; use numeric abbreviations like `-0500' instead. */static table const time_zone_table[] ={ { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */ { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ { "UTC", tZONE, HOUR ( 0) }, { "WET", tZONE, HOUR ( 0) }, /* Western European */ { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */ { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -