You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by mark benedetto king <mb...@lowlatency.com> on 2003/12/21 05:42:24 UTC

RFC: date parser strawman

Here's a quick patch for review.

Remove the getdate.y-based date-parser, replacing it with a simpler
though less powerful one.

* subversion/libsvn_subr/date.c: New file.
* subversion/libsvn_subr/getdate.y: Deleted.
* subversion/libsvn_subr/getdate.cw: Deleted.
* subversion/include/svn_time.h:
  (struct getdate_time): Removed.
  (svn_parse_date): Update prototype and documentation comment to reflect
   the new interface.
* subversion/libsvn_subr/opt.c
  (parse_one_rev): Use the new interface.
* autogen.sh:
  Remove generation of getdate.c from getdate.y.


Index: subversion/include/svn_time.h
===================================================================
--- subversion/include/svn_time.h	(revision 8041)
+++ subversion/include/svn_time.h	(working copy)
@@ -50,17 +50,13 @@
 const char *svn_time_to_human_cstring (apr_time_t when, apr_pool_t *pool);
 
 
-/** Needed by @c getdate.y parser. */
-struct getdate_time {
-  time_t time;
-  short timezone;
-};
-
-/** The one interface in our @c getdate.y parser; convert
- * human-readable date @a text into a standard C @c time_t.  The 2nd
- * argument is unused; we always pass @c NULL.
+/** Convert human-readable date @a text into an @c apr_time_t, using
+ * @a now as the current time and @a gmtoff as an indicator of
+ * seconds east of GMT, storing the result in @a val.  Return
+ * TRUE iff the date is parsed successfully.
  */
-time_t svn_parse_date (char *text, struct getdate_time *now);
+svn_boolean_t svn_parse_date (const char *text, apr_time_t now,
+                              apr_int32_t gmtoff, apr_time_t *val);
 
 
 /** Sleep until the next second, to ensure that any files modified
Index: subversion/libsvn_subr/opt.c
===================================================================
--- subversion/libsvn_subr/opt.c	(revision 8041)
+++ subversion/libsvn_subr/opt.c	(working copy)
@@ -292,21 +292,25 @@
 static char *parse_one_rev (svn_opt_revision_t *revision, char *str)
 {
   char *end, save;
-  time_t tm;
+  apr_time_t tm;
 
   if (*str == '{')
     {
+      apr_time_t now;
+      apr_time_exp_t nowexp;
       /* Brackets denote a date. */
       str++;
       end = strchr (str, '}');
       if (!end)
         return NULL;
       *end = '\0';
-      tm = svn_parse_date (str, NULL);
-      if (tm == -1)
+      now = apr_time_now ();
+      if (apr_time_exp_lt (&nowexp, now) != APR_SUCCESS)
         return NULL;
+      if (!svn_parse_date (str, now, nowexp.tm_gmtoff, &tm))
+        return NULL;
       revision->kind = svn_opt_revision_date;
-      apr_time_ansi_put (&(revision->value.date), tm);
+      revision->value.date = tm;
       return end + 1;
     }
   else if (apr_isdigit (*str))
Index: subversion/libsvn_subr/getdate.y
===================================================================
--- subversion/libsvn_subr/getdate.y	(revision 8041)
+++ subversion/libsvn_subr/getdate.y	(working copy)
@@ -1,996 +0,0 @@
-%{
-/*
-**  Originally written by Steven M. Bellovin <sm...@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
-**  <rs...@bbn.com> and Jim Berets <jb...@bbn.com> in August, 1990;
-**
-**  This grammar has 10 shift/reduce conflicts.
-**
-**  This code is in the public domain and has no copyright.
-**
-**    July 3rd, 2001:  added to Subversion project and slightly 
-**                     tweaked by Ben Collins-Sussman <su...@collab.net>
-*/
-/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
-/* SUPPRESS 288 on yyerrlab *//* Label unused */
-
-#ifdef HAVE_CONFIG_H
-#if defined (emacs) || defined (CONFIG_BROKETS)
-#include <config.h>
-#else
-#include "config.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 <stdio.h>
-#include <ctype.h>
-
-/* The code at the top of svn_parse_date which figures out the offset of the
-   current time zone checks various CPP symbols to see if special
-   tricks are need, but defaults to using the gettimeofday system call.
-   Include <sys/time.h> if that will be used.  */
-
-#if	defined(vms)
-#include <types.h>
-#else /* defined(vms) */
-#include <sys/types.h>
-#include <time.h>
-#include "svn_time.h"
-#endif	/* !defined(vms) */
-
-#include <string.h>
-
-
-/* Some old versions of bison generate parsers that use bcopy.
-   That loses on systems that don't provide the function, so we have
-   to redefine it here.  */
-#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
-#define bcopy(from, to, len) memcpy ((to), (from), (len))
-#endif
-
-#if defined (STDC_HEADERS)
-#include <stdlib.h>
-#endif
-
-/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
-   releases):
-
-   We don't want to mess with all the portability hassles of alloca.
-   In particular, most (all?) versions of bison will use alloca in
-   their parser.  If bison works on your system (e.g. it should work
-   with gcc), then go ahead and use it, but the more general solution
-   is to use byacc instead of bison, which should generate a portable
-   parser.  I played with adding "#define alloca dont_use_alloca", to
-   give an error if the parser generator uses alloca (and thus detect
-   unportable getdate.c's), but that seems to cause as many problems
-   as it solves.  */
-
-/* ### if these prototypes don't match the system's prototypes all
- * this will break. */
-extern struct tm	*gmtime(const time_t *t);
-extern struct tm	*localtime(const time_t *t);
-
-#define yyparse getdate_yyparse
-#define yylex getdate_yylex
-#define yyerror getdate_yyerror
-
-static int yyparse (void);
-static int yylex (void);
-static int yyerror (const char *s);
-
-#define EPOCH		1970
-#define HOUR(x)		((time_t)(x) * 60)
-#define SECSPERDAY	(24L * 60L * 60L)
-
-
-/*
-**  An entry in the lexical lookup table.
-*/
-typedef struct _TABLE {
-    const char	*name;
-    int		type;
-    time_t	value;
-} TABLE;
-
-
-/*
-**  Daylight-savings mode:  on, off, or not yet known.
-*/
-typedef enum _DSTMODE {
-    DSTon, DSToff, DSTmaybe
-} DSTMODE;
-
-/*
-**  Meridian:  am, pm, or 24-hour style.
-*/
-typedef enum _MERIDIAN {
-    MERam, MERpm, MER24
-} MERIDIAN;
-
-
-/*
-**  Global variables.  We could get rid of most of these by using a good
-**  union as the yacc stack.  (This routine was originally written before
-**  yacc had the %union construct.)  Maybe someday; right now we only use
-**  the %union very rarely.
-*/
-static char	*yyInput;
-static DSTMODE	yyDSTmode;
-static time_t	yyDayOrdinal;
-static time_t	yyDayNumber;
-static int	yyHaveDate;
-static int	yyHaveDay;
-static int	yyHaveRel;
-static int	yyHaveTime;
-static int	yyHaveZone;
-static time_t	yyTimezone;
-static time_t	yyDay;
-static time_t	yyHour;
-static time_t	yyMinutes;
-static time_t	yyMonth;
-static time_t	yySeconds;
-static time_t	yyYear;
-static MERIDIAN	yyMeridian;
-static time_t	yyRelMonth;
-static time_t	yyRelSeconds;
-
-%}
-
-%union {
-    time_t		Number;
-    enum _MERIDIAN	Meridian;
-}
-
-%token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
-%token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
-
-%type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
-%type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
-%type	<Meridian>	tMERIDIAN o_merid
-
-%%
-
-spec	: /* NULL */
-	| spec item
-	;
-
-item	: time {
-	    yyHaveTime++;
-	}
-	| zone {
-	    yyHaveZone++;
-	}
-	| date {
-	    yyHaveDate++;
-	}
-	| day {
-	    yyHaveDay++;
-	}
-	| rel {
-	    yyHaveRel++;
-	}
-	| number
-	;
-
-time	: tUNUMBER tMERIDIAN {
-	    yyHour = $1;
-	    yyMinutes = 0;
-	    yySeconds = 0;
-	    yyMeridian = $2;
-	}
-	| tUNUMBER ':' tUNUMBER o_merid {
-	    yyHour = $1;
-	    yyMinutes = $3;
-	    yySeconds = 0;
-	    yyMeridian = $4;
-	}
-	| tUNUMBER ':' tUNUMBER tSNUMBER {
-	    yyHour = $1;
-	    yyMinutes = $3;
-	    yyMeridian = MER24;
-	    yyDSTmode = DSToff;
-	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
-	}
-	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
-	    yyHour = $1;
-	    yyMinutes = $3;
-	    yySeconds = $5;
-	    yyMeridian = $6;
-	}
-	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
-	    yyHour = $1;
-	    yyMinutes = $3;
-	    yySeconds = $5;
-	    yyMeridian = MER24;
-	    yyDSTmode = DSToff;
-	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
-	}
-	;
-
-zone	: tZONE {
-	    yyTimezone = $1;
-	    yyDSTmode = DSToff;
-	}
-	| tDAYZONE {
-	    yyTimezone = $1;
-	    yyDSTmode = DSTon;
-	}
-	|
-	  tZONE tDST {
-	    yyTimezone = $1;
-	    yyDSTmode = DSTon;
-	}
-	;
-
-day	: tDAY {
-	    yyDayOrdinal = 1;
-	    yyDayNumber = $1;
-	}
-	| tDAY ',' {
-	    yyDayOrdinal = 1;
-	    yyDayNumber = $1;
-	}
-	| tUNUMBER tDAY {
-	    yyDayOrdinal = $1;
-	    yyDayNumber = $2;
-	}
-	;
-
-date	: tUNUMBER '/' tUNUMBER {
-	    yyMonth = $1;
-	    yyDay = $3;
-	}
-	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
-	    if ($1 >= 100) {
-		yyYear = $1;
-		yyMonth = $3;
-		yyDay = $5;
-	    } else {
-		yyMonth = $1;
-		yyDay = $3;
-		yyYear = $5;
-	    }
-	}
-	| tUNUMBER tSNUMBER tSNUMBER {
-	    /* ISO 8601 format.  yyyy-mm-dd.  */
-	    yyYear = $1;
-	    yyMonth = -$2;
-	    yyDay = -$3;
-	}
-	| tUNUMBER tMONTH tSNUMBER {
-	    /* e.g. 17-JUN-1992.  */
-	    yyDay = $1;
-	    yyMonth = $2;
-	    yyYear = -$3;
-	}
-	| tMONTH tUNUMBER {
-	    yyMonth = $1;
-	    yyDay = $2;
-	}
-	| tMONTH tUNUMBER ',' tUNUMBER {
-	    yyMonth = $1;
-	    yyDay = $2;
-	    yyYear = $4;
-	}
-	| tUNUMBER tMONTH {
-	    yyMonth = $2;
-	    yyDay = $1;
-	}
-	| tUNUMBER tMONTH tUNUMBER {
-	    yyMonth = $2;
-	    yyDay = $1;
-	    yyYear = $3;
-	}
-	;
-
-rel	: relunit tAGO {
-	    yyRelSeconds = -yyRelSeconds;
-	    yyRelMonth = -yyRelMonth;
-	}
-	| relunit
-	;
-
-relunit	: tUNUMBER tMINUTE_UNIT {
-	    yyRelSeconds += $1 * $2 * 60L;
-	}
-	| tSNUMBER tMINUTE_UNIT {
-	    yyRelSeconds += $1 * $2 * 60L;
-	}
-	| tMINUTE_UNIT {
-	    yyRelSeconds += $1 * 60L;
-	}
-	| tSNUMBER tSEC_UNIT {
-	    yyRelSeconds += $1;
-	}
-	| tUNUMBER tSEC_UNIT {
-	    yyRelSeconds += $1;
-	}
-	| tSEC_UNIT {
-	    yyRelSeconds++;
-	}
-	| tSNUMBER tMONTH_UNIT {
-	    yyRelMonth += $1 * $2;
-	}
-	| tUNUMBER tMONTH_UNIT {
-	    yyRelMonth += $1 * $2;
-	}
-	| tMONTH_UNIT {
-	    yyRelMonth += $1;
-	}
-	;
-
-number	: tUNUMBER {
-	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
-		yyYear = $1;
-	    else {
-		if($1>10000) {
-		    yyHaveDate++;
-		    yyDay= ($1)%100;
-		    yyMonth= ($1/100)%100;
-		    yyYear = $1/10000;
-		}
-		else {
-		    yyHaveTime++;
-		    if ($1 < 100) {
-			yyHour = $1;
-			yyMinutes = 0;
-		    }
-		    else {
-		    	yyHour = $1 / 100;
-		    	yyMinutes = $1 % 100;
-		    }
-		    yySeconds = 0;
-		    yyMeridian = MER24;
-	        }
-	    }
-	}
-	;
-
-o_merid	: /* NULL */ {
-	    $$ = MER24;
-	}
-	| tMERIDIAN {
-	    $$ = $1;
-	}
-	;
-
-%%
-
-/* Month and day table. */
-static TABLE const MonthDayTable[] = {
-    { "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 },
-    { NULL }
-};
-
-/* Time units table. */
-static TABLE const UnitsTable[] = {
-    { "year",		tMONTH_UNIT,	12 },
-    { "month",		tMONTH_UNIT,	1 },
-    { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
-    { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
-    { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
-    { "hour",		tMINUTE_UNIT,	60 },
-    { "minute",		tMINUTE_UNIT,	1 },
-    { "min",		tMINUTE_UNIT,	1 },
-    { "second",		tSEC_UNIT,	1 },
-    { "sec",		tSEC_UNIT,	1 },
-    { NULL }
-};
-
-/* Assorted relative-time words. */
-static TABLE const OtherTable[] = {
-    { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
-    { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
-    { "today",		tMINUTE_UNIT,	0 },
-    { "now",		tMINUTE_UNIT,	0 },
-    { "last",		tUNUMBER,	-1 },
-    { "this",		tMINUTE_UNIT,	0 },
-    { "next",		tUNUMBER,	2 },
-    { "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 },
-    { NULL }
-};
-
-/* The timezone table. */
-/* Some of these are commented out because a time_t can't store a float. */
-static TABLE const TimezoneTable[] = {
-    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
-    { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
-    { "utc",	tZONE,     HOUR( 0) },
-    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
-    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
-    { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
-    { "at",	tZONE,     HOUR( 2) },	/* Azores */
-#if	0
-    /* For completeness.  BST is also British Summer, and GST is
-     * also Guam Standard. */
-    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
-    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
-#endif
-#if 0
-    { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
-    { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
-    { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
-#endif
-    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
-    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
-    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
-    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
-    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
-    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
-    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
-    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
-    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
-    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
-    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
-    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
-    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
-    { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
-    { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
-    { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
-    { "nt",	tZONE,     HOUR(11) },	/* Nome */
-    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
-    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
-    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
-    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
-    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
-    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
-    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
-    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
-    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
-    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
-    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
-#if 0
-    { "it",	tZONE,     -HOUR(3.5) },/* Iran */
-#endif
-    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
-    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
-#if 0
-    { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
-#endif
-    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
-#if	0
-    /* For completeness.  NST is also Newfoundland Stanard, and SST is
-     * also Swedish Summer. */
-    { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
-    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
-#endif	/* 0 */
-    { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
-    { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
-#if 0
-    { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
-#endif
-    { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
-    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
-#if 0
-    { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
-    { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
-#endif
-    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
-    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
-    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
-    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
-    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
-    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
-    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
-    {  NULL  }
-};
-
-/* Military timezone table. */
-static TABLE const MilitaryTable[] = {
-    { "a",	tZONE,	HOUR(  1) },
-    { "b",	tZONE,	HOUR(  2) },
-    { "c",	tZONE,	HOUR(  3) },
-    { "d",	tZONE,	HOUR(  4) },
-    { "e",	tZONE,	HOUR(  5) },
-    { "f",	tZONE,	HOUR(  6) },
-    { "g",	tZONE,	HOUR(  7) },
-    { "h",	tZONE,	HOUR(  8) },
-    { "i",	tZONE,	HOUR(  9) },
-    { "k",	tZONE,	HOUR( 10) },
-    { "l",	tZONE,	HOUR( 11) },
-    { "m",	tZONE,	HOUR( 12) },
-    { "n",	tZONE,	HOUR(- 1) },
-    { "o",	tZONE,	HOUR(- 2) },
-    { "p",	tZONE,	HOUR(- 3) },
-    { "q",	tZONE,	HOUR(- 4) },
-    { "r",	tZONE,	HOUR(- 5) },
-    { "s",	tZONE,	HOUR(- 6) },
-    { "t",	tZONE,	HOUR(- 7) },
-    { "u",	tZONE,	HOUR(- 8) },
-    { "v",	tZONE,	HOUR(- 9) },
-    { "w",	tZONE,	HOUR(-10) },
-    { "x",	tZONE,	HOUR(-11) },
-    { "y",	tZONE,	HOUR(-12) },
-    { "z",	tZONE,	HOUR(  0) },
-    { NULL }
-};
-
-
-
-
-/* ARGSUSED */
-static int
-yyerror(s)
-    const char	*s;
-{
-  return 0;
-}
-
-
-static time_t
-ToSeconds(time_t Hours,
-          time_t Minutes,
-          time_t Seconds,
-          MERIDIAN Meridian)
-{
-    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
-	return -1;
-    switch (Meridian) {
-    case MER24:
-	if (Hours < 0 || Hours > 23)
-	    return -1;
-	return (Hours * 60L + Minutes) * 60L + Seconds;
-    case MERam:
-	if (Hours < 1 || Hours > 12)
-	    return -1;
-	if (Hours == 12)
-	    Hours = 0;
-	return (Hours * 60L + Minutes) * 60L + Seconds;
-    case MERpm:
-	if (Hours < 1 || Hours > 12)
-	    return -1;
-	if (Hours == 12)
-	    Hours = 0;
-	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
-    default:
-	abort ();
-    }
-    /* NOTREACHED */
-}
-
-
-/* Year is either
-   * A negative number, which means to use its absolute value (why?)
-   * A number from 0 to 99, which means a year from 1900 to 1999, or
-   * The actual year (>=100).  */
-static time_t
-Convert (time_t Month,
-         time_t Day,
-         time_t Year,
-         time_t Hours,
-         time_t Minutes,
-         time_t Seconds,
-         MERIDIAN Meridian,
-         DSTMODE DSTmode)
-{
-    static int DaysInMonth[12] = {
-	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-    };
-    time_t	tod;
-    time_t	Julian;
-    int		i;
-
-    if (Year < 0)
-	Year = -Year;
-    if (Year < 69)
-	Year += 2000;
-    else if (Year < 100)
-	Year += 1900;
-    DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
-		    ? 29 : 28;
-    /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
-       I'm too lazy to try to check for time_t overflow in another way.  */
-    if (Year < EPOCH || Year > 2038
-     || Month < 1 || Month > 12
-     /* Lint fluff:  "conversion from long may lose accuracy" */
-     || Day < 1 || Day > DaysInMonth[(int)--Month])
-	return -1;
-
-    for (Julian = Day - 1, i = 0; i < Month; i++)
-	Julian += DaysInMonth[i];
-    for (i = EPOCH; i < Year; i++)
-	Julian += 365 + (i % 4 == 0);
-    Julian *= SECSPERDAY;
-    Julian += yyTimezone * 60L;
-    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
-	return -1;
-    Julian += tod;
-    if (DSTmode == DSTon
-     || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
-	Julian -= 60 * 60;
-    return Julian;
-}
-
-
-static time_t
-DSTcorrect(time_t Start, time_t Future)
-{
-    time_t	StartDay;
-    time_t	FutureDay;
-
-    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
-    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
-    return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
-}
-
-
-static time_t
-RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
-{
-    struct tm	*tm;
-    time_t	now;
-
-    now = Start;
-    tm = localtime(&now);
-    now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
-    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
-    return DSTcorrect(Start, now);
-}
-
-
-static time_t
-RelativeMonth(time_t Start, time_t RelMonth)
-{
-    struct tm	*tm;
-    time_t	Month;
-    time_t	Year;
-
-    if (RelMonth == 0)
-	return 0;
-    tm = localtime(&Start);
-    Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
-    Year = Month / 12;
-    Month = Month % 12 + 1;
-    return DSTcorrect(Start,
-	    Convert(Month, (time_t)tm->tm_mday, Year,
-		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
-		MER24, DSTmaybe));
-}
-
-
-static int
-LookupWord(char *buff)
-{
-    register char	*p;
-    register char	*q;
-    register const TABLE	*tp;
-    int			i;
-    int			abbrev;
-
-    /* Make it lowercase. */
-    for (p = buff; *p; p++)
-	if (isupper(*p))
-	    *p = tolower(*p);
-
-    if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
-	yylval.Meridian = MERam;
-	return tMERIDIAN;
-    }
-    if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
-	yylval.Meridian = MERpm;
-	return tMERIDIAN;
-    }
-
-    /* See if we have an abbreviation for a month. */
-    if (strlen(buff) == 3)
-	abbrev = 1;
-    else if (strlen(buff) == 4 && buff[3] == '.') {
-	abbrev = 1;
-	buff[3] = '\0';
-    }
-    else
-	abbrev = 0;
-
-    for (tp = MonthDayTable; tp->name; tp++) {
-	if (abbrev) {
-	    if (strncmp(buff, tp->name, 3) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-	}
-	else if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-    }
-
-    for (tp = TimezoneTable; tp->name; tp++)
-	if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-
-    if (strcmp(buff, "dst") == 0) 
-	return tDST;
-
-    for (tp = UnitsTable; tp->name; tp++)
-	if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-
-    /* Strip off any plural and try the units table again. */
-    i = strlen(buff) - 1;
-    if (buff[i] == 's') {
-	buff[i] = '\0';
-	for (tp = UnitsTable; tp->name; tp++)
-	    if (strcmp(buff, tp->name) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-	buff[i] = 's';		/* Put back for "this" in OtherTable. */
-    }
-
-    for (tp = OtherTable; tp->name; tp++)
-	if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-
-    /* Military timezones. */
-    if (buff[1] == '\0' && isalpha(*buff)) {
-	for (tp = MilitaryTable; tp->name; tp++)
-	    if (strcmp(buff, tp->name) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-    }
-
-    /* Drop out any periods and try the timezone table again. */
-    for (i = 0, p = q = buff; *q; q++)
-	if (*q != '.')
-	    *p++ = *q;
-	else
-	    i++;
-    *p = '\0';
-    if (i)
-	for (tp = TimezoneTable; tp->name; tp++)
-	    if (strcmp(buff, tp->name) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-
-    return tID;
-}
-
-
-static int
-yylex()
-{
-    register char	c;
-    register char	*p;
-    char		buff[20];
-    int			Count;
-    int			sign;
-
-    for ( ; ; ) {
-	while (isspace(*yyInput))
-	    yyInput++;
-
-	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
-	    if (c == '-' || c == '+') {
-		sign = c == '-' ? -1 : 1;
-		if (!isdigit(*++yyInput))
-		    /* skip the '-' sign */
-		    continue;
-	    }
-	    else
-		sign = 0;
-	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
-		yylval.Number = 10 * yylval.Number + c - '0';
-	    yyInput--;
-	    if (sign < 0)
-		yylval.Number = -yylval.Number;
-	    return sign ? tSNUMBER : tUNUMBER;
-	}
-	if (isalpha(c)) {
-	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
-		if (p < &buff[sizeof buff - 1])
-		    *p++ = c;
-	    *p = '\0';
-	    yyInput--;
-	    return LookupWord(buff);
-	}
-	if (c != '(')
-	    return *yyInput++;
-	Count = 0;
-	do {
-	    c = *yyInput++;
-	    if (c == '\0')
-		return c;
-	    if (c == '(')
-		Count++;
-	    else if (c == ')')
-		Count--;
-	} while (Count > 0);
-    }
-}
-
-#define TM_YEAR_ORIGIN 1900
-
-/* Yield A - B, measured in seconds.  */
-static long
-difftm (struct tm *a, struct tm *b)
-{
-  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
-  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
-  int days = (
-	      /* difference in day of year */
-	      a->tm_yday - b->tm_yday
-	      /* + intervening leap days */
-	      +  ((ay >> 2) - (by >> 2))
-	      -  (ay/100 - by/100)
-	      +  ((ay/100 >> 2) - (by/100 >> 2))
-	      /* + difference in years * 365 */
-	      +  (long)(ay-by) * 365
-	      );
-  return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
-	      + (a->tm_min - b->tm_min))
-	  + (a->tm_sec - b->tm_sec));
-}
-
-time_t
-svn_parse_date(char *p, struct getdate_time *now)
-{
-    struct tm		*tm, gmt;
-    struct getdate_time	ftz;
-    time_t		Start;
-    time_t		tod;
-    time_t nowtime;
-
-    yyInput = p;
-    if (now == NULL) {
-	struct tm *gmt_ptr;
-
-        now = &ftz;
-	(void)time (&nowtime);
-
-	gmt_ptr = gmtime (&nowtime);
-	if (gmt_ptr != NULL)
-	{
-	    /* Make a copy, in case localtime modifies *tm (I think
-	       that comment now applies to *gmt_ptr, but I am too
-	       lazy to dig into how gmtime and locatime allocate the
-	       structures they return pointers to).  */
-	    gmt = *gmt_ptr;
-	}
-
-	if (! (tm = localtime (&nowtime)))
-	    return -1;
-
-	if (gmt_ptr != NULL)
-	    ftz.timezone = difftm (&gmt, tm) / 60;
-	else
-	    /* We are on a system like VMS, where the system clock is
-	       in local time and the system has no concept of timezones.
-	       Hopefully we can fake this out (for the case in which the
-	       user specifies no timezone) by just saying the timezone
-	       is zero.  */
-	    ftz.timezone = 0;
-
-	if(tm->tm_isdst)
-	    ftz.timezone += 60;
-    }
-    else
-    {
-	nowtime = now->time;
-    }
-
-    tm = localtime(&nowtime);
-    yyYear = tm->tm_year + 1900;
-    yyMonth = tm->tm_mon + 1;
-    yyDay = tm->tm_mday;
-    yyTimezone = now->timezone;
-    yyDSTmode = DSTmaybe;
-    yyHour = 0;
-    yyMinutes = 0;
-    yySeconds = 0;
-    yyMeridian = MER24;
-    yyRelSeconds = 0;
-    yyRelMonth = 0;
-    yyHaveDate = 0;
-    yyHaveDay = 0;
-    yyHaveRel = 0;
-    yyHaveTime = 0;
-    yyHaveZone = 0;
-
-    if (yyparse()
-     || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
-	return -1;
-
-    if (yyHaveDate || yyHaveTime || yyHaveDay) {
-	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
-		    yyMeridian, yyDSTmode);
-	if (Start < 0)
-	    return -1;
-    }
-    else {
-	Start = nowtime;
-	if (!yyHaveRel)
-	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
-    }
-
-    Start += yyRelSeconds;
-    Start += RelativeMonth(Start, yyRelMonth);
-
-    if (yyHaveDay && !yyHaveDate) {
-	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
-	Start += tod;
-    }
-
-    /* Have to do *something* with a legitimate -1 so it's distinguishable
-     * from the error return value.  (Alternately could set errno on error.) */
-    return Start == -1 ? 0 : Start;
-}
-
-
-#if	defined(TEST)
-
-/* ARGSUSED */
-int
-main(int ac, char *av[])
-{
-    char	buff[128];
-    time_t	d;
-
-    (void)printf("Enter date, or blank line to exit.\n\t> ");
-    (void)fflush(stdout);
-    while (gets(buff) && buff[0]) {
-	d = svn_parse_date(buff, (struct getdate_time *)NULL);
-	if (d == -1)
-	    (void)printf("Bad format - couldn't convert.\n");
-	else
-	    (void)printf("%s", ctime(&d));
-	(void)printf("\t> ");
-	(void)fflush(stdout);
-    }
-    exit(0);
-    /* NOTREACHED */
-}
-#endif	/* defined(TEST) */
Index: subversion/libsvn_subr/getdate.cw
===================================================================
--- subversion/libsvn_subr/getdate.cw	(revision 8041)
+++ subversion/libsvn_subr/getdate.cw	(working copy)
@@ -1,2213 +0,0 @@
-/* A Bison parser, made from subversion/libsvn_subr/getdate.y
-   by GNU bison 1.35.  */
-
-/*  This file should be copied to getdate.c by the Win32 build system since
-    in Unix-land it is generated by yacc/bison and getdate.y  */
-
-#define YYBISON 1  /* Identify Bison output.  */
-
-# define	tAGO	257
-# define	tDAY	258
-# define	tDAYZONE	259
-# define	tID	260
-# define	tMERIDIAN	261
-# define	tMINUTE_UNIT	262
-# define	tMONTH	263
-# define	tMONTH_UNIT	264
-# define	tSEC_UNIT	265
-# define	tSNUMBER	266
-# define	tUNUMBER	267
-# define	tZONE	268
-# define	tDST	269
-
-#line 1 "subversion/libsvn_subr/getdate.y"
-
-/*
-**  Originally written by Steven M. Bellovin <sm...@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
-**  <rs...@bbn.com> and Jim Berets <jb...@bbn.com> in August, 1990;
-**
-**  This grammar has 10 shift/reduce conflicts.
-**
-**  This code is in the public domain and has no copyright.
-**
-**    July 3rd, 2001:  added to Subversion project and slightly 
-**                     tweaked by Ben Collins-Sussman <su...@collab.net>
-*/
-/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
-/* SUPPRESS 288 on yyerrlab *//* Label unused */
-
-#ifdef HAVE_CONFIG_H
-#if defined (emacs) || defined (CONFIG_BROKETS)
-#include <config.h>
-#else
-#include "config.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 <stdio.h>
-#include <ctype.h>
-
-/* The code at the top of svn_parse_date which figures out the offset of the
-   current time zone checks various CPP symbols to see if special
-   tricks are need, but defaults to using the gettimeofday system call.
-   Include <sys/time.h> if that will be used.  */
-
-#if	defined(vms)
-#include <types.h>
-#else /* defined(vms) */
-#include <sys/types.h>
-#include <time.h>
-#include "svn_time.h"
-#endif	/* !defined(vms) */
-
-#include <string.h>
-
-
-/* Some old versions of bison generate parsers that use bcopy.
-   That loses on systems that don't provide the function, so we have
-   to redefine it here.  */
-#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
-#define bcopy(from, to, len) memcpy ((to), (from), (len))
-#endif
-
-#if 1 || defined (STDC_HEADERS)
-#include <stdlib.h>
-#endif
-
-/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
-   releases):
-
-   We don't want to mess with all the portability hassles of alloca.
-   In particular, most (all?) versions of bison will use alloca in
-   their parser.  If bison works on your system (e.g. it should work
-   with gcc), then go ahead and use it, but the more general solution
-   is to use byacc instead of bison, which should generate a portable
-   parser.  I played with adding "#define alloca dont_use_alloca", to
-   give an error if the parser generator uses alloca (and thus detect
-   unportable getdate.c's), but that seems to cause as many problems
-   as it solves.  */
-
-/* ### if these prototypes don't match the system's prototypes all
- * this will break. */
-/*extern struct tm	*gmtime(const time_t *t);
-extern struct tm	*localtime(const time_t *t);*/
-
-#define yyparse getdate_yyparse
-#define yylex getdate_yylex
-#define yyerror getdate_yyerror
-
-static int yyparse (void);
-static int yylex (void);
-static int yyerror (const char *s);
-
-#define EPOCH		1970
-#define HOUR(x)		((time_t)(x) * 60)
-#define SECSPERDAY	(24L * 60L * 60L)
-
-
-/*
-**  An entry in the lexical lookup table.
-*/
-typedef struct _TABLE {
-    const char	*name;
-    int		type;
-    time_t	value;
-} TABLE;
-
-
-/*
-**  Daylight-savings mode:  on, off, or not yet known.
-*/
-typedef enum _DSTMODE {
-    DSTon, DSToff, DSTmaybe
-} DSTMODE;
-
-/*
-**  Meridian:  am, pm, or 24-hour style.
-*/
-typedef enum _MERIDIAN {
-    MERam, MERpm, MER24
-} MERIDIAN;
-
-
-/*
-**  Global variables.  We could get rid of most of these by using a good
-**  union as the yacc stack.  (This routine was originally written before
-**  yacc had the %union construct.)  Maybe someday; right now we only use
-**  the %union very rarely.
-*/
-static char	*yyInput;
-static DSTMODE	yyDSTmode;
-static time_t	yyDayOrdinal;
-static time_t	yyDayNumber;
-static int	yyHaveDate;
-static int	yyHaveDay;
-static int	yyHaveRel;
-static int	yyHaveTime;
-static int	yyHaveZone;
-static time_t	yyTimezone;
-static time_t	yyDay;
-static time_t	yyHour;
-static time_t	yyMinutes;
-static time_t	yyMonth;
-static time_t	yySeconds;
-static time_t	yyYear;
-static MERIDIAN	yyMeridian;
-static time_t	yyRelMonth;
-static time_t	yyRelSeconds;
-
-
-#line 150 "subversion/libsvn_subr/getdate.y"
-#ifndef YYSTYPE
-typedef union {
-    time_t		Number;
-    enum _MERIDIAN	Meridian;
-} yystype;
-# define YYSTYPE yystype
-# define YYSTYPE_IS_TRIVIAL 1
-#endif
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-
-
-
-#define	YYFINAL		52
-#define	YYFLAG		-32768
-#define	YYNTBASE	19
-
-/* YYTRANSLATE(YYLEX) -- Bison token number corresponding to YYLEX. */
-#define YYTRANSLATE(x) ((unsigned)(x) <= 269 ? yytranslate[x] : 29)
-
-/* YYTRANSLATE[YYLEX] -- Bison token number corresponding to YYLEX. */
-static const char yytranslate[] =
-{
-       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,    17,     2,     2,    18,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,    16,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     1,     3,     4,     5,
-       6,     7,     8,     9,    10,    11,    12,    13,    14,    15
-};
-
-#if YYDEBUG
-static const short yyprhs[] =
-{
-       0,     0,     1,     4,     6,     8,    10,    12,    14,    16,
-      19,    24,    29,    36,    43,    45,    47,    50,    52,    55,
-      58,    62,    68,    72,    76,    79,    84,    87,    91,    94,
-      96,    99,   102,   104,   107,   110,   112,   115,   118,   120,
-     122,   123
-};
-static const short yyrhs[] =
-{
-      -1,    19,    20,     0,    21,     0,    22,     0,    24,     0,
-      23,     0,    25,     0,    27,     0,    13,     7,     0,    13,
-      16,    13,    28,     0,    13,    16,    13,    12,     0,    13,
-      16,    13,    16,    13,    28,     0,    13,    16,    13,    16,
-      13,    12,     0,    14,     0,     5,     0,    14,    15,     0,
-       4,     0,     4,    17,     0,    13,     4,     0,    13,    18,
-      13,     0,    13,    18,    13,    18,    13,     0,    13,    12,
-      12,     0,    13,     9,    12,     0,     9,    13,     0,     9,
-      13,    17,    13,     0,    13,     9,     0,    13,     9,    13,
-       0,    26,     3,     0,    26,     0,    13,     8,     0,    12,
-       8,     0,     8,     0,    12,    11,     0,    13,    11,     0,
-      11,     0,    12,    10,     0,    13,    10,     0,    10,     0,
-      13,     0,     0,     7,     0
-};
-
-#endif
-
-#if YYDEBUG
-/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
-static const short yyrline[] =
-{
-       0,   164,   165,   168,   171,   174,   177,   180,   183,   186,
-     192,   198,   205,   211,   221,   225,   229,   236,   240,   244,
-     250,   254,   265,   271,   277,   281,   286,   290,   297,   301,
-     304,   307,   310,   313,   316,   319,   322,   325,   328,   333,
-     360,   363
-};
-#endif
-
-
-#if (YYDEBUG) || defined YYERROR_VERBOSE
-
-/* YYTNAME[TOKEN_NUM] -- String name of the token TOKEN_NUM. */
-static const char *const yytname[] =
-{
-  "$", "error", "$undefined.", "tAGO", "tDAY", "tDAYZONE", "tID", 
-  "tMERIDIAN", "tMINUTE_UNIT", "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", 
-  "tSNUMBER", "tUNUMBER", "tZONE", "tDST", "':'", "','", "'/'", "spec", 
-  "item", "time", "zone", "day", "date", "rel", "relunit", "number", 
-  "o_merid", 0
-};
-#endif
-
-/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
-static const short yyr1[] =
-{
-       0,    19,    19,    20,    20,    20,    20,    20,    20,    21,
-      21,    21,    21,    21,    22,    22,    22,    23,    23,    23,
-      24,    24,    24,    24,    24,    24,    24,    24,    25,    25,
-      26,    26,    26,    26,    26,    26,    26,    26,    26,    27,
-      28,    28
-};
-
-/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
-static const short yyr2[] =
-{
-       0,     0,     2,     1,     1,     1,     1,     1,     1,     2,
-       4,     4,     6,     6,     1,     1,     2,     1,     2,     2,
-       3,     5,     3,     3,     2,     4,     2,     3,     2,     1,
-       2,     2,     1,     2,     2,     1,     2,     2,     1,     1,
-       0,     1
-};
-
-/* YYDEFACT[S] -- default rule to reduce with in state S when YYTABLE
-   doesn't specify something else to do.  Zero means the default is an
-   error. */
-static const short yydefact[] =
-{
-       1,     0,    17,    15,    32,     0,    38,    35,     0,    39,
-      14,     2,     3,     4,     6,     5,     7,    29,     8,    18,
-      24,    31,    36,    33,    19,     9,    30,    26,    37,    34,
-       0,     0,     0,    16,    28,     0,    23,    27,    22,    40,
-      20,    25,    41,    11,     0,    10,     0,    40,    21,    13,
-      12,     0,     0
-};
-
-static const short yydefgoto[] =
-{
-       1,    11,    12,    13,    14,    15,    16,    17,    18,    45
-};
-
-static const short yypact[] =
-{
-  -32768,     0,    -1,-32768,-32768,     4,-32768,-32768,    25,    11,
-      -8,-32768,-32768,-32768,-32768,-32768,-32768,    21,-32768,-32768,
-       9,-32768,-32768,-32768,-32768,-32768,-32768,   -10,-32768,-32768,
-      16,    19,    24,-32768,-32768,    26,-32768,-32768,-32768,    18,
-      13,-32768,-32768,-32768,    27,-32768,    28,    -6,-32768,-32768,
-  -32768,    38,-32768
-};
-
-static const short yypgoto[] =
-{
-  -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,    -5
-};
-
-
-#define	YYLAST		42
-
-
-static const short yytable[] =
-{
-      51,    42,    36,    37,     2,     3,    49,    33,     4,     5,
-       6,     7,     8,     9,    10,    24,    19,    20,    25,    26,
-      27,    28,    29,    30,    34,    42,    35,    31,    38,    32,
-      43,    46,    39,    21,    44,    22,    23,    40,    52,    41,
-      47,    48,    50
-};
-
-static const short yycheck[] =
-{
-       0,     7,    12,    13,     4,     5,    12,    15,     8,     9,
-      10,    11,    12,    13,    14,     4,    17,    13,     7,     8,
-       9,    10,    11,    12,     3,     7,    17,    16,    12,    18,
-      12,    18,    13,     8,    16,    10,    11,    13,     0,    13,
-      13,    13,    47
-};
-/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
-#line 3 "/usr/share/bison/bison.simple"
-
-/* Skeleton output parser for bison,
-
-   Copyright (C) 1984, 1989, 1990, 2000, 2001, 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.  */
-
-/* As a special exception, when this file is copied by Bison into a
-   Bison output file, you may use that output file without restriction.
-   This special exception was added by the Free Software Foundation
-   in version 1.24 of Bison.  */
-
-/* This is the parser code that is written into each bison parser when
-   the %semantic_parser declaration is not specified in the grammar.
-   It was written by Richard Stallman by simplifying the hairy parser
-   used when %semantic_parser is specified.  */
-
-/* All symbols defined below should begin with yy or YY, to avoid
-   infringing on user name space.  This should be done even for local
-   variables, as they might otherwise be expanded by user macros.
-   There are some unavoidable exceptions within include files to
-   define necessary library symbols; they are noted "INFRINGES ON
-   USER NAME SPACE" below.  */
-
-#if ! defined (yyoverflow) || defined (YYERROR_VERBOSE)
-
-/* The parser invokes alloca or malloc; define the necessary symbols.  */
-
-# if YYSTACK_USE_ALLOCA
-#  define YYSTACK_ALLOC alloca
-# else
-#  ifndef YYSTACK_USE_ALLOCA
-#   if defined (alloca) || defined (_ALLOCA_H)
-#    define YYSTACK_ALLOC alloca
-#   else
-#    ifdef __GNUC__
-#     define YYSTACK_ALLOC __builtin_alloca
-#    endif
-#   endif
-#  endif
-# endif
-
-# ifdef YYSTACK_ALLOC
-   /* Pacify GCC's `empty if-body' warning. */
-#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
-# else
-#  if defined (__STDC__) || defined (__cplusplus)
-#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-#   define YYSIZE_T size_t
-#  endif
-#  define YYSTACK_ALLOC malloc
-#  define YYSTACK_FREE free
-# endif
-#endif /* ! defined (yyoverflow) || defined (YYERROR_VERBOSE) */
-
-
-#if (! defined (yyoverflow) \
-     && (! defined (__cplusplus) \
-	 || (YYLTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
-
-/* A type that is properly aligned for any stack member.  */
-union yyalloc
-{
-  short yyss;
-  YYSTYPE yyvs;
-# if YYLSP_NEEDED
-  YYLTYPE yyls;
-# endif
-};
-
-/* The size of the maximum gap between one aligned stack and the next.  */
-# define YYSTACK_GAP_MAX (sizeof (union yyalloc) - 1)
-
-/* The size of an array large to enough to hold all stacks, each with
-   N elements.  */
-# if YYLSP_NEEDED
-#  define YYSTACK_BYTES(N) \
-     ((N) * (sizeof (short) + sizeof (YYSTYPE) + sizeof (YYLTYPE))	\
-      + 2 * YYSTACK_GAP_MAX)
-# else
-#  define YYSTACK_BYTES(N) \
-     ((N) * (sizeof (short) + sizeof (YYSTYPE))				\
-      + YYSTACK_GAP_MAX)
-# endif
-
-/* Copy COUNT objects from FROM to TO.  The source and destination do
-   not overlap.  */
-# ifndef YYCOPY
-#  if 1 < __GNUC__
-#   define YYCOPY(To, From, Count) \
-      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
-#  else
-#   define YYCOPY(To, From, Count)		\
-      do					\
-	{					\
-	  register YYSIZE_T yyi;		\
-	  for (yyi = 0; yyi < (Count); yyi++)	\
-	    (To)[yyi] = (From)[yyi];		\
-	}					\
-      while (0)
-#  endif
-# endif
-
-/* Relocate STACK from its old location to the new one.  The
-   local variables YYSIZE and YYSTACKSIZE give the old and new number of
-   elements in the stack, and YYPTR gives the new location of the
-   stack.  Advance YYPTR to a properly aligned location for the next
-   stack.  */
-# define YYSTACK_RELOCATE(Stack)					\
-    do									\
-      {									\
-	YYSIZE_T yynewbytes;						\
-	YYCOPY (&yyptr->Stack, Stack, yysize);				\
-	Stack = &yyptr->Stack;						\
-	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAX;	\
-	yyptr += yynewbytes / sizeof (*yyptr);				\
-      }									\
-    while (0)
-
-#endif
-
-
-#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
-# define YYSIZE_T __SIZE_TYPE__
-#endif
-#if ! defined (YYSIZE_T) && defined (size_t)
-# define YYSIZE_T size_t
-#endif
-#if ! defined (YYSIZE_T)
-# if defined (__STDC__) || defined (__cplusplus)
-#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
-#  define YYSIZE_T size_t
-# endif
-#endif
-#if ! defined (YYSIZE_T)
-# define YYSIZE_T unsigned int
-#endif
-
-#define yyerrok		(yyerrstatus = 0)
-#define yyclearin	(yychar = YYEMPTY)
-#define YYEMPTY		-2
-#define YYEOF		0
-#define YYACCEPT	goto yyacceptlab
-#define YYABORT 	goto yyabortlab
-#define YYERROR		goto yyerrlab1
-/* Like YYERROR except do call yyerror.  This remains here temporarily
-   to ease the transition to the new meaning of YYERROR, for GCC.
-   Once GCC version 2 has supplanted version 1, this can go.  */
-#define YYFAIL		goto yyerrlab
-#define YYRECOVERING()  (!!yyerrstatus)
-#define YYBACKUP(Token, Value)					\
-do								\
-  if (yychar == YYEMPTY && yylen == 1)				\
-    {								\
-      yychar = (Token);						\
-      yylval = (Value);						\
-      yychar1 = YYTRANSLATE (yychar);				\
-      YYPOPSTACK;						\
-      goto yybackup;						\
-    }								\
-  else								\
-    { 								\
-      yyerror ("syntax error: cannot back up");			\
-      YYERROR;							\
-    }								\
-while (0)
-
-#define YYTERROR	1
-#define YYERRCODE	256
-
-
-/* YYLLOC_DEFAULT -- Compute the default location (before the actions
-   are run).
-
-   When YYLLOC_DEFAULT is run, CURRENT is set the location of the
-   first token.  By default, to implement support for ranges, extend
-   its range to the last symbol.  */
-
-#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N)       	\
-   Current.last_line   = Rhs[N].last_line;	\
-   Current.last_column = Rhs[N].last_column;
-#endif
-
-
-/* YYLEX -- calling `yylex' with the right arguments.  */
-
-#if YYPURE
-# if YYLSP_NEEDED
-#  ifdef YYLEX_PARAM
-#   define YYLEX		yylex (&yylval, &yylloc, YYLEX_PARAM)
-#  else
-#   define YYLEX		yylex (&yylval, &yylloc)
-#  endif
-# else /* !YYLSP_NEEDED */
-#  ifdef YYLEX_PARAM
-#   define YYLEX		yylex (&yylval, YYLEX_PARAM)
-#  else
-#   define YYLEX		yylex (&yylval)
-#  endif
-# endif /* !YYLSP_NEEDED */
-#else /* !YYPURE */
-# define YYLEX			yylex ()
-#endif /* !YYPURE */
-
-
-/* Enable debugging if requested.  */
-#if YYDEBUG
-
-# ifndef YYFPRINTF
-#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
-#  define YYFPRINTF fprintf
-# endif
-
-# define YYDPRINTF(Args)			\
-do {						\
-  if (yydebug)					\
-    YYFPRINTF Args;				\
-} while (0)
-/* Nonzero means print parse trace.  It is left uninitialized so that
-   multiple parsers can coexist.  */
-int yydebug;
-#else /* !YYDEBUG */
-# define YYDPRINTF(Args)
-#endif /* !YYDEBUG */
-
-/* YYINITDEPTH -- initial size of the parser's stacks.  */
-#ifndef	YYINITDEPTH
-# define YYINITDEPTH 200
-#endif
-
-/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
-   if the built-in stack extension method is used).
-
-   Do not make this value too large; the results are undefined if
-   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
-   evaluated with infinite-precision integer arithmetic.  */
-
-#if YYMAXDEPTH == 0
-# undef YYMAXDEPTH
-#endif
-
-#ifndef YYMAXDEPTH
-# define YYMAXDEPTH 10000
-#endif
-
-#ifdef YYERROR_VERBOSE
-
-# ifndef yystrlen
-#  if defined (__GLIBC__) && defined (_STRING_H)
-#   define yystrlen strlen
-#  else
-/* Return the length of YYSTR.  */
-static YYSIZE_T
-#   if defined (__STDC__) || defined (__cplusplus)
-yystrlen (const char *yystr)
-#   else
-yystrlen (yystr)
-     const char *yystr;
-#   endif
-{
-  register const char *yys = yystr;
-
-  while (*yys++ != '\0')
-    continue;
-
-  return yys - yystr - 1;
-}
-#  endif
-# endif
-
-# ifndef yystpcpy
-#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
-#   define yystpcpy stpcpy
-#  else
-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
-   YYDEST.  */
-static char *
-#   if defined (__STDC__) || defined (__cplusplus)
-yystpcpy (char *yydest, const char *yysrc)
-#   else
-yystpcpy (yydest, yysrc)
-     char *yydest;
-     const char *yysrc;
-#   endif
-{
-  register char *yyd = yydest;
-  register const char *yys = yysrc;
-
-  while ((*yyd++ = *yys++) != '\0')
-    continue;
-
-  return yyd - 1;
-}
-#  endif
-# endif
-#endif
-
-#line 315 "/usr/share/bison/bison.simple"
-
-
-/* The user can define YYPARSE_PARAM as the name of an argument to be passed
-   into yyparse.  The argument should have type void *.
-   It should actually point to an object.
-   Grammar actions can access the variable by casting it
-   to the proper pointer type.  */
-
-#ifdef YYPARSE_PARAM
-# if defined (__STDC__) || defined (__cplusplus)
-#  define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
-#  define YYPARSE_PARAM_DECL
-# else
-#  define YYPARSE_PARAM_ARG YYPARSE_PARAM
-#  define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
-# endif
-#else /* !YYPARSE_PARAM */
-# define YYPARSE_PARAM_ARG
-# define YYPARSE_PARAM_DECL
-#endif /* !YYPARSE_PARAM */
-
-/* Prevent warning if -Wstrict-prototypes.  */
-#ifdef __GNUC__
-# ifdef YYPARSE_PARAM
-int yyparse (void *);
-# else
-int yyparse (void);
-# endif
-#endif
-
-/* YY_DECL_VARIABLES -- depending whether we use a pure parser,
-   variables are global, or local to YYPARSE.  */
-
-#define YY_DECL_NON_LSP_VARIABLES			\
-/* The lookahead symbol.  */				\
-int yychar;						\
-							\
-/* The semantic value of the lookahead symbol. */	\
-YYSTYPE yylval;						\
-							\
-/* Number of parse errors so far.  */			\
-int yynerrs;
-
-#if YYLSP_NEEDED
-# define YY_DECL_VARIABLES			\
-YY_DECL_NON_LSP_VARIABLES			\
-						\
-/* Location data for the lookahead symbol.  */	\
-YYLTYPE yylloc;
-#else
-# define YY_DECL_VARIABLES			\
-YY_DECL_NON_LSP_VARIABLES
-#endif
-
-
-/* If nonreentrant, generate the variables here. */
-
-#if !YYPURE
-YY_DECL_VARIABLES
-#endif  /* !YYPURE */
-
-int
-yyparse (YYPARSE_PARAM_ARG)
-     YYPARSE_PARAM_DECL
-{
-  /* If reentrant, generate the variables here. */
-#if YYPURE
-  YY_DECL_VARIABLES
-#endif  /* !YYPURE */
-
-  register int yystate;
-  register int yyn;
-  int yyresult;
-  /* Number of tokens to shift before error messages enabled.  */
-  int yyerrstatus;
-  /* Lookahead token as an internal (translated) token number.  */
-  int yychar1 = 0;
-
-  /* Three stacks and their tools:
-     `yyss': related to states,
-     `yyvs': related to semantic values,
-     `yyls': related to locations.
-
-     Refer to the stacks thru separate pointers, to allow yyoverflow
-     to reallocate them elsewhere.  */
-
-  /* The state stack. */
-  short	yyssa[YYINITDEPTH];
-  short *yyss = yyssa;
-  register short *yyssp;
-
-  /* The semantic value stack.  */
-  YYSTYPE yyvsa[YYINITDEPTH];
-  YYSTYPE *yyvs = yyvsa;
-  register YYSTYPE *yyvsp;
-
-#if YYLSP_NEEDED
-  /* The location stack.  */
-  YYLTYPE yylsa[YYINITDEPTH];
-  YYLTYPE *yyls = yylsa;
-  YYLTYPE *yylsp;
-#endif
-
-#if YYLSP_NEEDED
-# define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
-#else
-# define YYPOPSTACK   (yyvsp--, yyssp--)
-#endif
-
-  YYSIZE_T yystacksize = YYINITDEPTH;
-
-
-  /* The variables used to return semantic value and location from the
-     action routines.  */
-  YYSTYPE yyval;
-#if YYLSP_NEEDED
-  YYLTYPE yyloc;
-#endif
-
-  /* When reducing, the number of symbols on the RHS of the reduced
-     rule. */
-  int yylen;
-
-  YYDPRINTF ((stderr, "Starting parse\n"));
-
-  yystate = 0;
-  yyerrstatus = 0;
-  yynerrs = 0;
-  yychar = YYEMPTY;		/* Cause a token to be read.  */
-
-  /* Initialize stack pointers.
-     Waste one element of value and location stack
-     so that they stay on the same level as the state stack.
-     The wasted elements are never initialized.  */
-
-  yyssp = yyss;
-  yyvsp = yyvs;
-#if YYLSP_NEEDED
-  yylsp = yyls;
-#endif
-  goto yysetstate;
-
-/*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
-`------------------------------------------------------------*/
- yynewstate:
-  /* In all cases, when you get here, the value and location stacks
-     have just been pushed. so pushing a state here evens the stacks.
-     */
-  yyssp++;
-
- yysetstate:
-  *yyssp = yystate;
-
-  if (yyssp >= yyss + yystacksize - 1)
-    {
-      /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
-
-#ifdef yyoverflow
-      {
-	/* Give user a chance to reallocate the stack. Use copies of
-	   these so that the &'s don't force the real ones into
-	   memory.  */
-	YYSTYPE *yyvs1 = yyvs;
-	short *yyss1 = yyss;
-
-	/* Each stack pointer address is followed by the size of the
-	   data in use in that stack, in bytes.  */
-# if YYLSP_NEEDED
-	YYLTYPE *yyls1 = yyls;
-	/* This used to be a conditional around just the two extra args,
-	   but that might be undefined if yyoverflow is a macro.  */
-	yyoverflow ("parser stack overflow",
-		    &yyss1, yysize * sizeof (*yyssp),
-		    &yyvs1, yysize * sizeof (*yyvsp),
-		    &yyls1, yysize * sizeof (*yylsp),
-		    &yystacksize);
-	yyls = yyls1;
-# else
-	yyoverflow ("parser stack overflow",
-		    &yyss1, yysize * sizeof (*yyssp),
-		    &yyvs1, yysize * sizeof (*yyvsp),
-		    &yystacksize);
-# endif
-	yyss = yyss1;
-	yyvs = yyvs1;
-      }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyoverflowlab;
-# else
-      /* Extend the stack our own way.  */
-      if (yystacksize >= YYMAXDEPTH)
-	goto yyoverflowlab;
-      yystacksize *= 2;
-      if (yystacksize > YYMAXDEPTH)
-	yystacksize = YYMAXDEPTH;
-
-      {
-	short *yyss1 = yyss;
-	union yyalloc *yyptr =
-	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
-	if (! yyptr)
-	  goto yyoverflowlab;
-	YYSTACK_RELOCATE (yyss);
-	YYSTACK_RELOCATE (yyvs);
-# if YYLSP_NEEDED
-	YYSTACK_RELOCATE (yyls);
-# endif
-# undef YYSTACK_RELOCATE
-	if (yyss1 != yyssa)
-	  YYSTACK_FREE (yyss1);
-      }
-# endif
-#endif /* no yyoverflow */
-
-      yyssp = yyss + yysize - 1;
-      yyvsp = yyvs + yysize - 1;
-#if YYLSP_NEEDED
-      yylsp = yyls + yysize - 1;
-#endif
-
-      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-		  (unsigned long int) yystacksize));
-
-      if (yyssp >= yyss + yystacksize - 1)
-	YYABORT;
-    }
-
-  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
-
-  goto yybackup;
-
-
-/*-----------.
-| yybackup.  |
-`-----------*/
-yybackup:
-
-/* Do appropriate processing given the current state.  */
-/* Read a lookahead token if we need one and don't already have one.  */
-/* yyresume: */
-
-  /* First try to decide what to do without reference to lookahead token.  */
-
-  yyn = yypact[yystate];
-  if (yyn == YYFLAG)
-    goto yydefault;
-
-  /* Not known => get a lookahead token if don't already have one.  */
-
-  /* yychar is either YYEMPTY or YYEOF
-     or a valid token in external form.  */
-
-  if (yychar == YYEMPTY)
-    {
-      YYDPRINTF ((stderr, "Reading a token: "));
-      yychar = YYLEX;
-    }
-
-  /* Convert token to internal form (in yychar1) for indexing tables with */
-
-  if (yychar <= 0)		/* This means end of input. */
-    {
-      yychar1 = 0;
-      yychar = YYEOF;		/* Don't call YYLEX any more */
-
-      YYDPRINTF ((stderr, "Now at end of input.\n"));
-    }
-  else
-    {
-      yychar1 = YYTRANSLATE (yychar);
-
-#if YYDEBUG
-     /* We have to keep this `#if YYDEBUG', since we use variables
-	which are defined only if `YYDEBUG' is set.  */
-      if (yydebug)
-	{
-	  YYFPRINTF (stderr, "Next token is %d (%s",
-		     yychar, yytname[yychar1]);
-	  /* Give the individual parser a way to print the precise
-	     meaning of a token, for further debugging info.  */
-# ifdef YYPRINT
-	  YYPRINT (stderr, yychar, yylval);
-# endif
-	  YYFPRINTF (stderr, ")\n");
-	}
-#endif
-    }
-
-  yyn += yychar1;
-  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
-    goto yydefault;
-
-  yyn = yytable[yyn];
-
-  /* yyn is what to do for this token type in this state.
-     Negative => reduce, -yyn is rule number.
-     Positive => shift, yyn is new state.
-       New state is final state => don't bother to shift,
-       just return success.
-     0, or most negative number => error.  */
-
-  if (yyn < 0)
-    {
-      if (yyn == YYFLAG)
-	goto yyerrlab;
-      yyn = -yyn;
-      goto yyreduce;
-    }
-  else if (yyn == 0)
-    goto yyerrlab;
-
-  if (yyn == YYFINAL)
-    YYACCEPT;
-
-  /* Shift the lookahead token.  */
-  YYDPRINTF ((stderr, "Shifting token %d (%s), ",
-	      yychar, yytname[yychar1]));
-
-  /* Discard the token being shifted unless it is eof.  */
-  if (yychar != YYEOF)
-    yychar = YYEMPTY;
-
-  *++yyvsp = yylval;
-#if YYLSP_NEEDED
-  *++yylsp = yylloc;
-#endif
-
-  /* Count tokens shifted since error; after three, turn off error
-     status.  */
-  if (yyerrstatus)
-    yyerrstatus--;
-
-  yystate = yyn;
-  goto yynewstate;
-
-
-/*-----------------------------------------------------------.
-| yydefault -- do the default action for the current state.  |
-`-----------------------------------------------------------*/
-yydefault:
-  yyn = yydefact[yystate];
-  if (yyn == 0)
-    goto yyerrlab;
-  goto yyreduce;
-
-
-/*-----------------------------.
-| yyreduce -- Do a reduction.  |
-`-----------------------------*/
-yyreduce:
-  /* yyn is the number of a rule to reduce with.  */
-  yylen = yyr2[yyn];
-
-  /* If YYLEN is nonzero, implement the default value of the action:
-     `$$ = $1'.
-
-     Otherwise, the following line sets YYVAL to the semantic value of
-     the lookahead token.  This behavior is undocumented and Bison
-     users should not rely upon it.  Assigning to YYVAL
-     unconditionally makes the parser a bit smaller, and it avoids a
-     GCC warning that YYVAL may be used uninitialized.  */
-  yyval = yyvsp[1-yylen];
-
-#if YYLSP_NEEDED
-  /* Similarly for the default location.  Let the user run additional
-     commands if for instance locations are ranges.  */
-  yyloc = yylsp[1-yylen];
-  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
-#endif
-
-#if YYDEBUG
-  /* We have to keep this `#if YYDEBUG', since we use variables which
-     are defined only if `YYDEBUG' is set.  */
-  if (yydebug)
-    {
-      int yyi;
-
-      YYFPRINTF (stderr, "Reducing via rule %d (line %d), ",
-		 yyn, yyrline[yyn]);
-
-      /* Print the symbols being reduced, and their result.  */
-      for (yyi = yyprhs[yyn]; yyrhs[yyi] > 0; yyi++)
-	YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]);
-      YYFPRINTF (stderr, " -> %s\n", yytname[yyr1[yyn]]);
-    }
-#endif
-
-  switch (yyn) {
-
-case 3:
-#line 168 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHaveTime++;
-	;
-    break;}
-case 4:
-#line 171 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHaveZone++;
-	;
-    break;}
-case 5:
-#line 174 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHaveDate++;
-	;
-    break;}
-case 6:
-#line 177 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHaveDay++;
-	;
-    break;}
-case 7:
-#line 180 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHaveRel++;
-	;
-    break;}
-case 9:
-#line 186 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHour = yyvsp[-1].Number;
-	    yyMinutes = 0;
-	    yySeconds = 0;
-	    yyMeridian = yyvsp[0].Meridian;
-	;
-    break;}
-case 10:
-#line 192 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHour = yyvsp[-3].Number;
-	    yyMinutes = yyvsp[-1].Number;
-	    yySeconds = 0;
-	    yyMeridian = yyvsp[0].Meridian;
-	;
-    break;}
-case 11:
-#line 198 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHour = yyvsp[-3].Number;
-	    yyMinutes = yyvsp[-1].Number;
-	    yyMeridian = MER24;
-	    yyDSTmode = DSToff;
-	    yyTimezone = - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60);
-	;
-    break;}
-case 12:
-#line 205 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHour = yyvsp[-5].Number;
-	    yyMinutes = yyvsp[-3].Number;
-	    yySeconds = yyvsp[-1].Number;
-	    yyMeridian = yyvsp[0].Meridian;
-	;
-    break;}
-case 13:
-#line 211 "subversion/libsvn_subr/getdate.y"
-{
-	    yyHour = yyvsp[-5].Number;
-	    yyMinutes = yyvsp[-3].Number;
-	    yySeconds = yyvsp[-1].Number;
-	    yyMeridian = MER24;
-	    yyDSTmode = DSToff;
-	    yyTimezone = - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60);
-	;
-    break;}
-case 14:
-#line 221 "subversion/libsvn_subr/getdate.y"
-{
-	    yyTimezone = yyvsp[0].Number;
-	    yyDSTmode = DSToff;
-	;
-    break;}
-case 15:
-#line 225 "subversion/libsvn_subr/getdate.y"
-{
-	    yyTimezone = yyvsp[0].Number;
-	    yyDSTmode = DSTon;
-	;
-    break;}
-case 16:
-#line 230 "subversion/libsvn_subr/getdate.y"
-{
-	    yyTimezone = yyvsp[-1].Number;
-	    yyDSTmode = DSTon;
-	;
-    break;}
-case 17:
-#line 236 "subversion/libsvn_subr/getdate.y"
-{
-	    yyDayOrdinal = 1;
-	    yyDayNumber = yyvsp[0].Number;
-	;
-    break;}
-case 18:
-#line 240 "subversion/libsvn_subr/getdate.y"
-{
-	    yyDayOrdinal = 1;
-	    yyDayNumber = yyvsp[-1].Number;
-	;
-    break;}
-case 19:
-#line 244 "subversion/libsvn_subr/getdate.y"
-{
-	    yyDayOrdinal = yyvsp[-1].Number;
-	    yyDayNumber = yyvsp[0].Number;
-	;
-    break;}
-case 20:
-#line 250 "subversion/libsvn_subr/getdate.y"
-{
-	    yyMonth = yyvsp[-2].Number;
-	    yyDay = yyvsp[0].Number;
-	;
-    break;}
-case 21:
-#line 254 "subversion/libsvn_subr/getdate.y"
-{
-	    if (yyvsp[-4].Number >= 100) {
-		yyYear = yyvsp[-4].Number;
-		yyMonth = yyvsp[-2].Number;
-		yyDay = yyvsp[0].Number;
-	    } else {
-		yyMonth = yyvsp[-4].Number;
-		yyDay = yyvsp[-2].Number;
-		yyYear = yyvsp[0].Number;
-	    }
-	;
-    break;}
-case 22:
-#line 265 "subversion/libsvn_subr/getdate.y"
-{
-	    /* ISO 8601 format.  yyyy-mm-dd.  */
-	    yyYear = yyvsp[-2].Number;
-	    yyMonth = -yyvsp[-1].Number;
-	    yyDay = -yyvsp[0].Number;
-	;
-    break;}
-case 23:
-#line 271 "subversion/libsvn_subr/getdate.y"
-{
-	    /* e.g. 17-JUN-1992.  */
-	    yyDay = yyvsp[-2].Number;
-	    yyMonth = yyvsp[-1].Number;
-	    yyYear = -yyvsp[0].Number;
-	;
-    break;}
-case 24:
-#line 277 "subversion/libsvn_subr/getdate.y"
-{
-	    yyMonth = yyvsp[-1].Number;
-	    yyDay = yyvsp[0].Number;
-	;
-    break;}
-case 25:
-#line 281 "subversion/libsvn_subr/getdate.y"
-{
-	    yyMonth = yyvsp[-3].Number;
-	    yyDay = yyvsp[-2].Number;
-	    yyYear = yyvsp[0].Number;
-	;
-    break;}
-case 26:
-#line 286 "subversion/libsvn_subr/getdate.y"
-{
-	    yyMonth = yyvsp[0].Number;
-	    yyDay = yyvsp[-1].Number;
-	;
-    break;}
-case 27:
-#line 290 "subversion/libsvn_subr/getdate.y"
-{
-	    yyMonth = yyvsp[-1].Number;
-	    yyDay = yyvsp[-2].Number;
-	    yyYear = yyvsp[0].Number;
-	;
-    break;}
-case 28:
-#line 297 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds = -yyRelSeconds;
-	    yyRelMonth = -yyRelMonth;
-	;
-    break;}
-case 30:
-#line 304 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number * 60L;
-	;
-    break;}
-case 31:
-#line 307 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number * 60L;
-	;
-    break;}
-case 32:
-#line 310 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds += yyvsp[0].Number * 60L;
-	;
-    break;}
-case 33:
-#line 313 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds += yyvsp[-1].Number;
-	;
-    break;}
-case 34:
-#line 316 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds += yyvsp[-1].Number;
-	;
-    break;}
-case 35:
-#line 319 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelSeconds++;
-	;
-    break;}
-case 36:
-#line 322 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
-	;
-    break;}
-case 37:
-#line 325 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
-	;
-    break;}
-case 38:
-#line 328 "subversion/libsvn_subr/getdate.y"
-{
-	    yyRelMonth += yyvsp[0].Number;
-	;
-    break;}
-case 39:
-#line 333 "subversion/libsvn_subr/getdate.y"
-{
-	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
-		yyYear = yyvsp[0].Number;
-	    else {
-		if(yyvsp[0].Number>10000) {
-		    yyHaveDate++;
-		    yyDay= (yyvsp[0].Number)%100;
-		    yyMonth= (yyvsp[0].Number/100)%100;
-		    yyYear = yyvsp[0].Number/10000;
-		}
-		else {
-		    yyHaveTime++;
-		    if (yyvsp[0].Number < 100) {
-			yyHour = yyvsp[0].Number;
-			yyMinutes = 0;
-		    }
-		    else {
-		    	yyHour = yyvsp[0].Number / 100;
-		    	yyMinutes = yyvsp[0].Number % 100;
-		    }
-		    yySeconds = 0;
-		    yyMeridian = MER24;
-	        }
-	    }
-	;
-    break;}
-case 40:
-#line 360 "subversion/libsvn_subr/getdate.y"
-{
-	    yyval.Meridian = MER24;
-	;
-    break;}
-case 41:
-#line 363 "subversion/libsvn_subr/getdate.y"
-{
-	    yyval.Meridian = yyvsp[0].Meridian;
-	;
-    break;}
-}
-
-#line 705 "/usr/share/bison/bison.simple"
-
-
-  yyvsp -= yylen;
-  yyssp -= yylen;
-#if YYLSP_NEEDED
-  yylsp -= yylen;
-#endif
-
-#if YYDEBUG
-  if (yydebug)
-    {
-      short *yyssp1 = yyss - 1;
-      YYFPRINTF (stderr, "state stack now");
-      while (yyssp1 != yyssp)
-	YYFPRINTF (stderr, " %d", *++yyssp1);
-      YYFPRINTF (stderr, "\n");
-    }
-#endif
-
-  *++yyvsp = yyval;
-#if YYLSP_NEEDED
-  *++yylsp = yyloc;
-#endif
-
-  /* Now `shift' the result of the reduction.  Determine what state
-     that goes to, based on the state we popped back to and the rule
-     number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
-  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTBASE];
-
-  goto yynewstate;
-
-
-/*------------------------------------.
-| yyerrlab -- here on detecting error |
-`------------------------------------*/
-yyerrlab:
-  /* If not already recovering from an error, report this error.  */
-  if (!yyerrstatus)
-    {
-      ++yynerrs;
-
-#ifdef YYERROR_VERBOSE
-      yyn = yypact[yystate];
-
-      if (yyn > YYFLAG && yyn < YYLAST)
-	{
-	  YYSIZE_T yysize = 0;
-	  char *yymsg;
-	  int yyx, yycount;
-
-	  yycount = 0;
-	  /* Start YYX at -YYN if negative to avoid negative indexes in
-	     YYCHECK.  */
-	  for (yyx = yyn < 0 ? -yyn : 0;
-	       yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
-	    if (yycheck[yyx + yyn] == yyx)
-	      yysize += yystrlen (yytname[yyx]) + 15, yycount++;
-	  yysize += yystrlen ("parse error, unexpected ") + 1;
-	  yysize += yystrlen (yytname[YYTRANSLATE (yychar)]);
-	  yymsg = (char *) YYSTACK_ALLOC (yysize);
-	  if (yymsg != 0)
-	    {
-	      char *yyp = yystpcpy (yymsg, "parse error, unexpected ");
-	      yyp = yystpcpy (yyp, yytname[YYTRANSLATE (yychar)]);
-
-	      if (yycount < 5)
-		{
-		  yycount = 0;
-		  for (yyx = yyn < 0 ? -yyn : 0;
-		       yyx < (int) (sizeof (yytname) / sizeof (char *));
-		       yyx++)
-		    if (yycheck[yyx + yyn] == yyx)
-		      {
-			const char *yyq = ! yycount ? ", expecting " : " or ";
-			yyp = yystpcpy (yyp, yyq);
-			yyp = yystpcpy (yyp, yytname[yyx]);
-			yycount++;
-		      }
-		}
-	      yyerror (yymsg);
-	      YYSTACK_FREE (yymsg);
-	    }
-	  else
-	    yyerror ("parse error; also virtual memory exhausted");
-	}
-      else
-#endif /* defined (YYERROR_VERBOSE) */
-	yyerror ("parse error");
-    }
-  goto yyerrlab1;
-
-
-/*--------------------------------------------------.
-| yyerrlab1 -- error raised explicitly by an action |
-`--------------------------------------------------*/
-yyerrlab1:
-  if (yyerrstatus == 3)
-    {
-      /* If just tried and failed to reuse lookahead token after an
-	 error, discard it.  */
-
-      /* return failure if at end of input */
-      if (yychar == YYEOF)
-	YYABORT;
-      YYDPRINTF ((stderr, "Discarding token %d (%s).\n",
-		  yychar, yytname[yychar1]));
-      yychar = YYEMPTY;
-    }
-
-  /* Else will try to reuse lookahead token after shifting the error
-     token.  */
-
-  yyerrstatus = 3;		/* Each real token shifted decrements this */
-
-  goto yyerrhandle;
-
-
-/*-------------------------------------------------------------------.
-| yyerrdefault -- current state does not do anything special for the |
-| error token.                                                       |
-`-------------------------------------------------------------------*/
-yyerrdefault:
-#if 0
-  /* This is wrong; only states that explicitly want error tokens
-     should shift them.  */
-
-  /* If its default is to accept any token, ok.  Otherwise pop it.  */
-  yyn = yydefact[yystate];
-  if (yyn)
-    goto yydefault;
-#endif
-
-
-/*---------------------------------------------------------------.
-| yyerrpop -- pop the current state because it cannot handle the |
-| error token                                                    |
-`---------------------------------------------------------------*/
-yyerrpop:
-  if (yyssp == yyss)
-    YYABORT;
-  yyvsp--;
-  yystate = *--yyssp;
-#if YYLSP_NEEDED
-  yylsp--;
-#endif
-
-#if YYDEBUG
-  if (yydebug)
-    {
-      short *yyssp1 = yyss - 1;
-      YYFPRINTF (stderr, "Error: state stack now");
-      while (yyssp1 != yyssp)
-	YYFPRINTF (stderr, " %d", *++yyssp1);
-      YYFPRINTF (stderr, "\n");
-    }
-#endif
-
-/*--------------.
-| yyerrhandle.  |
-`--------------*/
-yyerrhandle:
-  yyn = yypact[yystate];
-  if (yyn == YYFLAG)
-    goto yyerrdefault;
-
-  yyn += YYTERROR;
-  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
-    goto yyerrdefault;
-
-  yyn = yytable[yyn];
-  if (yyn < 0)
-    {
-      if (yyn == YYFLAG)
-	goto yyerrpop;
-      yyn = -yyn;
-      goto yyreduce;
-    }
-  else if (yyn == 0)
-    goto yyerrpop;
-
-  if (yyn == YYFINAL)
-    YYACCEPT;
-
-  YYDPRINTF ((stderr, "Shifting error token, "));
-
-  *++yyvsp = yylval;
-#if YYLSP_NEEDED
-  *++yylsp = yylloc;
-#endif
-
-  yystate = yyn;
-  goto yynewstate;
-
-
-/*-------------------------------------.
-| yyacceptlab -- YYACCEPT comes here.  |
-`-------------------------------------*/
-yyacceptlab:
-  yyresult = 0;
-  goto yyreturn;
-
-/*-----------------------------------.
-| yyabortlab -- YYABORT comes here.  |
-`-----------------------------------*/
-yyabortlab:
-  yyresult = 1;
-  goto yyreturn;
-
-/*---------------------------------------------.
-| yyoverflowab -- parser overflow comes here.  |
-`---------------------------------------------*/
-yyoverflowlab:
-  yyerror ("parser stack overflow");
-  yyresult = 2;
-  /* Fall through.  */
-
-yyreturn:
-#ifndef yyoverflow
-  if (yyss != yyssa)
-    YYSTACK_FREE (yyss);
-#endif
-  return yyresult;
-}
-#line 368 "subversion/libsvn_subr/getdate.y"
-
-
-/* Month and day table. */
-static TABLE const MonthDayTable[] = {
-    { "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 },
-    { NULL }
-};
-
-/* Time units table. */
-static TABLE const UnitsTable[] = {
-    { "year",		tMONTH_UNIT,	12 },
-    { "month",		tMONTH_UNIT,	1 },
-    { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
-    { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
-    { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
-    { "hour",		tMINUTE_UNIT,	60 },
-    { "minute",		tMINUTE_UNIT,	1 },
-    { "min",		tMINUTE_UNIT,	1 },
-    { "second",		tSEC_UNIT,	1 },
-    { "sec",		tSEC_UNIT,	1 },
-    { NULL }
-};
-
-/* Assorted relative-time words. */
-static TABLE const OtherTable[] = {
-    { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
-    { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
-    { "today",		tMINUTE_UNIT,	0 },
-    { "now",		tMINUTE_UNIT,	0 },
-    { "last",		tUNUMBER,	-1 },
-    { "this",		tMINUTE_UNIT,	0 },
-    { "next",		tUNUMBER,	2 },
-    { "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 },
-    { NULL }
-};
-
-/* The timezone table. */
-/* Some of these are commented out because a time_t can't store a float. */
-static TABLE const TimezoneTable[] = {
-    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
-    { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
-    { "utc",	tZONE,     HOUR( 0) },
-    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
-    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
-    { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
-    { "at",	tZONE,     HOUR( 2) },	/* Azores */
-#if	0
-    /* For completeness.  BST is also British Summer, and GST is
-     * also Guam Standard. */
-    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
-    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
-#endif
-#if 0
-    { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
-    { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
-    { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
-#endif
-    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
-    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
-    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
-    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
-    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
-    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
-    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
-    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
-    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
-    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
-    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
-    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
-    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
-    { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
-    { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
-    { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
-    { "nt",	tZONE,     HOUR(11) },	/* Nome */
-    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
-    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
-    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
-    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
-    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
-    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
-    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
-    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
-    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
-    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
-    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
-#if 0
-    { "it",	tZONE,     -HOUR(3.5) },/* Iran */
-#endif
-    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
-    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
-#if 0
-    { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
-#endif
-    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
-#if	0
-    /* For completeness.  NST is also Newfoundland Stanard, and SST is
-     * also Swedish Summer. */
-    { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
-    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
-#endif	/* 0 */
-    { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
-    { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
-#if 0
-    { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
-#endif
-    { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
-    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
-#if 0
-    { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
-    { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
-#endif
-    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
-    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
-    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
-    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
-    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
-    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
-    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
-    {  NULL  }
-};
-
-/* Military timezone table. */
-static TABLE const MilitaryTable[] = {
-    { "a",	tZONE,	HOUR(  1) },
-    { "b",	tZONE,	HOUR(  2) },
-    { "c",	tZONE,	HOUR(  3) },
-    { "d",	tZONE,	HOUR(  4) },
-    { "e",	tZONE,	HOUR(  5) },
-    { "f",	tZONE,	HOUR(  6) },
-    { "g",	tZONE,	HOUR(  7) },
-    { "h",	tZONE,	HOUR(  8) },
-    { "i",	tZONE,	HOUR(  9) },
-    { "k",	tZONE,	HOUR( 10) },
-    { "l",	tZONE,	HOUR( 11) },
-    { "m",	tZONE,	HOUR( 12) },
-    { "n",	tZONE,	HOUR(- 1) },
-    { "o",	tZONE,	HOUR(- 2) },
-    { "p",	tZONE,	HOUR(- 3) },
-    { "q",	tZONE,	HOUR(- 4) },
-    { "r",	tZONE,	HOUR(- 5) },
-    { "s",	tZONE,	HOUR(- 6) },
-    { "t",	tZONE,	HOUR(- 7) },
-    { "u",	tZONE,	HOUR(- 8) },
-    { "v",	tZONE,	HOUR(- 9) },
-    { "w",	tZONE,	HOUR(-10) },
-    { "x",	tZONE,	HOUR(-11) },
-    { "y",	tZONE,	HOUR(-12) },
-    { "z",	tZONE,	HOUR(  0) },
-    { NULL }
-};
-
-
-
-
-/* ARGSUSED */
-static int
-yyerror(s)
-    const char	*s;
-{
-  return 0;
-}
-
-
-static time_t
-ToSeconds(time_t Hours,
-          time_t Minutes,
-          time_t Seconds,
-          MERIDIAN Meridian)
-{
-    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
-	return -1;
-    switch (Meridian) {
-    case MER24:
-	if (Hours < 0 || Hours > 23)
-	    return -1;
-	return (Hours * 60L + Minutes) * 60L + Seconds;
-    case MERam:
-	if (Hours < 1 || Hours > 12)
-	    return -1;
-	if (Hours == 12)
-	    Hours = 0;
-	return (Hours * 60L + Minutes) * 60L + Seconds;
-    case MERpm:
-	if (Hours < 1 || Hours > 12)
-	    return -1;
-	if (Hours == 12)
-	    Hours = 0;
-	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
-    default:
-	abort ();
-    }
-    /* NOTREACHED */
-}
-
-
-/* Year is either
-   * A negative number, which means to use its absolute value (why?)
-   * A number from 0 to 99, which means a year from 1900 to 1999, or
-   * The actual year (>=100).  */
-static time_t
-Convert (time_t Month,
-         time_t Day,
-         time_t Year,
-         time_t Hours,
-         time_t Minutes,
-         time_t Seconds,
-         MERIDIAN Meridian,
-         DSTMODE DSTmode)
-{
-    static int DaysInMonth[12] = {
-	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-    };
-    time_t	tod;
-    time_t	Julian;
-    int		i;
-
-    if (Year < 0)
-	Year = -Year;
-    if (Year < 69)
-	Year += 2000;
-    else if (Year < 100)
-	Year += 1900;
-    DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
-		    ? 29 : 28;
-    /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
-       I'm too lazy to try to check for time_t overflow in another way.  */
-    if (Year < EPOCH || Year > 2038
-     || Month < 1 || Month > 12
-     /* Lint fluff:  "conversion from long may lose accuracy" */
-     || Day < 1 || Day > DaysInMonth[(int)--Month])
-	return -1;
-
-    for (Julian = Day - 1, i = 0; i < Month; i++)
-	Julian += DaysInMonth[i];
-    for (i = EPOCH; i < Year; i++)
-	Julian += 365 + (i % 4 == 0);
-    Julian *= SECSPERDAY;
-    Julian += yyTimezone * 60L;
-    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
-	return -1;
-    Julian += tod;
-    if (DSTmode == DSTon
-     || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
-	Julian -= 60 * 60;
-    return Julian;
-}
-
-
-static time_t
-DSTcorrect(time_t Start, time_t Future)
-{
-    time_t	StartDay;
-    time_t	FutureDay;
-
-    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
-    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
-    return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
-}
-
-
-static time_t
-RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
-{
-    struct tm	*tm;
-    time_t	now;
-
-    now = Start;
-    tm = localtime(&now);
-    now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
-    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
-    return DSTcorrect(Start, now);
-}
-
-
-static time_t
-RelativeMonth(time_t Start, time_t RelMonth)
-{
-    struct tm	*tm;
-    time_t	Month;
-    time_t	Year;
-
-    if (RelMonth == 0)
-	return 0;
-    tm = localtime(&Start);
-    Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
-    Year = Month / 12;
-    Month = Month % 12 + 1;
-    return DSTcorrect(Start,
-	    Convert(Month, (time_t)tm->tm_mday, Year,
-		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
-		MER24, DSTmaybe));
-}
-
-
-static int
-LookupWord(char *buff)
-{
-    register char	*p;
-    register char	*q;
-    register const TABLE	*tp;
-    int			i;
-    int			abbrev;
-
-    /* Make it lowercase. */
-    for (p = buff; *p; p++)
-	if (isupper(*p))
-	    *p = tolower(*p);
-
-    if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
-	yylval.Meridian = MERam;
-	return tMERIDIAN;
-    }
-    if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
-	yylval.Meridian = MERpm;
-	return tMERIDIAN;
-    }
-
-    /* See if we have an abbreviation for a month. */
-    if (strlen(buff) == 3)
-	abbrev = 1;
-    else if (strlen(buff) == 4 && buff[3] == '.') {
-	abbrev = 1;
-	buff[3] = '\0';
-    }
-    else
-	abbrev = 0;
-
-    for (tp = MonthDayTable; tp->name; tp++) {
-	if (abbrev) {
-	    if (strncmp(buff, tp->name, 3) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-	}
-	else if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-    }
-
-    for (tp = TimezoneTable; tp->name; tp++)
-	if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-
-    if (strcmp(buff, "dst") == 0) 
-	return tDST;
-
-    for (tp = UnitsTable; tp->name; tp++)
-	if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-
-    /* Strip off any plural and try the units table again. */
-    i = strlen(buff) - 1;
-    if (buff[i] == 's') {
-	buff[i] = '\0';
-	for (tp = UnitsTable; tp->name; tp++)
-	    if (strcmp(buff, tp->name) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-	buff[i] = 's';		/* Put back for "this" in OtherTable. */
-    }
-
-    for (tp = OtherTable; tp->name; tp++)
-	if (strcmp(buff, tp->name) == 0) {
-	    yylval.Number = tp->value;
-	    return tp->type;
-	}
-
-    /* Military timezones. */
-    if (buff[1] == '\0' && isalpha(*buff)) {
-	for (tp = MilitaryTable; tp->name; tp++)
-	    if (strcmp(buff, tp->name) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-    }
-
-    /* Drop out any periods and try the timezone table again. */
-    for (i = 0, p = q = buff; *q; q++)
-	if (*q != '.')
-	    *p++ = *q;
-	else
-	    i++;
-    *p = '\0';
-    if (i)
-	for (tp = TimezoneTable; tp->name; tp++)
-	    if (strcmp(buff, tp->name) == 0) {
-		yylval.Number = tp->value;
-		return tp->type;
-	    }
-
-    return tID;
-}
-
-
-static int
-yylex()
-{
-    register char	c;
-    register char	*p;
-    char		buff[20];
-    int			Count;
-    int			sign;
-
-    for ( ; ; ) {
-	while (isspace(*yyInput))
-	    yyInput++;
-
-	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
-	    if (c == '-' || c == '+') {
-		sign = c == '-' ? -1 : 1;
-		if (!isdigit(*++yyInput))
-		    /* skip the '-' sign */
-		    continue;
-	    }
-	    else
-		sign = 0;
-	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
-		yylval.Number = 10 * yylval.Number + c - '0';
-	    yyInput--;
-	    if (sign < 0)
-		yylval.Number = -yylval.Number;
-	    return sign ? tSNUMBER : tUNUMBER;
-	}
-	if (isalpha(c)) {
-	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
-		if (p < &buff[sizeof buff - 1])
-		    *p++ = c;
-	    *p = '\0';
-	    yyInput--;
-	    return LookupWord(buff);
-	}
-	if (c != '(')
-	    return *yyInput++;
-	Count = 0;
-	do {
-	    c = *yyInput++;
-	    if (c == '\0')
-		return c;
-	    if (c == '(')
-		Count++;
-	    else if (c == ')')
-		Count--;
-	} while (Count > 0);
-    }
-}
-
-#define TM_YEAR_ORIGIN 1900
-
-/* Yield A - B, measured in seconds.  */
-static long
-difftm (struct tm *a, struct tm *b)
-{
-  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
-  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
-  int days = (
-	      /* difference in day of year */
-	      a->tm_yday - b->tm_yday
-	      /* + intervening leap days */
-	      +  ((ay >> 2) - (by >> 2))
-	      -  (ay/100 - by/100)
-	      +  ((ay/100 >> 2) - (by/100 >> 2))
-	      /* + difference in years * 365 */
-	      +  (long)(ay-by) * 365
-	      );
-  return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
-	      + (a->tm_min - b->tm_min))
-	  + (a->tm_sec - b->tm_sec));
-}
-
-time_t
-svn_parse_date(char *p, struct getdate_time *now)
-{
-    struct tm		*tm, gmt;
-    struct getdate_time	ftz;
-    time_t		Start;
-    time_t		tod;
-    time_t nowtime;
-
-    yyInput = p;
-    if (now == NULL) {
-	struct tm *gmt_ptr;
-
-        now = &ftz;
-	(void)time (&nowtime);
-
-	gmt_ptr = gmtime (&nowtime);
-	if (gmt_ptr != NULL)
-	{
-	    /* Make a copy, in case localtime modifies *tm (I think
-	       that comment now applies to *gmt_ptr, but I am too
-	       lazy to dig into how gmtime and locatime allocate the
-	       structures they return pointers to).  */
-	    gmt = *gmt_ptr;
-	}
-
-	if (! (tm = localtime (&nowtime)))
-	    return -1;
-
-	if (gmt_ptr != NULL)
-	    ftz.timezone = difftm (&gmt, tm) / 60;
-	else
-	    /* We are on a system like VMS, where the system clock is
-	       in local time and the system has no concept of timezones.
-	       Hopefully we can fake this out (for the case in which the
-	       user specifies no timezone) by just saying the timezone
-	       is zero.  */
-	    ftz.timezone = 0;
-
-	if(tm->tm_isdst)
-	    ftz.timezone += 60;
-    }
-    else
-    {
-	nowtime = now->time;
-    }
-
-    tm = localtime(&nowtime);
-    yyYear = tm->tm_year + 1900;
-    yyMonth = tm->tm_mon + 1;
-    yyDay = tm->tm_mday;
-    yyTimezone = now->timezone;
-    yyDSTmode = DSTmaybe;
-    yyHour = 0;
-    yyMinutes = 0;
-    yySeconds = 0;
-    yyMeridian = MER24;
-    yyRelSeconds = 0;
-    yyRelMonth = 0;
-    yyHaveDate = 0;
-    yyHaveDay = 0;
-    yyHaveRel = 0;
-    yyHaveTime = 0;
-    yyHaveZone = 0;
-
-    if (yyparse()
-     || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
-	return -1;
-
-    if (yyHaveDate || yyHaveTime || yyHaveDay) {
-	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
-		    yyMeridian, yyDSTmode);
-	if (Start < 0)
-	    return -1;
-    }
-    else {
-	Start = nowtime;
-	if (!yyHaveRel)
-	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
-    }
-
-    Start += yyRelSeconds;
-    Start += RelativeMonth(Start, yyRelMonth);
-
-    if (yyHaveDay && !yyHaveDate) {
-	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
-	Start += tod;
-    }
-
-    /* Have to do *something* with a legitimate -1 so it's distinguishable
-     * from the error return value.  (Alternately could set errno on error.) */
-    return Start == -1 ? 0 : Start;
-}
-
-
-#if	defined(TEST)
-
-/* ARGSUSED */
-int
-main(int ac, char *av[])
-{
-    char	buff[128];
-    time_t	d;
-
-    (void)printf("Enter date, or blank line to exit.\n\t> ");
-    (void)fflush(stdout);
-    while (gets(buff) && buff[0]) {
-	d = svn_parse_date(buff, (struct getdate_time *)NULL);
-	if (d == -1)
-	    (void)printf("Bad format - couldn't convert.\n");
-	else
-	    (void)printf("%s", ctime(&d));
-	(void)printf("\t> ");
-	(void)fflush(stdout);
-    }
-    exit(0);
-    /* NOTREACHED */
-}
-#endif	/* defined(TEST) */
Index: subversion/libsvn_subr/date.c
===================================================================
--- subversion/libsvn_subr/date.c	(revision 0)
+++ subversion/libsvn_subr/date.c	(revision 0)
@@ -0,0 +1,194 @@
+#include <apr_lib.h>
+#include <svn_time.h>
+
+/* Possible return values from char_match() */
+typedef enum {
+  match_none,     /* no match */
+  match_simple,   /* direct match from template char to value char */
+  match_skip,     /* potential skip */
+  match_complete, /* match at end-of-template/end-of-value */
+} match_type_t;
+
+
+/* Determine how a particular character VCHAR matches the current
+   template character TCHAR.
+
+   Legal template characters are:
+
+   YMDhmsH: match any digit
+   :-TZ and space: match themselves
+   +: matches '-' and '+'
+   .: matches '.' and ','
+   ?: matches either the end-of-value or
+      causes the special "match_skip" match.  */
+
+static match_type_t
+char_match (int tchar, int vchar)
+{
+  switch (tchar)
+    {
+    case 0:
+      return vchar ? match_none : match_complete;
+
+    case 'Y': case 'M': case 'D':
+    case 'h': case 'm': case 's':
+    case 'H': 
+      return apr_isascii (vchar) && apr_isdigit (vchar)? match_simple :
+                                                         match_none;
+    case ':': case '-': case 'T':
+    case ' ': case 'Z':
+      return vchar == tchar? match_simple : match_none;
+
+    case '.':
+      return vchar == '.' || vchar == ',' ? match_simple : match_none;
+
+    case '+':
+      return vchar == '-' || vchar == '+' ? match_simple : match_none;
+
+    case '?':
+      return vchar ? match_skip : match_complete;
+
+    default:
+      return match_none;
+    }
+}
+
+/* Attempt to match the date-string in VALUE to provided TEMPLATE.
+   As each character is matched, the corresponding OPCODE is applied
+   using the corresponding OPERAND.  The matched values are store in
+   RESULTS, the first COUNT integers of which are zeroed at entry.
+   Return TRUE iff the VALUE matches the TEMPLATE.
+
+   Template/Value matching is described above.  Legal opcodes
+   are:
+
+   'A': Append VALUE to results[OPERAND].
+   'M': Add VALUE fractional seconds into results[OPERAND].
+   'S': Set results[OPERAND] to VALUE.
+   '?': If the next VALUE and TEMPLATE char match then continue
+        parsing, otherwise advance the parse state to the next
+        occurance of OPERAND in TEMPLATE.  Unspecified behaviour
+        results if there is no such occurance.
+   ' ': Do nothing.  */
+
+static svn_boolean_t
+template_match (const char *value, const char *template, const char *opcodes,
+                const char *operands, int *results, int count)
+{
+  int multiplier = 100000;
+  char tchar, vchar, opcode;
+  int operand;
+  int skip;
+
+  memset (results, 0, count * sizeof (*results));
+
+  for (;;)
+    {
+      tchar = *template++;
+      opcode = *opcodes++;
+      operand = *operands++;
+      vchar = *value;
+
+      switch (char_match (tchar, vchar))
+        {
+        case match_complete:
+          return TRUE;
+        case match_none:
+          return FALSE;
+        case match_simple:
+          value++;
+          break;
+        case match_skip:
+          break;
+        }
+
+      switch (opcode)
+        {
+        case 'A': 
+          results[operand - '0'] = results[operand - '0'] * 10 + vchar - '0';
+          break;
+        case 'M':
+          results[operand - '0'] += (vchar - '0') * multiplier;
+          multiplier /= 10;
+          break;
+        case 'S':
+          results[operand - '0'] = vchar;
+          break;
+        case '?':
+          if (char_match (*template, *value) == match_simple)
+            break;
+          skip = strchr (template, operand) - template;
+          template += skip;
+          opcodes += skip;
+          operands += skip;
+        }
+    }
+}
+
+#define RESULT_COUNT 10
+
+svn_boolean_t
+svn_parse_date (const char *value, apr_time_t now, apr_int32_t gmtoff,
+                apr_time_t *val)
+{
+  apr_time_exp_t exp;
+  int results[RESULT_COUNT];
+
+  memset(&exp, 0, sizeof (exp));
+ 
+  if (template_match (value, /* try ISO-8601 extended */
+                      "YYYY-MM-DD?Thh?:mm?:ss?.s?s?s?s?s?s?Z?+HH?:MM",
+                      "AAAA AA AA? AA? AA? AA? M?M?M?M?M?M?S SAA  AA",
+                      "0000 11 22Z 33Z 44Z 55Z 6Z6Z6Z6Z6Z6Z7 788  99",
+                      results, RESULT_COUNT)
+      || template_match (value, /* try ISO-8601 basic */
+                         "YYYYMMDD?Thh?mm?ss?.s?s?s?s?s?s?Z?+HH?MM",
+                         "AAAAAAAA? AA?AA?AA? M?M?M?M?M?M?S SAA AA",
+                         "00001122Z 33Z44Z55Z 6Z6Z6Z6Z6Z6Z7 788 99",
+                         results, RESULT_COUNT)
+      || template_match (value, /* try the "svn log" format */
+                         "YYYY-MM-DD hh:mm:ss?.s?s?s?s?s?s? +HHMM",
+                         "AAAA AA AA AA AA AA? M?M?M?M?M?M  SAAAA",
+                         "0000 11 22 33 44 55  6 6 6 6 6 6  78899",
+                         results, RESULT_COUNT))
+    {
+      exp.tm_year = results[0] - 1900;
+      exp.tm_mon = results[1] - 1;
+      exp.tm_mday = results[2];
+    }
+  else if (template_match (value, /* try just a time */
+                           "hh:mm?:ss?.s?s?s?s?s?s",
+                           "AA AA  AA  M M M M M M",
+                           "33 44  55  6 6 6 6 6 6",
+                           results, RESULT_COUNT))
+    {
+      apr_time_exp_t expnow;
+      if (apr_time_exp_tz (&expnow, now, gmtoff) != APR_SUCCESS)
+        return FALSE;
+      exp.tm_year = expnow.tm_year;
+      exp.tm_mon = expnow.tm_mon;
+      exp.tm_mday = expnow.tm_mday;
+    }
+  else
+    return FALSE;
+
+  exp.tm_hour = results[3];
+  exp.tm_min = results[4];
+  exp.tm_sec = results[5];
+  exp.tm_usec = results[6];
+
+  switch (results[7])
+    {
+    case 0:
+      exp.tm_gmtoff = gmtoff;
+      break;
+    case '+':
+      exp.tm_gmtoff = results[8]*3600 + results[9]*60; 
+      break;
+    case '-':
+      exp.tm_gmtoff = -(results[8]*3600 + results[9]*60); 
+      break;
+    }
+
+  return apr_time_exp_gmt_get (val, &exp) == APR_SUCCESS;
+}
Index: autogen.sh
===================================================================
--- autogen.sh	(revision 8041)
+++ autogen.sh	(working copy)
@@ -70,23 +70,6 @@
 # any old aclocal.m4 left over from prior build so it doesn't cause errors.
 rm -f aclocal.m4
 
-# Produce getdate.c from getdate.y.
-# Again, this means that "developers" who run autogen.sh need either
-# yacc or bison -- but not people who compile sourceballs, since `make
-# dist` will include getdate.c.
-echo "Creating getdate.c..."
-bison -o subversion/libsvn_subr/getdate.c subversion/libsvn_subr/getdate.y
-if [ $? -ne 0 ]; then
-    yacc -o subversion/libsvn_subr/getdate.c subversion/libsvn_subr/getdate.y
-    if [ $? -ne 0 ]; then
-        echo
-        echo "   Error:  can't find either bison or yacc."
-        echo "   One of these is needed to generate the date parser."
-        echo
-        exit 1
-    fi
-fi
-
 # Create the file detailing all of the build outputs for SVN.
 #
 # Note: this dependency on Python is fine: only SVN developers use autogen.sh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Greg Hudson <gh...@MIT.EDU>.
Okay, I have read this carefully and can find nothing significant to
object to in this code.  I'm ready to vote +1 for it as a 1.0 candidate.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Greg Hudson <gh...@MIT.EDU>.
On Thu, 2004-01-01 at 12:20, Max Bowsher wrote:
> Well, it is the format used by cvs in "cvs log" output. So cvs users are
> more likely to be familiar with this farmat than any of the myriad other
> formats cvs allows.

Ah, right.  Interesting factual tidbit: CVS didn't used to accept this
date format, even though "cvs log" used it.  (I remember sending a bug
report about that.)  getdate.y as included in gnats added support for it
with the comment:

        /* 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.  */

In my mind, that comment contributes evidence that YYYY/MM/DD is a
marginalized date format from a human point of view, but you're right
that the specific class of CVS and RCS users is more likely to be used
to it.

I think I still lean towards "punt it for now, and see if we notice
evidence that people mind."  With global revision numbers, dated
revisions don't have as many applications as they do in CVS anyway.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Max Bowsher <ma...@ukf.net>.
Julian Reschke wrote:
> Max Bowsher wrote:
>
>> If YYYY/MM/DD is cheap to support, I think svn should support it -
because
>> cvs supports it, and svn aim to replace cvs.
>
> The same can be said about almost every other format. This particular
> format seems to be a particulary bad idea as it isn't a standard
> anywhere, right?

Well, it is the format used by cvs in "cvs log" output. So cvs users are
more likely to be familiar with this farmat than any of the myriad other
formats cvs allows.

> Besides, I thought the whole point was to start with the international
> standard format - ISO8601 - and to move everything else into post-1.0
> releases?

My apologies, I haven't been following the thread very closely - must have
missed that.
In which case, the question is, is the fact that cvs log uses this format
enough to make this format worthwhile for 1.0 despite this.

Max.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Julian Reschke <ju...@gmx.de>.
Max Bowsher wrote:

> If YYYY/MM/DD is cheap to support, I think svn should support it - because
> cvs supports it, and svn aim to replace cvs.

The same can be said about almost every other format. This particular 
format seems to be a particulary bad idea as it isn't a standard 
anywhere, right?

Besides, I thought the whole point was to start with the international 
standard format - ISO8601 - and to move everything else into post-1.0 
releases?

Julian

-- 
<green/>bytes GmbH -- http://www.greenbytes.de -- tel:+492512807760

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Max Bowsher <ma...@ukf.net>.
Julian Reschke wrote:
> Juanma Barranquero wrote:
>
>> On Wed, 31 Dec 2003 10:02:49 +0000, Colin Watson
<cj...@flatline.org.uk>
>> wrote:
>>
>>
>>> I often use YYYY/MM/DD because I like the freedom from
>>> ambiguity but sometimes forget exactly what the ISO separator is.
>>
>>
>> I've used it, because I'm used at '/' as a date separator, and I like
>> the fact that YYYY/MM/DD can be easily sorted (in logs, for example).
>
> Oh well. ISO8601 can be sorted the same way. Please stick to standard
> formats instead of inventing new ones.

If YYYY/MM/DD is cheap to support, I think svn should support it - because
cvs supports it, and svn aim to replace cvs.

Max.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Julian Reschke <ju...@gmx.de>.
Juanma Barranquero wrote:

> On Wed, 31 Dec 2003 10:02:49 +0000, Colin Watson <cj...@flatline.org.uk> wrote:
> 
> 
>>I often use YYYY/MM/DD because I like the freedom from
>>ambiguity but sometimes forget exactly what the ISO separator is.
> 
> 
> I've used it, because I'm used at '/' as a date separator, and I like
> the fact that YYYY/MM/DD can be easily sorted (in logs, for example).

Oh well. ISO8601 can be sorted the same way. Please stick to standard 
formats instead of inventing new ones.

Julian

-- 
<green/>bytes GmbH -- http://www.greenbytes.de -- tel:+492512807760

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Juanma Barranquero <le...@mi.madritel.es>.
On Wed, 31 Dec 2003 10:02:49 +0000, Colin Watson <cj...@flatline.org.uk> wrote:

> I often use YYYY/MM/DD because I like the freedom from
> ambiguity but sometimes forget exactly what the ISO separator is.

I've used it, because I'm used at '/' as a date separator, and I like
the fact that YYYY/MM/DD can be easily sorted (in logs, for example).

                                                           /L/e/k/t/u


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Julian Reschke <ju...@gmx.de>.
Colin Watson wrote:
> I'd been under the impression that YYYY/MM/DD was a commonly used date
> format in Japan, but I could be wrong there. Nevertheless, it's
> unambiguous so seems safe to support; I've never heard of anyone using
> YYYY/DD/MM. I often use YYYY/MM/DD because I like the freedom from
> ambiguity but sometimes forget exactly what the ISO separator is.

In which case it would be better to remember that it is "-" once and 
stop inventing new formats :-).

Julian

-- 
<green/>bytes GmbH -- http://www.greenbytes.de -- tel:+492512807760

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by SASAKI Suguru <sa...@sonik.org>.
On Wed, 31 Dec 2003 10:02:49 +0000, Colin Watson wrote:
> I'd been under the impression that YYYY/MM/DD was a commonly used date
> format in Japan, but I could be wrong there.

Yes, here in Japan, YYYY/MM/DD (and YY/MM/DD) is a very common date format.

I'm, as a Japanese, +1 for supporting YYYY/MM/DD.

But isn't locale-specific-ness a little problem?
If other locale-specific date formats will not be supported in 1.0,
I think, YYYY/MM/DD need not to be supported, too.
(Support in post 1.0 is appropriate.)


Regards,

=====
SASAKI Suguru
  mailto:sasaki {at} sonik.org

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Colin Watson <cj...@flatline.org.uk>.
On Tue, Dec 30, 2003 at 05:15:17PM -0500, Greg Hudson wrote:
> Oh, right.  The other thing I wanted to note is that I don't think
> YYYY/MM/DD is terribly common.  YYYY-MM-DD is pretty much the standard
> for people who care about precision and internationalization of date
> formats, and MM/DD/YY(YY) or DD/MM/YY(YY) is the "vernacular" standard
> for Americans and Europeans who don't worry about misinterpretation by
> people from other cultures.
> 
> Just my gut feeling; if people have evidence to the contrary, I'm all
> ears.

I'd been under the impression that YYYY/MM/DD was a commonly used date
format in Japan, but I could be wrong there. Nevertheless, it's
unambiguous so seems safe to support; I've never heard of anyone using
YYYY/DD/MM. I often use YYYY/MM/DD because I like the freedom from
ambiguity but sometimes forget exactly what the ISO separator is.

Cheers,

-- 
Colin Watson                                  [cjwatson@flatline.org.uk]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Andrew Snare <aj...@pigpond.com>.
Greg Hudson <gh...@MIT.EDU> wrote:
> Oh, right.  The other thing I wanted to note is that I don't think
> YYYY/MM/DD is terribly common.  YYYY-MM-DD is pretty much the standard
> for people who care about precision and internationalization of date
> formats, and MM/DD/YY(YY) or DD/MM/YY(YY) is the "vernacular" standard
> for Americans and Europeans who don't worry about misinterpretation by
> people from other cultures.
> 
> Just my gut feeling; if people have evidence to the contrary, I'm all
> ears.

On Tue, Dec 30, 2003 at 03:27:46PM -0600, kfogel@collab.net wrote:
> Hmm.  We have different intuitions, but I have no evidence either way.

I'd concur with Greg; to avoid a culture clash, people who care
about being unambiguous use the format defined by ISO 8601. Although
almost no-one supports the full gamut of what the spec allows, there
are strong arguments for using a subset of it. Rather than re-iterate
them here, take a peek at <http://www.cl.cam.ac.uk/~mgk25/iso-time.html>
for an excellent overview.

 - Andrew

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by kf...@collab.net.
Greg Hudson <gh...@MIT.EDU> writes:
> Oh, right.  The other thing I wanted to note is that I don't think
> YYYY/MM/DD is terribly common.  YYYY-MM-DD is pretty much the standard
> for people who care about precision and internationalization of date
> formats, and MM/DD/YY(YY) or DD/MM/YY(YY) is the "vernacular" standard
> for Americans and Europeans who don't worry about misinterpretation by
> people from other cultures.
> 
> Just my gut feeling; if people have evidence to the contrary, I'm all
> ears.

Hmm.  We have different intuitions, but I have no evidence either way.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Greg Hudson <gh...@MIT.EDU>.
Oh, right.  The other thing I wanted to note is that I don't think
YYYY/MM/DD is terribly common.  YYYY-MM-DD is pretty much the standard
for people who care about precision and internationalization of date
formats, and MM/DD/YY(YY) or DD/MM/YY(YY) is the "vernacular" standard
for Americans and Europeans who don't worry about misinterpretation by
people from other cultures.

Just my gut feeling; if people have evidence to the contrary, I'm all
ears.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by mark benedetto king <mb...@lowlatency.com>.
On Tue, Dec 30, 2003 at 05:07:29PM -0500, Greg Hudson wrote:
> 
> Just noticed a naming bug, incidentally; the constant MILLI should
> probably be named MICRO instead.
> 

How embarassing!  At least the template character was right...

--ben

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Greg Hudson <gh...@MIT.EDU>.
On Tue, 2003-12-30 at 15:08, kfogel@collab.net wrote:
> I'm a bit mystified by the casting of &ms to get base, and by the
> pointer arithmetic here.

This is how you deal with structure offsets in C.  I had initially
suggested that mbk should write a function which maps a template
character to an apr_int32_t * using a case statement or a ? :
expression, in which case you could use more conventional C constructs,
but the table approach has its elegance too.  But, since you can't write
&exp->tm_usec in a table initializer (in C89, at least), you have to use
the byte offset instead.

A comment might be appropriate, since this is a little-used feature of
C.

Just noticed a naming bug, incidentally; the constant MILLI should
probably be named MICRO instead.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by kf...@collab.net.
mark benedetto king <mb...@lowlatency.com> writes:
> > The function returns boolean *and* error -- why both?  (I'm sure
> > there's a good reason, just the doc string should explain the error
> > conditions and how they relate to 'matched'.)
> 
> This is in order to distinguish between parse-errors and internal errors,
> like not being able to convert an exploded date into an apr_time_t.
> 
> This could be accomplished by peeking at the error type, but I thought
> it would be cleaner this way.  I tend to think of SVN_ERR as a sort of
> ad-hoc structured exception handling mechanism, and try to use it that
> way.

Thanks -- can something like this go in the doc string, then?

> I'll make the necessary book changes and include them in an updated
> patch.

Awesome.

> How about:
> 
> "Use GMTOFF to bias the results when no timezone indicator is present
> in VALUE."

Sounds good to me.

> Will update patch.
> 
> > > +  char *base = (char *)&ms;
> > > +
>  [...]
> > > +      place = (apr_int32_t *)(base + match->offset);
> > 
> > I'm a bit mystified by the casting of &ms to get base, and by the
> > pointer arithmetic here.  Probably I could puzzle it out, but a
> > comment explaining what's going on might be good.  My endianness bells
> > started ringing when I saw the assignment to place above... (I'm sure
> > you've thought of that and there's nothing to worry about, just wonder
> > how many other people's bells will go off.)
> 
> The offsets that come from APR_OFFSETOF are in bytes.  This means that
> we need to use a char * in order to get the right results when doing
> the pointer arithmetic.  I don't think endianness comes into play here;
> the pointer to the base of the apr_int32_t should be the same either way.

That, or even even more detailed version of it, would make a great
comment in the file.

> > > +        case NOOP:
> > > +          continue;
> > 
> > Am I correct in understanding that this permits repeats of NOOP
> > characters?  For example, a template of "YYYY-MM" would allow
> > "2004----01", and "YYYY MM" would allow "2004    01"?  (This is a 
> > good thing, just want to make sure I understand correctly.)
> 
> No, because the template pointer is unconditionally incremented
> on each iteration of the match loop.

Oh, right.

So, do you want to allow (say) multiple whitespace in a row?

> > Are we starting out deliberately very strict, with intent to loosen up
> > and allow more formats later?
> 
> Yes (and that's why TABs are not supported in place of spaces).  These
> formats are essentially the ISO-8601 extended and basic date-time formats,
> plus (for completeness and convenience of cut-n-pasting) the format from
> "svn log", plus (for less typing) a time-only specification.
> 
> > I'm wondering about "YYYY/MM/DD", "YY/MM/DD", for example.
> > 
> 
> YYYY/MM/DD is locale-specific (I think).  YY/MM/DD is locale-specific and
> ambiguous, since it looks quite a bit like both DD/MM/YY and MM/DD/YY.

How does replacing hyphens with slashes make something
locale-specific?  (You probably mean, there's some standard out there
which declares that hyphens are for ISO-8601, and slashes are for
locales.  I'm talking about what people do in practice.  I don't think
most people think differently about a date just because they used
slashes instead of dashes.  It's just as easy to reverse days and
months no matter what the separator char is.)

> I suppose we could just pick one and document it as Our Standard, though.

Nah, why treat them differently?  Just declare that we always
interpret months before days, and then allow any common sep char.

> > I'd put more parens for precedence clarity in there, but there are
> > others who feel that's weak-kneed and lily-livered.  Your call.
> 
> There are already more parentheses than necessary in that condition. :-)

Well, I see where you stand :-).

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by mark benedetto king <mb...@lowlatency.com>.
On Tue, Dec 30, 2003 at 02:08:21PM -0600, kfogel@collab.net wrote:
> mark benedetto king <mb...@lowlatency.com> writes:
> > +svn_error_t *
> > +svn_parse_date (svn_boolean_t *matched, apr_time_t *val, const char *value,
> > +                apr_time_t now, apr_int32_t gmtoff, apr_pool_t *pool);
> 
> A couple of interface comments:
> 
> It's confusing that the input is named "value" and the output "val".
> Maybe choose clearer names?

Good idea.

> 
> The function returns boolean *and* error -- why both?  (I'm sure
> there's a good reason, just the doc string should explain the error
> conditions and how they relate to 'matched'.)
> 

This is in order to distinguish between parse-errors and internal errors,
like not being able to convert an exploded date into an apr_time_t.

This could be accomplished by peeking at the error type, but I thought
it would be cleaner this way.  I tend to think of SVN_ERR as a sort of
ad-hoc structured exception handling mechanism, and try to use it that
way.

> And a documentation comment: Obviously, we'll need to update the book
> and any other place where we document date formats.  I understand this
> patch is just about implementation, and that you're intentionally
> waiting on the other stuff.  I'm just noting it here so we don't

I'll make the necessary book changes and include them in an updated
patch.

> neglect it.  (Glad you got the INSTALL file, though -- I'd totally
> forgotten about that.)
> 

ISTR that C. Scott Ananian remembered in his original patch.  Otherwise
I might have forgotten as well.

> Code comments below.  It's extremely clean & well-designed, was really
> a pleasure to read, by the way.

Thanks, it was a pleasure to write.

 [snip]
> For space, should we match TAB (ascii 9) as well in the second field?
 [snip]
> No "/" permitted, I see.  Is it atypical to want that?  (More on this
> below.)
> 

More on this below.

> > +/* Attempt to match the date-string in VALUE to the provided TEMPLATE,
> > +   using the rules defined above.  Use GMTOFF is to bias the results
> > +   when no timezone indicator is present.  On successful match, store
> > +   the matched values in EXP, and return TRUE.  Otherwise, return
> > +   FALSE. */
> 
> Second sentence of doc string unclear, extra word somewhere?  Also,
> store matched values in "*EXP", not "EXP", right?  (I know that's
> nitpicky, but might as well be consistent with elsewhere.)

How about:

"Use GMTOFF to bias the results when no timezone indicator is present
in VALUE."

Will update patch.

> > +  char *base = (char *)&ms;
> > +
 [...]
> > +      place = (apr_int32_t *)(base + match->offset);
> 
> I'm a bit mystified by the casting of &ms to get base, and by the
> pointer arithmetic here.  Probably I could puzzle it out, but a
> comment explaining what's going on might be good.  My endianness bells
> started ringing when I saw the assignment to place above... (I'm sure
> you've thought of that and there's nothing to worry about, just wonder
> how many other people's bells will go off.)

The offsets that come from APR_OFFSETOF are in bytes.  This means that
we need to use a char * in order to get the right results when doing
the pointer arithmetic.  I don't think endianness comes into play here;
the pointer to the base of the apr_int32_t should be the same either way.

> > +        case NOOP:
> > +          continue;
> 
> Am I correct in understanding that this permits repeats of NOOP
> characters?  For example, a template of "YYYY-MM" would allow
> "2004----01", and "YYYY MM" would allow "2004    01"?  (This is a 
> good thing, just want to make sure I understand correctly.)

No, because the template pointer is unconditionally incremented
on each iteration of the match loop.

> 
> Are we starting out deliberately very strict, with intent to loosen up
> and allow more formats later?
> 

Yes (and that's why TABs are not supported in place of spaces).  These
formats are essentially the ISO-8601 extended and basic date-time formats,
plus (for completeness and convenience of cut-n-pasting) the format from
"svn log", plus (for less typing) a time-only specification.

> I'm wondering about "YYYY/MM/DD", "YY/MM/DD", for example.
> 

YYYY/MM/DD is locale-specific (I think).  YY/MM/DD is locale-specific and
ambiguous, since it looks quite a bit like both DD/MM/YY and MM/DD/YY.

I suppose we could just pick one and document it as Our Standard, though.

> > +  if (exp.tm_mon == 1
> > +      && exp.tm_mday == 29
> > +      && (exp.tm_year % 4 != 0
> > +          || (exp.tm_year % 100 == 0 && exp.tm_year % 400 != 100)))
> > +    return SVN_NO_ERROR;
> 
> I'd put more parens for precedence clarity in there, but there are
> others who feel that's weak-kneed and lily-livered.  Your call.
> 

There are already more parentheses than necessary in that condition. :-)

--ben


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by kf...@collab.net.
mark benedetto king <mb...@lowlatency.com> writes:
> +/** Convert a human-readable date @a value into an @c apr_time_t, using
> + * @a now as the current time and @a gmtoff as an indicator of
> + * seconds east of GMT, storing the result in @a val.  Set @a matched
> + * to indicate whether or not @a value was parsed successfully.  Perform
> + * any allocation in @a pool.
>   */
> -time_t svn_parse_date (char *text, struct getdate_time *now);
> +svn_error_t *
> +svn_parse_date (svn_boolean_t *matched, apr_time_t *val, const char *value,
> +                apr_time_t now, apr_int32_t gmtoff, apr_pool_t *pool);

A couple of interface comments:

It's confusing that the input is named "value" and the output "val".
Maybe choose clearer names?

The function returns boolean *and* error -- why both?  (I'm sure
there's a good reason, just the doc string should explain the error
conditions and how they relate to 'matched'.)

And a documentation comment: Obviously, we'll need to update the book
and any other place where we document date formats.  I understand this
patch is just about implementation, and that you're intentionally
waiting on the other stuff.  I'm just noting it here so we don't
neglect it.  (Glad you got the INSTALL file, though -- I'd totally
forgotten about that.)

Code comments below.  It's extremely clean & well-designed, was really
a pleasure to read, by the way.

> Index: subversion/libsvn_subr/date.c
> ===================================================================
> --- subversion/libsvn_subr/date.c	(revision 0)
> +++ subversion/libsvn_subr/date.c	(revision 0)
> @@ -0,0 +1,228 @@
> +#include <apr_lib.h>
> +#include <svn_time.h>
> +
> +/* Valid rule actions */
> +enum rule_action {
> +  ACCUM,    /* Accumulate a decimal value */
> +  MILLI,    /* Accumulate milliseconds */
> +  TZIND,    /* Handle +, -, Z */
> +  NOOP,     /* Do nothing */
> +  SKIPFROM, /* If at end-of-value, accept the match.  Otherwise,
> +               if the next template character matches the current
> +               value character, continue processing as normal.
> +               Otherwise, attempt to complete matching starting
> +               immediately after the first subsequent occurrance of
> +               ']' in the template. */
> +  SKIP,     /* Ignore this template character */
> +  ACCEPT    /* Accept the value */
> +};
> +
> +/* How to handle a particular character in a template */
> +typedef struct
> +{
> +  char key;                /* The template char that this rule matches */
> +  const char *valid;       /* String of valid chars for this rule */
> +  enum rule_action action; /* What action to take when the rule is matched */
> +  int offset;              /* Where to store the any results of the action,
> +                              relative to the base of an apr_time_exp_t */
> +} rule;
> +
> +
> +/* The parsed values, before localtime/gmt processing */
> +typedef struct
> +{
> +  apr_time_exp_t base;
> +  apr_int32_t offhours;
> +  apr_int32_t offminutes;
> +} match_state;
> +
> +#define DIGITS "0123456789"
> +
> +/* A declarative specification of how each template character
> +   should be processed, using a rule for each valid symbol. */
> +static const rule
> +rules[] =
> +{
> +  { 'Y', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_year) },
> +  { 'M', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_mon) },
> +  { 'D', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_mday) },
> +  { 'h', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_hour) },
> +  { 'm', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_min) },
> +  { 's', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_sec) },
> +  { 'u', DIGITS, MILLI, APR_OFFSETOF (match_state, base.tm_usec) },
> +  { 'O', DIGITS, ACCUM, APR_OFFSETOF (match_state, offhours) },
> +  { 'o', DIGITS, ACCUM, APR_OFFSETOF (match_state, offminutes) },
> +  { '+', "-+", TZIND, 0 },
> +  { 'Z', "Z", TZIND, 0 },
> +  { ':', ":", NOOP, 0 },
> +  { '-', "-", NOOP, 0 },
> +  { 'T', "T", NOOP, 0 },
> +  { ' ', " ", NOOP, 0 },

For space, should we match TAB (ascii 9) as well in the second field?

> +  { '.', ".,", NOOP, 0 },
> +  { '[', NULL, SKIPFROM, 0 },
> +  { ']', NULL, SKIP, 0 },
> +  { '\0', "", ACCEPT, 0 },
> +};

No "/" permitted, I see.  Is it atypical to want that?  (More on this
below.)

> +/* Return the rule associated with TCHAR, or NULL if there
> +   is no such rule */
> +static const rule *
> +find_rule (char tchar)
> +{
> +  int i = sizeof (rules)/sizeof (rules[0]);
> +  while (i--)
> +    if (rules[i].key == tchar)
> +      return &rules[i]; 
> +  return NULL;
> +}
> +
> +/* Attempt to match the date-string in VALUE to the provided TEMPLATE,
> +   using the rules defined above.  Use GMTOFF is to bias the results
> +   when no timezone indicator is present.  On successful match, store
> +   the matched values in EXP, and return TRUE.  Otherwise, return
> +   FALSE. */

Second sentence of doc string unclear, extra word somewhere?  Also,
store matched values in "*EXP", not "EXP", right?  (I know that's
nitpicky, but might as well be consistent with elsewhere.)

> +static svn_boolean_t
> +template_match (apr_time_exp_t *exp, const char *template, const char *value,
> +                apr_int32_t gmtoff)
> +{
> +  int multiplier = 100000;
> +  int tzind = 0;
> +  match_state ms;
> +  char *base = (char *)&ms;
> +
> +  memset (&ms, 0, sizeof (ms));
> +
> +  for (;;)
> +    {
> +      const rule *match = find_rule (*template++);
> +      char vchar = *value++;
> +      apr_int32_t *place;
> +
> +      if (!match || (match->valid && !strchr (match->valid, vchar)))
> +        return FALSE;
> +
> +      place = (apr_int32_t *)(base + match->offset);

I'm a bit mystified by the casting of &ms to get base, and by the
pointer arithmetic here.  Probably I could puzzle it out, but a
comment explaining what's going on might be good.  My endianness bells
started ringing when I saw the assignment to place above... (I'm sure
you've thought of that and there's nothing to worry about, just wonder
how many other people's bells will go off.)

> +      switch (match->action)
> +        {
> +        case ACCUM:
> +          *place = *place * 10 + vchar - '0';
> +          continue;
> +        case MILLI:
> +          *place += (vchar - '0') * multiplier;
> +          multiplier /= 10;
> +          continue;
> +        case TZIND:
> +          tzind = vchar;
> +          continue;
> +        case SKIP:
> +          value--;
> +          continue;
> +        case NOOP:
> +          continue;
> +        case SKIPFROM:
> +          if (!vchar)
> +            break;
> +          match = find_rule (*template);
> +          if (!strchr (match->valid, vchar))
> +            template = strchr (template, ']') + 1;
> +          value--;
> +          continue;
> +        case ACCEPT:
> +          break;
> +        }

Am I correct in understanding that this permits repeats of NOOP
characters?  For example, a template of "YYYY-MM" would allow
"2004----01", and "YYYY MM" would allow "2004    01"?  (This is a 
good thing, just want to make sure I understand correctly.)

> +      break;
> +    }
> +
> +  switch (tzind)
> +    {
> +    case 0:
> +      ms.base.tm_gmtoff = gmtoff;
> +      break;
> +    case '+':
> +      ms.base.tm_gmtoff = ms.offhours * 3600 + ms.offminutes * 60;
> +      break;
> +    case '-':
> +      ms.base.tm_gmtoff = -(ms.offhours * 3600 + ms.offminutes * 60);
> +      break;
> +    }
> +
> +  *exp = ms.base;
> +  return TRUE;
> +}
> +
> +static int
> +valid_days_by_month[] = {
> +  31, 29, 31, 30,
> +  31, 30, 31, 31,
> +  30, 31, 30, 31
> +};

Heh.  I saw the "29" and wondered, do we want to automatically parse
for leap year violations, since it's a fixed formula?  Then I searched
forward in the patch for "leap" and saw that you do :-).

> +svn_error_t *
> +svn_parse_date (svn_boolean_t *matched, apr_time_t *val, const char *value,
> +                apr_time_t now, apr_int32_t gmtoff, apr_pool_t *pool)
> +{
> +  apr_time_exp_t exp;
> +  apr_status_t apr_err; 
> +
> +  *matched = FALSE;
> + 
> +  if (template_match (&exp, /* try ISO-8601 extended, UTC */
> +                      "YYYY-MM-DD[Thh[:mm[:ss[.u[u[u[u[u[u][Z]",
> +                      value, gmtoff)
> +      || template_match (&exp, /* try ISO-8601 extended, with offset */
> +                         "YYYY-MM-DD[Thh[:mm[:ss[.u[u[u[u[u[u]+oo[:oo]",
> +                         value, gmtoff)
> +      || template_match (&exp, /* try ISO-8601 basic, UTC */
> +                         "YYYYMMDD[Thh[mm[ss[.u[u[u[u[u[u][Z]",
> +                         value, gmtoff)
> +      || template_match (&exp, /* try ISO-8601 basic, with offset */
> +                         "YYYYMMDD[Thh[mm[ss[.u[u[u[u[u[u]+oo[oo]",
> +                         value, gmtoff)
> +      || template_match (&exp, /* try the "svn log" format */
> +                         "YYYY-MM-DD hh:mm:ss[.u[u[u[u[u[u][ +oo[oo]",
> +                         value, gmtoff))

Are we starting out deliberately very strict, with intent to loosen up
and allow more formats later?

I'm wondering about "YYYY/MM/DD", "YY/MM/DD", for example.

I realize you posted a long mail explaining about formats, and I
didn't bring this up then :-).  I just figured it would be easy to
discuss once we had the patch, since it would obviously be a minor
code tweak (and clearly it is).

> +    {
> +      exp.tm_year -= 1900;
> +      exp.tm_mon -= 1;
> +    }
> +  else if (template_match (&exp, /* try just a time */
> +                           "hh:mm[:ss[.u[u[u[u[u[u]",
> +                           value, gmtoff))
> +    {
> +      apr_time_exp_t expnow;
> +      apr_err = apr_time_exp_tz (&expnow, now, gmtoff);
> +      if (apr_err != APR_SUCCESS)
> +        return svn_error_wrap_apr (apr_err, "Can't manipulate current date");
> +      exp.tm_year = expnow.tm_year;
> +      exp.tm_mon = expnow.tm_mon;
> +      exp.tm_mday = expnow.tm_mday;
> +    }
> +  else
> +    return SVN_NO_ERROR;
> +
> +  /* range validation, allowing for leap seconds */
> +  if (exp.tm_mon > 11
> +      || exp.tm_mday > valid_days_by_month[exp.tm_mon]
> +      || exp.tm_hour > 23
> +      || exp.tm_min > 59
> +      || exp.tm_sec > 60  
> +      || abs (exp.tm_gmtoff) > 86399
> +      || (abs (exp.tm_gmtoff/60)%60) > 59)
> +    return SVN_NO_ERROR;
> +
> +  /* february/leap-year day checking.  tm_year is bias-1900, so centuries
> +     that equal 100 (mod 400) are multiples of 400. */
> +  if (exp.tm_mon == 1
> +      && exp.tm_mday == 29
> +      && (exp.tm_year % 4 != 0
> +          || (exp.tm_year % 100 == 0 && exp.tm_year % 400 != 100)))
> +    return SVN_NO_ERROR;

I'd put more parens for precedence clarity in there, but there are
others who feel that's weak-kneed and lily-livered.  Your call.

I'm not going to +1 it for 1.0, as you know I wasn't in favor of doing
this in 1.0.  But I certainly won't veto it, and perhaps a small,
secret part of my heart hopes it goes in :-).  It's a heck of a lot
cleaner and more manageable than what we've currently got.

-Karl

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by mark benedetto king <mb...@lowlatency.com>.
On Sun, Dec 21, 2003 at 12:42:24AM -0500, mark benedetto king wrote:
> Here's a quick patch for review.
> 

Here's an updated one, with an overhaul^W^Wimprovements based on
feedback from Brane and Greg Hudson.  To save bandwidth, I have not
included the diffs for the deleted files.

Remove the getdate.y-based date-parser, replacing it with a simpler
though less powerful one.

* subversion/libsvn_subr/date.c: New file.  
* subversion/libsvn_subr/getdate.y: Deleted.
* subversion/libsvn_subr/getdate.cw: Deleted.
* subversion/include/svn_time.h:
  (struct getdate_time): Removed.
  (svn_parse_date): Update prototype and documentation comment to reflect
   the new interface.
* subversion/libsvn_subr/opt.c
  (parse_one_rev): Add pool parameter, and use the new svn_parse_date
   interface.
  (svn_opt_parse_revision): Pass pool into parse_one_rev().
* autogen.sh:
  Remove generation of getdate.c from getdate.y.
* INSTALL:
  Remove bison/yacc dependency, renumber subsequent dependencies.


Index: subversion/include/svn_time.h
===================================================================
--- subversion/include/svn_time.h	(revision 8109)
+++ subversion/include/svn_time.h	(working copy)
@@ -50,17 +50,15 @@
 const char *svn_time_to_human_cstring (apr_time_t when, apr_pool_t *pool);
 
 
-/** Needed by @c getdate.y parser. */
-struct getdate_time {
-  time_t time;
-  short timezone;
-};
-
-/** The one interface in our @c getdate.y parser; convert
- * human-readable date @a text into a standard C @c time_t.  The 2nd
- * argument is unused; we always pass @c NULL.
+/** Convert a human-readable date @a value into an @c apr_time_t, using
+ * @a now as the current time and @a gmtoff as an indicator of
+ * seconds east of GMT, storing the result in @a val.  Set @a matched
+ * to indicate whether or not @a value was parsed successfully.  Perform
+ * any allocation in @a pool.
  */
-time_t svn_parse_date (char *text, struct getdate_time *now);
+svn_error_t *
+svn_parse_date (svn_boolean_t *matched, apr_time_t *val, const char *value,
+                apr_time_t now, apr_int32_t gmtoff, apr_pool_t *pool);
 
 
 /** Sleep until the next second, to ensure that any files modified
Index: subversion/libsvn_subr/opt.c
===================================================================
--- subversion/libsvn_subr/opt.c	(revision 8109)
+++ subversion/libsvn_subr/opt.c	(working copy)
@@ -288,25 +288,41 @@
 
 /* Parse one revision specification.  Return pointer to character
    after revision, or NULL if the revision is invalid.  Modifies
-   str, so make sure to pass a copy of anything precious. */
-static char *parse_one_rev (svn_opt_revision_t *revision, char *str)
+   str, so make sure to pass a copy of anything precious.  Uses
+   POOL for temporary allocation. */
+static char *parse_one_rev (svn_opt_revision_t *revision, char *str,
+                            apr_pool_t *pool)
 {
   char *end, save;
-  time_t tm;
 
   if (*str == '{')
     {
+      apr_time_t now;
+      apr_time_exp_t nowexp;
+      svn_boolean_t matched;
+      apr_time_t tm;
+      svn_error_t *err;
+
       /* Brackets denote a date. */
       str++;
       end = strchr (str, '}');
       if (!end)
         return NULL;
       *end = '\0';
-      tm = svn_parse_date (str, NULL);
-      if (tm == -1)
+      now = apr_time_now();
+      if (apr_time_exp_lt (&nowexp, now) != APR_SUCCESS)
         return NULL;
+      err = svn_parse_date (&matched, &tm, str, now, nowexp.tm_gmtoff,
+                            pool);
+      if (err)
+        {
+          svn_error_clear (err);
+          return NULL;
+        }
+      if (!matched)
+        return NULL; 
       revision->kind = svn_opt_revision_date;
-      apr_time_ansi_put (&(revision->value.date), tm);
+      revision->value.date = tm;
       return end + 1;
     }
   else if (apr_isdigit (*str))
@@ -350,11 +366,11 @@
   /* Operate on a copy of the argument. */
   left_rev = apr_pstrdup (pool, arg);
 
-  right_rev = parse_one_rev (start_revision, left_rev);
+  right_rev = parse_one_rev (start_revision, left_rev, pool);
   if (right_rev && *right_rev == ':')
     {
       right_rev++;
-      end = parse_one_rev (end_revision, right_rev);
+      end = parse_one_rev (end_revision, right_rev, pool);
       if (!end || *end != '\0')
         return -1;
     }
Index: subversion/libsvn_subr/date.c
===================================================================
--- subversion/libsvn_subr/date.c	(revision 0)
+++ subversion/libsvn_subr/date.c	(revision 0)
@@ -0,0 +1,228 @@
+#include <apr_lib.h>
+#include <svn_time.h>
+
+/* Valid rule actions */
+enum rule_action {
+  ACCUM,    /* Accumulate a decimal value */
+  MILLI,    /* Accumulate milliseconds */
+  TZIND,    /* Handle +, -, Z */
+  NOOP,     /* Do nothing */
+  SKIPFROM, /* If at end-of-value, accept the match.  Otherwise,
+               if the next template character matches the current
+               value character, continue processing as normal.
+               Otherwise, attempt to complete matching starting
+               immediately after the first subsequent occurrance of
+               ']' in the template. */
+  SKIP,     /* Ignore this template character */
+  ACCEPT    /* Accept the value */
+};
+
+/* How to handle a particular character in a template */
+typedef struct
+{
+  char key;                /* The template char that this rule matches */
+  const char *valid;       /* String of valid chars for this rule */
+  enum rule_action action; /* What action to take when the rule is matched */
+  int offset;              /* Where to store the any results of the action,
+                              relative to the base of an apr_time_exp_t */
+} rule;
+
+
+/* The parsed values, before localtime/gmt processing */
+typedef struct
+{
+  apr_time_exp_t base;
+  apr_int32_t offhours;
+  apr_int32_t offminutes;
+} match_state;
+
+#define DIGITS "0123456789"
+
+/* A declarative specification of how each template character
+   should be processed, using a rule for each valid symbol. */
+static const rule
+rules[] =
+{
+  { 'Y', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_year) },
+  { 'M', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_mon) },
+  { 'D', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_mday) },
+  { 'h', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_hour) },
+  { 'm', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_min) },
+  { 's', DIGITS, ACCUM, APR_OFFSETOF (match_state, base.tm_sec) },
+  { 'u', DIGITS, MILLI, APR_OFFSETOF (match_state, base.tm_usec) },
+  { 'O', DIGITS, ACCUM, APR_OFFSETOF (match_state, offhours) },
+  { 'o', DIGITS, ACCUM, APR_OFFSETOF (match_state, offminutes) },
+  { '+', "-+", TZIND, 0 },
+  { 'Z', "Z", TZIND, 0 },
+  { ':', ":", NOOP, 0 },
+  { '-', "-", NOOP, 0 },
+  { 'T', "T", NOOP, 0 },
+  { ' ', " ", NOOP, 0 },
+  { '.', ".,", NOOP, 0 },
+  { '[', NULL, SKIPFROM, 0 },
+  { ']', NULL, SKIP, 0 },
+  { '\0', "", ACCEPT, 0 },
+};
+
+/* Return the rule associated with TCHAR, or NULL if there
+   is no such rule */
+static const rule *
+find_rule (char tchar)
+{
+  int i = sizeof (rules)/sizeof (rules[0]);
+  while (i--)
+    if (rules[i].key == tchar)
+      return &rules[i]; 
+  return NULL;
+}
+
+/* Attempt to match the date-string in VALUE to the provided TEMPLATE,
+   using the rules defined above.  Use GMTOFF is to bias the results
+   when no timezone indicator is present.  On successful match, store
+   the matched values in EXP, and return TRUE.  Otherwise, return
+   FALSE. */
+static svn_boolean_t
+template_match (apr_time_exp_t *exp, const char *template, const char *value,
+                apr_int32_t gmtoff)
+{
+  int multiplier = 100000;
+  int tzind = 0;
+  match_state ms;
+  char *base = (char *)&ms;
+
+  memset (&ms, 0, sizeof (ms));
+
+  for (;;)
+    {
+      const rule *match = find_rule (*template++);
+      char vchar = *value++;
+      apr_int32_t *place;
+
+      if (!match || (match->valid && !strchr (match->valid, vchar)))
+        return FALSE;
+
+      place = (apr_int32_t *)(base + match->offset);
+      switch (match->action)
+        {
+        case ACCUM:
+          *place = *place * 10 + vchar - '0';
+          continue;
+        case MILLI:
+          *place += (vchar - '0') * multiplier;
+          multiplier /= 10;
+          continue;
+        case TZIND:
+          tzind = vchar;
+          continue;
+        case SKIP:
+          value--;
+          continue;
+        case NOOP:
+          continue;
+        case SKIPFROM:
+          if (!vchar)
+            break;
+          match = find_rule (*template);
+          if (!strchr (match->valid, vchar))
+            template = strchr (template, ']') + 1;
+          value--;
+          continue;
+        case ACCEPT:
+          break;
+        }
+
+      break;
+    }
+
+  switch (tzind)
+    {
+    case 0:
+      ms.base.tm_gmtoff = gmtoff;
+      break;
+    case '+':
+      ms.base.tm_gmtoff = ms.offhours * 3600 + ms.offminutes * 60;
+      break;
+    case '-':
+      ms.base.tm_gmtoff = -(ms.offhours * 3600 + ms.offminutes * 60);
+      break;
+    }
+
+  *exp = ms.base;
+  return TRUE;
+}
+
+static int
+valid_days_by_month[] = {
+  31, 29, 31, 30,
+  31, 30, 31, 31,
+  30, 31, 30, 31
+};
+
+svn_error_t *
+svn_parse_date (svn_boolean_t *matched, apr_time_t *val, const char *value,
+                apr_time_t now, apr_int32_t gmtoff, apr_pool_t *pool)
+{
+  apr_time_exp_t exp;
+  apr_status_t apr_err; 
+
+  *matched = FALSE;
+ 
+  if (template_match (&exp, /* try ISO-8601 extended, UTC */
+                      "YYYY-MM-DD[Thh[:mm[:ss[.u[u[u[u[u[u][Z]",
+                      value, gmtoff)
+      || template_match (&exp, /* try ISO-8601 extended, with offset */
+                         "YYYY-MM-DD[Thh[:mm[:ss[.u[u[u[u[u[u]+oo[:oo]",
+                         value, gmtoff)
+      || template_match (&exp, /* try ISO-8601 basic, UTC */
+                         "YYYYMMDD[Thh[mm[ss[.u[u[u[u[u[u][Z]",
+                         value, gmtoff)
+      || template_match (&exp, /* try ISO-8601 basic, with offset */
+                         "YYYYMMDD[Thh[mm[ss[.u[u[u[u[u[u]+oo[oo]",
+                         value, gmtoff)
+      || template_match (&exp, /* try the "svn log" format */
+                         "YYYY-MM-DD hh:mm:ss[.u[u[u[u[u[u][ +oo[oo]",
+                         value, gmtoff))
+    {
+      exp.tm_year -= 1900;
+      exp.tm_mon -= 1;
+    }
+  else if (template_match (&exp, /* try just a time */
+                           "hh:mm[:ss[.u[u[u[u[u[u]",
+                           value, gmtoff))
+    {
+      apr_time_exp_t expnow;
+      apr_err = apr_time_exp_tz (&expnow, now, gmtoff);
+      if (apr_err != APR_SUCCESS)
+        return svn_error_wrap_apr (apr_err, "Can't manipulate current date");
+      exp.tm_year = expnow.tm_year;
+      exp.tm_mon = expnow.tm_mon;
+      exp.tm_mday = expnow.tm_mday;
+    }
+  else
+    return SVN_NO_ERROR;
+
+  /* range validation, allowing for leap seconds */
+  if (exp.tm_mon > 11
+      || exp.tm_mday > valid_days_by_month[exp.tm_mon]
+      || exp.tm_hour > 23
+      || exp.tm_min > 59
+      || exp.tm_sec > 60  
+      || abs (exp.tm_gmtoff) > 86399
+      || (abs (exp.tm_gmtoff/60)%60) > 59)
+    return SVN_NO_ERROR;
+
+  /* february/leap-year day checking.  tm_year is bias-1900, so centuries
+     that equal 100 (mod 400) are multiples of 400. */
+  if (exp.tm_mon == 1
+      && exp.tm_mday == 29
+      && (exp.tm_year % 4 != 0
+          || (exp.tm_year % 100 == 0 && exp.tm_year % 400 != 100)))
+    return SVN_NO_ERROR;
+
+  apr_err = apr_time_exp_gmt_get (val, &exp);
+  if (apr_err != APR_SUCCESS)
+    return svn_error_wrap_apr (apr_err, "Can't calculate requested date");
+
+  *matched = TRUE;
+  return SVN_NO_ERROR;
+}
Index: INSTALL
===================================================================
--- INSTALL	(revision 8109)
+++ INSTALL	(working copy)
@@ -154,25 +154,8 @@
       newer. The autogen.sh script knows about that.
 
 
-      4.  bison or yacc (Unix only)
+      4.  Neon library 0.24.4 (http://www.webdav.org/neon/)
 
-      This is required only if you plan to build from the latest source
-      (see section II.B), which you probably want to do. See above.
-
-      The reason one of these programs is required is that it will
-      generate the code which parses complex date formats, so that
-      Subversion can work with dates like "yesterday" and "last month"
-      and "four hours ago". Note that most modern Unices come with one
-      or the other of these programs, and only one is required.
-
-      The reason you don't need one of these programs on a Windows
-      platform is that the date parsing file has been pregenerated
-      and will automatically be copied into place by the Windows
-      Build.
-
-
-      5.  Neon library 0.24.4 (http://www.webdav.org/neon/)
-
       The Neon library allows a Subversion client to interact with remote
       repositories over the Internet via a WebDAV based protocol.  If you 
       want to use Subversion to connect to a server over ra_dav (via a 
@@ -197,7 +180,7 @@
       subdirectory beneath wherever "--with-neon" is pointed.
 
 
-      6.  Berkeley DB 4.2.52
+      5.  Berkeley DB 4.2.52
 
       Berkeley DB is needed to build a Subversion server, or to access
       a repository on local disk.  If you are only interested in
@@ -225,7 +208,7 @@
           http://subversion.tigris.org/servlets/ProjectDocumentList
 
 
-      7.  Apache Web Server 2.0.48 or newer
+      6.  Apache Web Server 2.0.48 or newer
           (http://httpd.apache.org/download.cgi)
 
       The Apache HTTP server is required if you wish to offer your
@@ -236,7 +219,7 @@
       done: See section III for details.
 
 
-      8.  Python 2.0 (http://www.python.org/)
+      7.  Python 2.0 (http://www.python.org/)
 
       If you want to run "make check" or build from the latest source
       under Unix as described in section II.B and III.D, install
@@ -245,21 +228,21 @@
       system.
 
 
-      9.  Visual C++ 6.0 or newer (Windows Only)
+      8.  Visual C++ 6.0 or newer (Windows Only)
 
       To build Subversion under any of the MS Windows platforms, you
       will need a copy of Microsoft Visual C++.  You can generate the
       project files using the gen-make.py script.
 
 
-      10. Perl 5.8 or newer (Windows only)
+      9. Perl 5.8 or newer (Windows only)
 
       To build Subversion under any of the MS Windows platforms, you
       will also need Perl 5.8 or newer to run apr-util's w32locatedb.pl
       script.
 
 
-      11. Libraries for our libraries
+      10. Libraries for our libraries
 
       Some of the libraries that Subversion depends on themselves have
       optional dependencies that can add features to what Subversion
@@ -321,7 +304,7 @@
       libraries.
 
 
-      12. Building The Documentation
+      11. Building The Documentation
 
       The master source format for Subversion's documentation is
       Docbook Lite.  See doc/book/README for instructions how to
Index: autogen.sh
===================================================================
--- autogen.sh	(revision 8109)
+++ autogen.sh	(working copy)
@@ -70,23 +70,6 @@
 # any old aclocal.m4 left over from prior build so it doesn't cause errors.
 rm -f aclocal.m4
 
-# Produce getdate.c from getdate.y.
-# Again, this means that "developers" who run autogen.sh need either
-# yacc or bison -- but not people who compile sourceballs, since `make
-# dist` will include getdate.c.
-echo "Creating getdate.c..."
-bison -o subversion/libsvn_subr/getdate.c subversion/libsvn_subr/getdate.y
-if [ $? -ne 0 ]; then
-    yacc -o subversion/libsvn_subr/getdate.c subversion/libsvn_subr/getdate.y
-    if [ $? -ne 0 ]; then
-        echo
-        echo "   Error:  can't find either bison or yacc."
-        echo "   One of these is needed to generate the date parser."
-        echo
-        exit 1
-    fi
-fi
-
 # Create the file detailing all of the build outputs for SVN.
 #
 # Note: this dependency on Python is fine: only SVN developers use autogen.sh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Greg Hudson <gh...@MIT.EDU>.
I'm going to wait until the next pass to review carefully for details,
but you need to document your template parser a bit better, in my
opinion.  The meaning of '?' in a template isn't very clear, nor do you
say that '?' in opcodes must correspond to a '?' in template.  And I
cannot figure out what a 'Z' in the operands field means.

You might also consider simplifying the matcher interface by punting the
operands parameter, accepting an apr_time_exp_t * parameter instead of
an integer results array, and using the template character to decide
which field to apply the results to.  A helper function could map
<apr_time_exp_t *, char> to <apr_int32_t *> to decide what structure
field to operate on, since all of the apr_time_exp_t fields have the
same type.  (Except, you'd have to deal with skipping differently. 
Hmm.)


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Branko Čibej <br...@xbc.nu>.
mark benedetto king wrote:

>On Sun, Dec 21, 2003 at 07:28:59AM +0100, Branko ??ibej wrote:
>  
>
>>>+  if (template_match (value, /* try ISO-8601 extended */
>>>+                      "YYYY-MM-DD?Thh?:mm?:ss?.s?s?s?s?s?s?Z?+HH?:MM",
>>>+                      "AAAA AA AA? AA? AA? AA? M?M?M?M?M?M?S SAA  AA",
>>>+                      "0000 11 22Z 33Z 44Z 55Z 6Z6Z6Z6Z6Z6Z7 788  99",
>>>+                      results, RESULT_COUNT)
>>> 
>>>
>>>      
>>>
>>Won't this match "yyyy-mm-dd hh:mm:ssZ+hh:mm"? If I understand the
>>template logic correctly, you're allowed to use both the magic "Z" UTC
>>indicator and an explicit offset at the same time.
>>    
>>
>
>Yes.  The sad thing is that I did it intentionally after misreading my
>own proposal based on the ISO 8601:2000 document.  Will fix, but at
>first glance this will require a change to the matcher, not just the
>template.
>  
>
I think using two different match templates, one with "Z?" and one with
"+HH?:MM" would do the trick, wouldn't it? No need to complicate the
matcher.

-- 
Brane Čibej   <br...@xbc.nu>   http://www.xbc.nu/brane/

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by mark benedetto king <mb...@lowlatency.com>.
On Sun, Dec 21, 2003 at 07:28:59AM +0100, Branko ??ibej wrote:
> >-time_t svn_parse_date (char *text, struct getdate_time *now);
> >+svn_boolean_t svn_parse_date (const char *text, apr_time_t now,
> >+                              apr_int32_t gmtoff, apr_time_t *val);
> >  
> >
> A few issues here:
> 
>     * We usually put output parameters first in the argument list.
>     * Wouldn't it be better for this function to return an svn_error_t*?
>       Even if you don't need it now, you almost certainly will once the
>       date parser is extended to support more formats (and L10N).
>     * The same argument suggests this function would need a pool someday.
> 

Good points, will fix.

> Cound we say "valid" here instead of "legal"? People might get the
> impression that typing an incorrect date might get them sent to jail. :-)
> 

You never know in this country.  Will fix, anyway. :-)

> [...]
> 
> >+/* Attempt to match the date-string in VALUE to provided TEMPLATE.
> >+   As each character is matched, the corresponding OPCODE is applied
> >+   using the corresponding OPERAND.  The matched values are store in
> >  
> >
>                                                                ^^^^^^ stored
> 

Good eye.

> 
> >+   RESULTS, the first COUNT integers of which are zeroed at entry.
> >+   Return TRUE iff the VALUE matches the TEMPLATE.
> >+
> >+   Template/Value matching is described above.  Legal opcodes
> >+   are:
> >  
> >
> legal->valid again
> 
> [...]
> 
> >+  if (template_match (value, /* try ISO-8601 extended */
> >+                      "YYYY-MM-DD?Thh?:mm?:ss?.s?s?s?s?s?s?Z?+HH?:MM",
> >+                      "AAAA AA AA? AA? AA? AA? M?M?M?M?M?M?S SAA  AA",
> >+                      "0000 11 22Z 33Z 44Z 55Z 6Z6Z6Z6Z6Z6Z7 788  99",
> >+                      results, RESULT_COUNT)
> >  
> >
> Won't this match "yyyy-mm-dd hh:mm:ssZ+hh:mm"? If I understand the
> template logic correctly, you're allowed to use both the magic "Z" UTC
> indicator and an explicit offset at the same time.

Yes.  The sad thing is that I did it intentionally after misreading my
own proposal based on the ISO 8601:2000 document.  Will fix, but at
first glance this will require a change to the matcher, not just the
template.

Also, I realize now that this implementation does not do any sanity
checking on the parsed values before handing them off to
apr_time_exp_gmt_get().  Will add that, too.

--ben


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: RFC: date parser strawman

Posted by Branko Čibej <br...@xbc.nu>.
mark benedetto king wrote:

>Here's a quick patch for review.
>
>Remove the getdate.y-based date-parser, replacing it with a simpler
>though less powerful one.
>  
>
[... ]

>+/** Convert human-readable date @a text into an @c apr_time_t, using
>+ * @a now as the current time and @a gmtoff as an indicator of
>+ * seconds east of GMT, storing the result in @a val.  Return
>+ * TRUE iff the date is parsed successfully.
>  */
>-time_t svn_parse_date (char *text, struct getdate_time *now);
>+svn_boolean_t svn_parse_date (const char *text, apr_time_t now,
>+                              apr_int32_t gmtoff, apr_time_t *val);
>  
>
A few issues here:

    * We usually put output parameters first in the argument list.
    * Wouldn't it be better for this function to return an svn_error_t*?
      Even if you don't need it now, you almost certainly will once the
      date parser is extended to support more formats (and L10N).
    * The same argument suggests this function would need a pool someday.


>Index: subversion/libsvn_subr/date.c
>===================================================================
>--- subversion/libsvn_subr/date.c	(revision 0)
>+++ subversion/libsvn_subr/date.c	(revision 0)
>@@ -0,0 +1,194 @@
>+#include <apr_lib.h>
>+#include <svn_time.h>
>+
>+/* Possible return values from char_match() */
>+typedef enum {
>+  match_none,     /* no match */
>+  match_simple,   /* direct match from template char to value char */
>+  match_skip,     /* potential skip */
>+  match_complete, /* match at end-of-template/end-of-value */
>+} match_type_t;
>+
>+
>+/* Determine how a particular character VCHAR matches the current
>+   template character TCHAR.
>+
>+   Legal template characters are:
>  
>
Cound we say "valid" here instead of "legal"? People might get the
impression that typing an incorrect date might get them sent to jail. :-)

[...]

>+/* Attempt to match the date-string in VALUE to provided TEMPLATE.
>+   As each character is matched, the corresponding OPCODE is applied
>+   using the corresponding OPERAND.  The matched values are store in
>  
>
                                                               ^^^^^^ stored


>+   RESULTS, the first COUNT integers of which are zeroed at entry.
>+   Return TRUE iff the VALUE matches the TEMPLATE.
>+
>+   Template/Value matching is described above.  Legal opcodes
>+   are:
>  
>
legal->valid again

[...]

>+  if (template_match (value, /* try ISO-8601 extended */
>+                      "YYYY-MM-DD?Thh?:mm?:ss?.s?s?s?s?s?s?Z?+HH?:MM",
>+                      "AAAA AA AA? AA? AA? AA? M?M?M?M?M?M?S SAA  AA",
>+                      "0000 11 22Z 33Z 44Z 55Z 6Z6Z6Z6Z6Z6Z7 788  99",
>+                      results, RESULT_COUNT)
>  
>
Won't this match "yyyy-mm-dd hh:mm:ssZ+hh:mm"? If I understand the
template logic correctly, you're allowed to use both the magic "Z" UTC
indicator and an explicit offset at the same time.


-- 
Brane Čibej   <br...@xbc.nu>   http://www.xbc.nu/brane/

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org