about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LYUtils.c255
-rw-r--r--src/LYmktime.c337
-rw-r--r--src/descrip.mms4
-rw-r--r--src/makefile.dos9
-rw-r--r--src/makefile.dsl9
-rw-r--r--src/makefile.in18
-rw-r--r--src/makefile.wsl9
-rw-r--r--src/parsdate.c1453
-rw-r--r--src/parsdate.h21
-rw-r--r--src/parsdate.y919
10 files changed, 2766 insertions, 268 deletions
diff --git a/src/LYUtils.c b/src/LYUtils.c
index 367222b1..806dfd06 100644
--- a/src/LYUtils.c
+++ b/src/LYUtils.c
@@ -1,4 +1,4 @@
-/* $LynxId: LYUtils.c,v 1.167 2008/02/17 18:30:19 ravenexp Exp $ */
+/* $LynxId: LYUtils.c,v 1.168 2008/06/29 21:44:03 tom Exp $ */
 #include <HTUtils.h>
 #include <HTTCP.h>
 #include <HTParse.h>
@@ -5526,259 +5526,6 @@ char *LYAddPathToSave(char *fname)
     return result;
 }
 
-/*
- * This function takes a string in the format
- *	"Mon, 01-Jan-96 13:45:35 GMT" or
- *	"Mon,  1 Jan 1996 13:45:35 GMT"" or
- *	"dd-mm-yyyy"
- * as an argument, and returns its conversion to clock format (seconds since
- * 00:00:00 Jan 1 1970), or 0 if the string doesn't match the expected pattern. 
- * It also returns 0 if the time is in the past and the "absolute" argument is
- * FALSE.  It is intended for handling 'expires' strings in Version 0 cookies
- * homologously to 'max-age' strings in Version 1 cookies, for which 0 is the
- * minimum, and greater values are handled as '[max-age seconds] + time(NULL)'. 
- * If "absolute" if TRUE, we return the clock format value itself, but if
- * anything goes wrong when parsing the expected patterns, we still return 0. 
- * - FM
- */
-time_t LYmktime(char *string,
-		BOOL absolute)
-{
-    char *s;
-    time_t now, clock2;
-    int day, month, year, hour, minutes, seconds;
-    char *start;
-    char temp[8];
-
-    /*
-     * Make sure we have a string to parse.  - FM
-     */
-    if (!non_empty(string))
-	return (0);
-    s = string;
-    CTRACE((tfp, "LYmktime: Parsing '%s'\n", s));
-
-    /*
-     * Skip any lead alphabetic "Day, " field and seek a numeric day field.  -
-     * FM
-     */
-    while (*s != '\0' && !isdigit(UCH(*s)))
-	s++;
-    if (*s == '\0')
-	return (0);
-
-    /*
-     * Get the numeric day and convert to an integer.  - FM
-     */
-    start = s;
-    while (*s != '\0' && isdigit(UCH(*s)))
-	s++;
-    if (*s == '\0' || (s - start) > 2)
-	return (0);
-    LYstrncpy(temp, start, (int) (s - start));
-    day = atoi(temp);
-    if (day < 1 || day > 31)
-	return (0);
-
-    /*
-     * Get the month string and convert to an integer.  - FM
-     */
-    while (*s != '\0' && !isalnum(UCH(*s)))
-	s++;
-    if (*s == '\0')
-	return (0);
-    start = s;
-    while (*s != '\0' && isalnum(UCH(*s)))
-	s++;
-    if ((*s == '\0') ||
-	(s - start) < (isdigit(UCH(*(s - 1))) ? 2 : 3) ||
-	(s - start) > (isdigit(UCH(*(s - 1))) ? 2 : 9))
-	return (0);
-    LYstrncpy(temp, start, (isdigit(UCH(*(s - 1))) ? 2 : 3));
-    switch (TOUPPER(temp[0])) {
-    case '0':
-    case '1':
-	month = atoi(temp);
-	if (month < 1 || month > 12) {
-	    return (0);
-	}
-	break;
-    case 'A':
-	if (!strcasecomp(temp, "Apr")) {
-	    month = 4;
-	} else if (!strcasecomp(temp, "Aug")) {
-	    month = 8;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'D':
-	if (!strcasecomp(temp, "Dec")) {
-	    month = 12;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'F':
-	if (!strcasecomp(temp, "Feb")) {
-	    month = 2;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'J':
-	if (!strcasecomp(temp, "Jan")) {
-	    month = 1;
-	} else if (!strcasecomp(temp, "Jun")) {
-	    month = 6;
-	} else if (!strcasecomp(temp, "Jul")) {
-	    month = 7;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'M':
-	if (!strcasecomp(temp, "Mar")) {
-	    month = 3;
-	} else if (!strcasecomp(temp, "May")) {
-	    month = 5;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'N':
-	if (!strcasecomp(temp, "Nov")) {
-	    month = 11;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'O':
-	if (!strcasecomp(temp, "Oct")) {
-	    month = 10;
-	} else {
-	    return (0);
-	}
-	break;
-    case 'S':
-	if (!strcasecomp(temp, "Sep")) {
-	    month = 9;
-	} else {
-	    return (0);
-	}
-	break;
-    default:
-	return (0);
-    }
-
-    /*
-     * Get the numeric year string and convert to an integer.  - FM
-     */
-    while (*s != '\0' && !isdigit(UCH(*s)))
-	s++;
-    if (*s == '\0')
-	return (0);
-    start = s;
-    while (*s != '\0' && isdigit(UCH(*s)))
-	s++;
-    if ((s - start) == 4) {
-	LYstrncpy(temp, start, 4);
-    } else if ((s - start) == 2) {
-	now = time(NULL);
-	/*
-	 * Assume that received 2-digit dates >= 70 are 19xx; others
-	 * are 20xx.  Only matters when dealing with broken software
-	 * (HTTP server or web page) which is not Y2K compliant.  The
-	 * line is drawn on a best-guess basis; it is impossible for
-	 * this to be completely accurate because it depends on what
-	 * the broken sender software intends.  (This totally breaks
-	 * in 2100 -- setting up the next crisis...) - BL
-	 */
-	if (atoi(start) >= 70)
-	    LYstrncpy(temp, "19", 2);
-	else
-	    LYstrncpy(temp, "20", 2);
-	strncat(temp, start, 2);
-	temp[4] = '\0';
-    } else {
-	return (0);
-    }
-    year = atoi(temp);
-
-    /*
-     * Get the numeric hour string and convert to an integer.  - FM
-     */
-    while (*s != '\0' && !isdigit(UCH(*s)))
-	s++;
-    if (*s == '\0') {
-	hour = 0;
-	minutes = 0;
-	seconds = 0;
-    } else {
-	start = s;
-	while (*s != '\0' && isdigit(UCH(*s)))
-	    s++;
-	if (*s != ':' || (s - start) > 2)
-	    return (0);
-	LYstrncpy(temp, start, (int) (s - start));
-	hour = atoi(temp);
-
-	/*
-	 * Get the numeric minutes string and convert to an integer.  - FM
-	 */
-	while (*s != '\0' && !isdigit(UCH(*s)))
-	    s++;
-	if (*s == '\0')
-	    return (0);
-	start = s;
-	while (*s != '\0' && isdigit(UCH(*s)))
-	    s++;
-	if (*s != ':' || (s - start) > 2)
-	    return (0);
-	LYstrncpy(temp, start, (int) (s - start));
-	minutes = atoi(temp);
-
-	/*
-	 * Get the numeric seconds string and convert to an integer.  - FM
-	 */
-	while (*s != '\0' && !isdigit(UCH(*s)))
-	    s++;
-	if (*s == '\0')
-	    return (0);
-	start = s;
-	while (*s != '\0' && isdigit(UCH(*s)))
-	    s++;
-	if (*s == '\0' || (s - start) > 2)
-	    return (0);
-	LYstrncpy(temp, start, (int) (s - start));
-	seconds = atoi(temp);
-    }
-
-    /*
-     * Convert to clock format (seconds since 00:00:00 Jan 1 1970), but then
-     * zero it if it's in the past and "absolute" is not TRUE.  - FM
-     */
-    month -= 3;
-    if (month < 0) {
-	month += 12;
-	year--;
-    }
-    day += (year - 1968) * 1461 / 4;
-    day += ((((month * 153) + 2) / 5) - 672);
-    clock2 = (time_t) ((day * 60 * 60 * 24) +
-		       (hour * 60 * 60) +
-		       (minutes * 60) +
-		       seconds);
-    if (absolute == FALSE && (long) (time((time_t *) 0) - clock2) >= 0)
-	clock2 = (time_t) 0;
-    if (clock2 > 0)
-	CTRACE((tfp, "LYmktime: clock=%ld, ctime=%s",
-		(long) clock2,
-		ctime(&clock2)));
-
-    return (clock2);
-}
-
 #if !defined(HAVE_PUTENV) && !defined(_WINDOWS)
 /*
  * No putenv on the NeXT so we use this code instead!
diff --git a/src/LYmktime.c b/src/LYmktime.c
new file mode 100644
index 00000000..dcec9bda
--- /dev/null
+++ b/src/LYmktime.c
@@ -0,0 +1,337 @@
+/* $LynxId: LYmktime.c,v 1.5 2008/06/30 23:49:30 tom Exp $ */
+
+#include <LYStrings.h>
+#include <LYUtils.h>
+
+#include <parsdate.h>
+
+#ifdef TEST_DRIVER
+char *LYstrncpy(char *dst,
+		const char *src,
+		int n)
+{
+    char *val;
+    int len;
+
+    if (src == 0)
+	src = "";
+    len = strlen(src);
+
+    if (n < 0)
+	n = 0;
+
+    val = strncpy(dst, src, n);
+    if (len < n)
+	*(dst + len) = '\0';
+    else
+	*(dst + n) = '\0';
+    return val;
+}
+#define strcasecomp strcasecmp
+BOOLEAN WWW_TraceFlag = FALSE;
+FILE *TraceFP(void)
+{
+    return stderr;
+}
+#define USE_PARSDATE 0
+#else
+#define USE_PARSDATE 1
+#endif
+
+/*
+ * This function takes a string in the format
+ *	"Mon, 01-Jan-96 13:45:35 GMT" or
+ *	"Mon,  1 Jan 1996 13:45:35 GMT" or
+ *	"dd-mm-yyyy"
+ * as an argument, and returns its conversion to clock format (seconds since
+ * 00:00:00 Jan 1 1970), or 0 if the string doesn't match the expected pattern. 
+ * It also returns 0 if the time is in the past and the "absolute" argument is
+ * FALSE.  It is intended for handling 'expires' strings in Version 0 cookies
+ * homologously to 'max-age' strings in Version 1 cookies, for which 0 is the
+ * minimum, and greater values are handled as '[max-age seconds] + time(NULL)'. 
+ * If "absolute" if TRUE, we return the clock format value itself, but if
+ * anything goes wrong when parsing the expected patterns, we still return 0. 
+ * - FM
+ */
+time_t LYmktime(char *string,
+		BOOL absolute)
+{
+#if USE_PARSDATE
+    time_t result;
+
+    if (non_empty(string)) {
+	CTRACE((tfp, "LYmktime: Parsing '%s'\n", string));
+	result = parsedate(string, 0);
+
+	if (!absolute) {
+	    if ((time((time_t *) 0) - result) >= 0)
+		result = 0;
+	}
+	if (result != 0) {
+	    CTRACE((tfp, "LYmktime: clock=%ld, ctime=%s",
+		    (long) result,
+		    ctime(&result)));
+	}
+    }
+    return result;
+#else
+    char *s;
+    time_t now, clock2;
+    int day, month, year, hour, minutes, seconds;
+    char *start;
+    char temp[8];
+
+    /*
+     * Make sure we have a string to parse.  - FM
+     */
+    if (!non_empty(string))
+	return (0);
+    s = string;
+    CTRACE((tfp, "LYmktime: Parsing '%s'\n", s));
+
+    /*
+     * Skip any lead alphabetic "Day, " field and seek a numeric day field.  -
+     * FM
+     */
+    while (*s != '\0' && !isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0')
+	return (0);
+
+    /*
+     * Get the numeric day and convert to an integer.  - FM
+     */
+    start = s;
+    while (*s != '\0' && isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0' || (s - start) > 2)
+	return (0);
+    LYstrncpy(temp, start, (int) (s - start));
+    day = atoi(temp);
+    if (day < 1 || day > 31)
+	return (0);
+
+    /*
+     * Get the month string and convert to an integer.  - FM
+     */
+    while (*s != '\0' && !isalnum(UCH(*s)))
+	s++;
+    if (*s == '\0')
+	return (0);
+    start = s;
+    while (*s != '\0' && isalnum(UCH(*s)))
+	s++;
+    if ((*s == '\0') ||
+	(s - start) < (isdigit(UCH(*(s - 1))) ? 2 : 3) ||
+	(s - start) > (isdigit(UCH(*(s - 1))) ? 2 : 9))
+	return (0);
+    LYstrncpy(temp, start, (isdigit(UCH(*(s - 1))) ? 2 : 3));
+    switch (TOUPPER(temp[0])) {
+    case '0':
+    case '1':
+	month = atoi(temp);
+	if (month < 1 || month > 12) {
+	    return (0);
+	}
+	break;
+    case 'A':
+	if (!strcasecomp(temp, "Apr")) {
+	    month = 4;
+	} else if (!strcasecomp(temp, "Aug")) {
+	    month = 8;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'D':
+	if (!strcasecomp(temp, "Dec")) {
+	    month = 12;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'F':
+	if (!strcasecomp(temp, "Feb")) {
+	    month = 2;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'J':
+	if (!strcasecomp(temp, "Jan")) {
+	    month = 1;
+	} else if (!strcasecomp(temp, "Jun")) {
+	    month = 6;
+	} else if (!strcasecomp(temp, "Jul")) {
+	    month = 7;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'M':
+	if (!strcasecomp(temp, "Mar")) {
+	    month = 3;
+	} else if (!strcasecomp(temp, "May")) {
+	    month = 5;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'N':
+	if (!strcasecomp(temp, "Nov")) {
+	    month = 11;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'O':
+	if (!strcasecomp(temp, "Oct")) {
+	    month = 10;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'S':
+	if (!strcasecomp(temp, "Sep")) {
+	    month = 9;
+	} else {
+	    return (0);
+	}
+	break;
+    default:
+	return (0);
+    }
+
+    /*
+     * Get the numeric year string and convert to an integer.  - FM
+     */
+    while (*s != '\0' && !isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0')
+	return (0);
+    start = s;
+    while (*s != '\0' && isdigit(UCH(*s)))
+	s++;
+    if ((s - start) == 4) {
+	LYstrncpy(temp, start, 4);
+    } else if ((s - start) == 2) {
+	now = time(NULL);
+	/*
+	 * Assume that received 2-digit dates >= 70 are 19xx; others
+	 * are 20xx.  Only matters when dealing with broken software
+	 * (HTTP server or web page) which is not Y2K compliant.  The
+	 * line is drawn on a best-guess basis; it is impossible for
+	 * this to be completely accurate because it depends on what
+	 * the broken sender software intends.  (This totally breaks
+	 * in 2100 -- setting up the next crisis...) - BL
+	 */
+	if (atoi(start) >= 70)
+	    LYstrncpy(temp, "19", 2);
+	else
+	    LYstrncpy(temp, "20", 2);
+	strncat(temp, start, 2);
+	temp[4] = '\0';
+    } else {
+	return (0);
+    }
+    year = atoi(temp);
+
+    /*
+     * Get the numeric hour string and convert to an integer.  - FM
+     */
+    while (*s != '\0' && !isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0') {
+	hour = 0;
+	minutes = 0;
+	seconds = 0;
+    } else {
+	start = s;
+	while (*s != '\0' && isdigit(UCH(*s)))
+	    s++;
+	if (*s != ':' || (s - start) > 2)
+	    return (0);
+	LYstrncpy(temp, start, (int) (s - start));
+	hour = atoi(temp);
+
+	/*
+	 * Get the numeric minutes string and convert to an integer.  - FM
+	 */
+	while (*s != '\0' && !isdigit(UCH(*s)))
+	    s++;
+	if (*s == '\0')
+	    return (0);
+	start = s;
+	while (*s != '\0' && isdigit(UCH(*s)))
+	    s++;
+	if (*s != ':' || (s - start) > 2)
+	    return (0);
+	LYstrncpy(temp, start, (int) (s - start));
+	minutes = atoi(temp);
+
+	/*
+	 * Get the numeric seconds string and convert to an integer.  - FM
+	 */
+	while (*s != '\0' && !isdigit(UCH(*s)))
+	    s++;
+	if (*s == '\0')
+	    return (0);
+	start = s;
+	while (*s != '\0' && isdigit(UCH(*s)))
+	    s++;
+	if (*s == '\0' || (s - start) > 2)
+	    return (0);
+	LYstrncpy(temp, start, (int) (s - start));
+	seconds = atoi(temp);
+    }
+
+    /*
+     * Convert to clock format (seconds since 00:00:00 Jan 1 1970), but then
+     * zero it if it's in the past and "absolute" is not TRUE.  - FM
+     */
+    month -= 3;
+    if (month < 0) {
+	month += 12;
+	year--;
+    }
+    day += (year - 1968) * 1461 / 4;
+    day += ((((month * 153) + 2) / 5) - 672);
+    clock2 = (time_t) ((day * 60 * 60 * 24) +
+		       (hour * 60 * 60) +
+		       (minutes * 60) +
+		       seconds);
+    if (absolute == FALSE && (long) (time((time_t *) 0) - clock2) >= 0)
+	clock2 = (time_t) 0;
+    if (clock2 > 0)
+	CTRACE((tfp, "LYmktime: clock=%ld, ctime=%s",
+		(long) clock2,
+		ctime(&clock2)));
+
+    return (clock2);
+#endif
+}
+
+#ifdef TEST_DRIVER
+static void test_mktime(char *source)
+{
+    time_t before = LYmktime(source, TRUE);
+    time_t after = parsedate(source, 0);
+
+    printf("TEST %s\n", source);
+    printf("\t%10ld  %s", (long) before, ctime(&before));
+    printf("\t%10ld  %s", (long) after, ctime(&after));
+    if (before != after)
+	printf("\t****\n");
+}
+
+int main(void)
+{
+    test_mktime("Mon, 01-Jan-96 13:45:35 GMT");
+    test_mktime("Mon,  1 Jan 1996 13:45:35 GMT");
+    test_mktime("31-12-1999");
+    test_mktime("Wed May 14 22:00:00 2008");
+    test_mktime("Sun, 29-Jun-2008 23:19:30 GMT");
+    test_mktime("Sun Jul 06 07:00:00 2008 GMT");
+    return 0;
+}
+#endif
diff --git a/src/descrip.mms b/src/descrip.mms
index 3fc03ff2..6b22b78d 100644
--- a/src/descrip.mms
+++ b/src/descrip.mms
@@ -1,3 +1,5 @@
+! $LynxId: descrip.mms,v 1.12 2008/06/30 23:50:22 tom Exp $
+!
 !       Make LYNX hypertext browser under VMS
 !       =====================================
 !
@@ -64,7 +66,7 @@ OBJS =	DefaultStyle.obj, GridText.obj, HTAlert.obj, HTFWriter.obj, -
 	LYMap.obj, LYNews.obj, LYOptions.obj, LYPrint.obj, LYReadCFG.obj, -
 	LYSearch.obj, LYShowInfo.obj, LYStrings.obj, LYTraversal.obj, -
 	LYUpload.obj, LYUtils.obj, LYexit.obj, LYrcFile.obj, TRSTable.obj, -
-	UCAuto.obj, UCAux.obj, UCdomap.obj
+	LYmktime.obj, UCAuto.obj, UCAux.obj, UCdomap.obj, parsdate.obj
 
 .ifdef SLANG
 SCREEN_DEF = USE_SLANG
diff --git a/src/makefile.dos b/src/makefile.dos
index 5aeee682..ed0358db 100644
--- a/src/makefile.dos
+++ b/src/makefile.dos
@@ -1,14 +1,15 @@
-# $LynxId: makefile.dos,v 1.30 2008/01/08 00:54:14 tom Exp $
+# $LynxId: makefile.dos,v 1.32 2008/06/30 23:53:42 tom Exp $
+
 OBJS= UCdomap.o UCAux.o UCAuto.o \
 LYClean.o LYShowInfo.o LYEdit.o LYStrings.o \
 LYMail.o HTAlert.o GridText.o LYGetFile.o \
-LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYUtils.o \
+LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYmktime.o LYUtils.o \
 LYOptions.o LYReadCFG.o LYSearch.o LYHistory.o LYSession.o \
 LYForms.o LYPrint.o LYrcFile.o LYDownload.o LYNews.o LYKeymap.o \
 HTML.o HTFWriter.o HTInit.o DefaultStyle.o LYLocal.o LYUpload.o \
 LYLeaks.o LYexit.o LYJump.o LYList.o LYCgi.o LYTraversal.o \
 LYEditmap.o LYCharSets.o LYCharUtils.o LYMap.o LYCookie.o LYExtern.o \
-LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o
+LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o parsdate.o
 
 CFLAGS= -O2 $(MCFLAGS) $(INTLFLAGS) -I. -I..
 
@@ -110,3 +111,5 @@ LYShowInfo.o:	../userdefs.h
 LYStrings.o:	../userdefs.h
 LYTraversal.o:	../userdefs.h
 LYUtils.o:	../userdefs.h
+LYmktime.o:	../userdefs.h
+parsdate.o:	../userdefs.h
diff --git a/src/makefile.dsl b/src/makefile.dsl
index beb0ccf6..64024f7f 100644
--- a/src/makefile.dsl
+++ b/src/makefile.dsl
@@ -1,14 +1,15 @@
-# $LynxId: makefile.dsl,v 1.17 2008/01/08 00:54:07 tom Exp $
+# $LynxId: makefile.dsl,v 1.19 2008/06/30 23:53:42 tom Exp $
+
 OBJS= UCdomap.o UCAux.o UCAuto.o \
 LYClean.o LYShowInfo.o LYEdit.o LYStrings.o \
 LYMail.o HTAlert.o GridText.o LYGetFile.o \
-LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYUtils.o \
+LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYmktime.o LYUtils.o \
 LYOptions.o LYReadCFG.o LYSearch.o LYHistory.o LYSession.o \
 LYForms.o LYPrint.o LYrcFile.o LYDownload.o LYNews.o LYKeymap.o \
 HTML.o HTFWriter.o HTInit.o DefaultStyle.o LYLocal.o LYUpload.o \
 LYLeaks.o LYexit.o LYJump.o LYList.o LYCgi.o LYTraversal.o \
 LYEditmap.o LYCharSets.o LYCharUtils.o LYMap.o LYCookie.o LYExtern.o \
-LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o
+LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o parsdate.o
 
 CFLAGS= -O2 $(MCFLAGS) $(INTLFLAGS) -I. -I.. $(SLANGINC)
 
@@ -100,3 +101,5 @@ LYShowInfo.o:	../userdefs.h
 LYStrings.o:	../userdefs.h
 LYTraversal.o:	../userdefs.h
 LYUtils.o:	../userdefs.h
+LYmktime.o:	../userdefs.h
+parsdate.o:	../userdefs.h
diff --git a/src/makefile.in b/src/makefile.in
index 5e024615..cd446f31 100644
--- a/src/makefile.in
+++ b/src/makefile.in
@@ -1,4 +1,4 @@
-# $LynxId: makefile.in,v 1.50 2007/05/23 00:19:09 tom Exp $
+# $LynxId: makefile.in,v 1.52 2008/06/30 23:36:34 tom Exp $
 # template-makefile for Lynx src directory
 
 SHELL		= @CONFIG_SHELL@
@@ -71,14 +71,14 @@ CHARTRANS_OBJS	= UCdomap$o UCAux$o UCAuto$o
 OBJS	= \
 	LYClean$o LYShowInfo$o LYEdit$o LYStrings$o LYMail$o \
 	HTAlert$o GridText$o LYGetFile$o LYMain$o LYMainLoop$o \
-	LYCurses$o LYBookmark$o LYUtils$o LYOptions$o \
+	LYCurses$o LYBookmark$o LYmktime$o LYUtils$o LYOptions$o \
 	LYReadCFG$o LYSearch$o LYHistory$o LYForms$o LYPrint$o \
 	LYrcFile$o LYDownload$o LYNews$o LYKeymap$o HTML$o \
 	HTFWriter$o HTInit$o DefaultStyle$o LYUpload$o \
 	LYLeaks$o LYexit$o LYJump$o LYList$o LYCgi$o \
 	LYTraversal$o LYEditmap$o LYCharSets$o LYCharUtils$o \
 	LYMap$o LYCookie$o LYStyle$o LYHash$o LYPrettySrc$o \
-	TRSTable$o $(CHARTRANS_OBJS) @EXTRA_OBJS@ @LIBOBJS@
+	TRSTable$o parsdate$o $(CHARTRANS_OBJS) @EXTRA_OBJS@ @LIBOBJS@
 
 C_SRC	= $(OBJS:$o=.c)
 
@@ -133,6 +133,7 @@ HTInit$o :		$(top_srcdir)/userdefs.h
 LYCharSets$o :		$(top_srcdir)/userdefs.h
 LYGetFile$o :		$(top_srcdir)/userdefs.h
 LYKeymap$o :		$(top_srcdir)/userdefs.h
+LYLeaks$o :		$(CMN)LYLeaks.h $(CMN)HTString.h
 LYMail$o :		$(top_srcdir)/userdefs.h
 LYMain$o :		$(top_srcdir)/userdefs.h $(top_builddir)/lynx_cfg.h
 LYMainLoop$o :		$(top_srcdir)/userdefs.h
@@ -141,8 +142,8 @@ LYReadCFG$o :		$(top_srcdir)/userdefs.h
 LYShowInfo$o :		$(top_builddir)/cfg_defs.h
 LYTraversal$o :		$(top_srcdir)/userdefs.h
 LYUtils$o :		$(top_srcdir)/userdefs.h
+LYmktime$o :		$(top_srcdir)/userdefs.h
 LYrcFile$o :		$(top_srcdir)/userdefs.h
-LYLeaks$o :		$(CMN)LYLeaks.h $(CMN)HTString.h
 
 CHRTR= chrtrans/
 
@@ -201,6 +202,15 @@ chrtrans/makeuctb$(BUILD_EXEEXT):
 UCAux$o : UCAux.c $(CMN)UCAux.h $(CMN)UCDefs.h
 LYCookie$o : $(top_srcdir)/userdefs.h
 
+# test-driver for LYmktime
+test_mktime: LYmktime.c parsdate.o
+	$(CC) -o $@ $(CC_OPTS) -DTEST_DRIVER LYmktime.c parsdate.o
+
+# update generated source
+parsdate.c : parsdate.y
+	yacc  parsdate.y 
+	mv y.tab.c $@
+
 depend : $(TABLES)
 	makedepend -fmakefile -- $(CC_OPTS) -- $(C_SRC)
 
diff --git a/src/makefile.wsl b/src/makefile.wsl
index 342d6854..5890ec38 100644
--- a/src/makefile.wsl
+++ b/src/makefile.wsl
@@ -1,14 +1,15 @@
-# $LynxId: makefile.wsl,v 1.13 2008/01/08 00:53:56 tom Exp $
+# $LynxId: makefile.wsl,v 1.15 2008/06/30 23:53:42 tom Exp $
+
 OBJS= UCdomap.o UCAux.o UCAuto.o \
 LYClean.o LYShowInfo.o LYEdit.o LYStrings.o \
 LYMail.o HTAlert.o GridText.o LYGetFile.o \
-LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYUtils.o \
+LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYmktime.o LYUtils.o \
 LYOptions.o LYReadCFG.o LYSearch.o LYHistory.o LYSession.o \
 LYForms.o LYPrint.o LYrcFile.o LYDownload.o LYNews.o LYKeymap.o \
 HTML.o HTFWriter.o HTInit.o DefaultStyle.o LYLocal.o LYUpload.o \
 LYLeaks.o LYexit.o LYJump.o LYList.o LYCgi.o LYTraversal.o \
 LYEditmap.o LYCharSets.o LYCharUtils.o LYMap.o LYCookie.o LYExtern.o \
-LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o
+LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o parsdate.o
 
 CFLAGS= -O1 $(MCFLAGS) -I. -I.. $(SLANGINC)
 
@@ -63,3 +64,5 @@ LYShowInfo.o:	../userdefs.h
 LYStrings.o:	../userdefs.h
 LYTraversal.o:	../userdefs.h
 LYUtils.o:	../userdefs.h
+LYmktime.o:	../userdefs.h
+parsdate.o:	../userdefs.h
diff --git a/src/parsdate.c b/src/parsdate.c
new file mode 100644
index 00000000..2db197a2
--- /dev/null
+++ b/src/parsdate.c
@@ -0,0 +1,1453 @@
+#ifndef lint
+static const char yysccsid[] = "@(#)yaccpar	1.9 (Berkeley) 02/21/93";
+#endif
+
+#include <stdlib.h>
+
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYPATCH 20050813
+
+#define YYEMPTY (-1)
+#define yyclearin    (yychar = YYEMPTY)
+#define yyerrok      (yyerrflag = 0)
+#define YYRECOVERING (yyerrflag != 0)
+
+extern int yyparse(void);
+
+static int yygrowstack(void);
+#define YYPREFIX "yy"
+#line 2 "parsdate.y"
+/*
+ *  $LynxId: parsdate.c,v 1.1 2008/06/30 23:25:19 tom Exp $
+ *
+ *  This module is adapted and extended from tin, to use for LYmktime().
+ *
+ *  Project   : tin - a Usenet reader
+ *  Module    : parsedate.y
+ *  Author    : S. Bellovin, R. $alz, J. Berets, P. Eggert
+ *  Created   : 1990-08-01
+ *  Updated   : 2008-06-30 (by Thomas Dickey, for Lynx)
+ *  Notes     : This grammar has 8 shift/reduce conflicts.
+ *
+ *              Originally written by Steven M. Bellovin <smb@research.att.com>
+ *              while at the University of North Carolina at Chapel Hill.
+ *              Later tweaked by a couple of people on Usenet.  Completely
+ *              overhauled by Rich $alz <rsalz@osf.org> and Jim Berets
+ *              <jberets@bbn.com> in August, 1990.
+ *
+ *              Further revised (removed obsolete constructs and cleaned up
+ *              timezone names) in August, 1991, by Rich.
+ *              Paul Eggert <eggert@twinsun.com> helped in September 1992.
+ *              Roland Rosenfeld added MET DST code in April 1994.
+ *
+ *  Revision  : 1.13
+ *  Copyright : This code is in the public domain and has no copyright.
+ */
+
+/* SUPPRESS 530 */ /* Empty body for statement */
+/* SUPPRESS 593 on yyerrlab */ /* Label was not used */
+/* SUPPRESS 593 on yynewstate */ /* Label was not used */
+/* SUPPRESS 595 on yypvt */ /* Automatic variable may be used before set */
+
+#include <parsdate.h>
+
+/*
+**  Get the number of elements in a fixed-size array, or a pointer just
+**  past the end of it.
+*/
+#define ENDOF(array)	(&array[ARRAY_SIZE(array)])
+
+#define CTYPE(isXXXXX, c)	(((unsigned char)(c) < 128) && isXXXXX(((int)c)))
+
+typedef char	*STRING;
+
+extern int date_parse(void);
+
+#define yyparse		date_parse
+#define yylex		date_lex
+#define yyerror		date_error
+
+
+    /* See the LeapYears table in Convert. */
+#define EPOCH		1970
+#define END_OF_TIME	2038
+
+    /* Constants for general time calculations. */
+#define DST_OFFSET	1
+#define SECSPERDAY	(24L * 60L * 60L)
+    /* Readability for TABLE stuff. */
+#define HOUR(x)		(x * 60)
+
+#define LPAREN		'('
+#define RPAREN		')'
+#define IS7BIT(x)	((unsigned int)(x) < 0200)
+
+
+/*
+**  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 them by using a yacc
+**  union, but this is more efficient.  (This routine predates the
+**  yacc %union construct.)
+*/
+static char	*yyInput;
+static DSTMODE	yyDSTmode;
+static int	yyHaveDate;
+static int	yyHaveRel;
+static int	yyHaveTime;
+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;
+
+static time_t	ToSeconds(time_t, time_t, time_t, MERIDIAN);
+static time_t	Convert(time_t, time_t, time_t, time_t, time_t, time_t, MERIDIAN, DSTMODE);
+static time_t	DSTcorrect(time_t, time_t);
+static time_t	RelativeMonth(time_t, time_t);
+static int	LookupWord(char	*, int);
+static int	date_lex(void);
+static int	GetTimeInfo(TIMEINFO *Now);
+
+/*
+ * The 'date_error()' function is declared here to work around a defect in
+ * bison 1.22, which redefines 'const' further down in this file, making it
+ * impossible to put a prototype here, and the function later.  We're using
+ * 'const' on the parameter to quiet gcc's -Wwrite-strings warning.
+ */
+/*ARGSUSED*/
+static void
+date_error(const char GCC_UNUSED *s)
+{
+    /*NOTREACHED*/
+}
+
+#line 127 "parsdate.y"
+typedef union {
+    time_t		Number;
+    enum _MERIDIAN	Meridian;
+} YYSTYPE;
+#line 151 "y.tab.c"
+#define tDAY 257
+#define tDAYZONE 258
+#define tMERIDIAN 259
+#define tMONTH 260
+#define tMONTH_UNIT 261
+#define tSEC_UNIT 262
+#define tSNUMBER 263
+#define tUNUMBER 264
+#define tZONE 265
+#define tDST 266
+#define YYERRCODE 256
+short yylhs[] = {                                        -1,
+    0,    0,    4,    4,    4,    4,    4,    4,    5,    5,
+    5,    5,    5,    2,    2,    2,    2,    2,    1,    6,
+    6,    6,    6,    6,    6,    6,    6,    6,    7,    8,
+    8,    8,    8,    3,    3,
+};
+short yylen[] = {                                         2,
+    0,    2,    1,    2,    1,    1,    2,    1,    2,    4,
+    4,    6,    6,    1,    1,    2,    2,    1,    1,    3,
+    5,    2,    4,    2,    3,    5,    6,    3,    9,    2,
+    2,    2,    2,    0,    1,
+};
+short yydefred[] = {                                      1,
+    0,    0,    0,    0,    0,    2,    0,    5,    0,    8,
+    0,    0,    0,   32,   30,   35,    0,   33,   31,    0,
+    0,    0,    9,    0,   19,    0,   18,    4,    7,    0,
+    0,    0,   25,   28,    0,    0,   16,   17,    0,    0,
+    0,   23,    0,   11,   10,    0,    0,   26,    0,    0,
+   21,    0,   27,   13,   12,    0,    0,   29,
+};
+short yydgoto[] = {                                       1,
+   27,   28,   23,    6,    7,    8,    9,   10,
+};
+short yysindex[] = {                                      0,
+ -240,  -41, -256, -227,  -45,    0, -251,    0, -251,    0,
+ -254, -249,  -22,    0,    0,    0, -237,    0,    0, -235,
+ -228, -226,    0, -236,    0, -224,    0,    0,    0, -223,
+  -39, -222,    0,    0,  -58,   -7,    0,    0,  -15, -220,
+ -215,    0, -218,    0,    0, -217, -216,    0, -214, -234,
+    0,   -8,    0,    0,    0, -213, -212,    0,
+};
+short yyrindex[] = {                                      0,
+    0,    0,    0,    0,    5,    0,   26,    0,   31,    0,
+    0,    0,   11,    0,    0,    0,   37,    0,    0,    0,
+    0,    0,    0,   16,    0,   32,    0,    0,    0,    0,
+    0,    0,    0,    0,    1,   21,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    1,
+    0,    0,    0,    0,    0,    0,    0,    0,
+};
+short yygindex[] = {                                      0,
+  -17,   44,  -31,    0,    0,    0,    0,    0,
+};
+#define YYTABLESIZE 300
+short yytable[] = {                                      43,
+   34,   22,   12,   45,   34,   41,   24,   13,   38,   30,
+   22,   25,   21,   26,   31,   15,    2,   44,   55,    3,
+   20,   32,    4,    5,   16,    3,   33,   34,   25,   37,
+    6,   14,   54,   14,   15,   35,   24,   36,   25,   46,
+   39,   42,   47,   48,   49,   50,   51,   52,   53,   56,
+   57,   58,   29,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+   16,    0,    0,    0,   25,    0,    0,    0,    0,    0,
+    0,    0,    0,   16,   17,   18,   19,   20,   11,    0,
+   40,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,   34,   34,    0,
+   34,   34,   34,    0,   34,   34,    0,   22,   34,   34,
+   22,    0,   15,   22,   22,   15,    0,   20,   15,   15,
+   20,    0,    3,   20,   20,    3,    0,    6,   14,    3,
+    6,   14,    0,   24,    6,   14,   24,    0,    0,   24,
+};
+short yycheck[] = {                                      58,
+    0,   47,   44,   35,    0,   45,  258,  264,   26,  264,
+    0,  263,   58,  265,  264,    0,  257,   35,   50,  260,
+    0,   44,  263,  264,  259,    0,  264,  263,  263,  266,
+    0,    0,   50,  261,  262,  264,    0,  264,  263,   47,
+  264,  264,   58,  264,  260,  264,  264,  264,  263,   58,
+  264,  264,    9,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  259,   -1,   -1,   -1,  263,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  259,  260,  261,  262,  263,  260,   -1,
+  260,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  257,  258,   -1,
+  260,  257,  258,   -1,  264,  265,   -1,  257,  264,  265,
+  260,   -1,  257,  263,  264,  260,   -1,  257,  263,  264,
+  260,   -1,  257,  263,  264,  260,   -1,  257,  257,  264,
+  260,  260,   -1,  257,  264,  264,  260,   -1,   -1,  263,
+};
+#define YYFINAL 1
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 266
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,"','","'-'",0,"'/'",0,0,0,0,0,0,0,0,0,0,"':'",0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"tDAY","tDAYZONE",
+"tMERIDIAN","tMONTH","tMONTH_UNIT","tSEC_UNIT","tSNUMBER","tUNUMBER","tZONE",
+"tDST",
+};
+char *yyrule[] = {
+"$accept : spec",
+"spec :",
+"spec : spec item",
+"item : time",
+"item : time zone",
+"item : date",
+"item : both",
+"item : both zone",
+"item : rel",
+"time : tUNUMBER o_merid",
+"time : tUNUMBER ':' tUNUMBER o_merid",
+"time : tUNUMBER ':' tUNUMBER numzone",
+"time : tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid",
+"time : tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone",
+"zone : tZONE",
+"zone : tDAYZONE",
+"zone : tDAYZONE tDST",
+"zone : tZONE numzone",
+"zone : numzone",
+"numzone : tSNUMBER",
+"date : tUNUMBER '/' tUNUMBER",
+"date : tUNUMBER '/' tUNUMBER '/' tUNUMBER",
+"date : tMONTH tUNUMBER",
+"date : tMONTH tUNUMBER ',' tUNUMBER",
+"date : tUNUMBER tMONTH",
+"date : tUNUMBER tMONTH tUNUMBER",
+"date : tDAY ',' tUNUMBER tMONTH tUNUMBER",
+"date : tDAY ',' tUNUMBER '-' tMONTH tSNUMBER",
+"date : tUNUMBER tSNUMBER tSNUMBER",
+"both : tDAY tMONTH tUNUMBER tUNUMBER ':' tUNUMBER ':' tUNUMBER tUNUMBER",
+"rel : tSNUMBER tSEC_UNIT",
+"rel : tUNUMBER tSEC_UNIT",
+"rel : tSNUMBER tMONTH_UNIT",
+"rel : tUNUMBER tMONTH_UNIT",
+"o_merid :",
+"o_merid : tMERIDIAN",
+};
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+
+/* define the initial stack-sizes */
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH  YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH  500
+#endif
+#endif
+
+#define YYINITSTACKSIZE 500
+
+int      yydebug;
+int      yynerrs;
+int      yyerrflag;
+int      yychar;
+short   *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE  yyval;
+YYSTYPE  yylval;
+
+/* variables for the parser stack */
+static short   *yyss;
+static short   *yysslim;
+static YYSTYPE *yyvs;
+static int      yystacksize;
+#line 349 "parsdate.y"
+
+/*
+**  An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+    const char *name;
+    int		type;
+    time_t	value;
+} TABLE;
+
+/* Month and day table. */
+static const TABLE 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 },
+    { "october",	tMONTH, 10 },
+    { "november",	tMONTH, 11 },
+    { "december",	tMONTH, 12 },
+	/* The value of the day isn't used... */
+    { "sunday",		tDAY, 0 },
+    { "monday",		tDAY, 0 },
+    { "tuesday",	tDAY, 0 },
+    { "wednesday",	tDAY, 0 },
+    { "thursday",	tDAY, 0 },
+    { "friday",		tDAY, 0 },
+    { "saturday",	tDAY, 0 },
+};
+
+/* Time units table. */
+static const TABLE	UnitsTable[] = {
+    { "year",		tMONTH_UNIT,	12 },
+    { "month",		tMONTH_UNIT,	1 },
+    { "week",		tSEC_UNIT,	7 * 24 * 60 * 60 },
+    { "day",		tSEC_UNIT,	1 * 24 * 60 * 60 },
+    { "hour",		tSEC_UNIT,	60 * 60 },
+    { "minute",		tSEC_UNIT,	60 },
+    { "min",		tSEC_UNIT,	60 },
+    { "second",		tSEC_UNIT,	1 },
+    { "sec",		tSEC_UNIT,	1 },
+};
+
+/* Timezone table. */
+static const TABLE	TimezoneTable[] = {
+    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
+    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
+    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
+    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
+    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
+    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
+    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
+    { "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 */
+    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
+    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
+    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
+    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
+    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
+    { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
+    { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
+    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
+    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
+/* Additional aliases for MET / MET DST *************************************/
+    { "mez",    tZONE,     -HOUR(1) },  /* Middle European */
+    { "mewt",   tZONE,     -HOUR(1) },  /* Middle European Winter */
+    { "mest",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mes",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mesz",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "msz",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "metdst", tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+/****************************************************************************/
+    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
+    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
+    { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
+    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
+    { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
+    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
+    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
+    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
+    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
+    { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
+    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
+    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
+    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
+    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
+    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
+    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
+
+    /* For completeness we include the following entries. */
+#if	0
+
+    /* Duplicate names.  Either they conflict with a zone listed above
+     * (which is either more likely to be seen or just been in circulation
+     * longer), or they conflict with another zone in this section and
+     * we could not reasonably choose one over the other. */
+    { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
+    { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
+    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
+    { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
+    { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
+    { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
+    { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
+    { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
+    { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
+    { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
+    { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
+    { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
+    { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
+    { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
+    { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
+    { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
+    { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
+    { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
+    { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
+    { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
+    { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
+
+    /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
+    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
+    { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
+    { "at",	tZONE,     HOUR( 2) },	/* Azores */
+    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
+    { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
+    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
+    { "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 */
+    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
+    { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
+    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
+    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
+    { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
+    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
+    { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
+    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
+    { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
+    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
+    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
+    { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
+    { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
+    { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
+    { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
+#endif	/* 0 */
+};
+
+static time_t
+ToSeconds(
+    time_t	Hours,
+    time_t	Minutes,
+    time_t	Seconds,
+    MERIDIAN	Meridian)
+{
+    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
+	return -1;
+    if (Meridian == MER24) {
+	if (Hours < 0 || Hours > 23)
+	    return -1;
+    }
+    else {
+	if (Hours < 1 || Hours > 12)
+		return -1;
+	if (Hours == 12)
+		Hours = 0;
+	if (Meridian == MERpm)
+		Hours += 12;
+    }
+    return (Hours * 60L + Minutes) * 60L + Seconds;
+}
+
+
+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	dst)
+{
+    static const int	DaysNormal[13] = {
+	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	DaysLeap[13] = {
+	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	LeapYears[] = {
+	1972, 1976, 1980, 1984, 1988, 1992, 1996,
+	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
+    };
+    const int *yp;
+    const int *mp;
+    int i;
+    time_t Julian;
+    time_t tod;
+
+    if (Year < 0)
+	Year = -Year;
+    if (Year < 70)
+        Year += 2000;
+    if (Year < 100)
+	Year += 1900;
+    if (Year < EPOCH)
+	Year += 100;
+    for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
+	if (Year == *yp) {
+	    mp = DaysLeap;
+	    break;
+	}
+    if (Year < EPOCH || Year > END_OF_TIME
+     || Month < 1 || Month > 12
+     /* NOSTRICT */ /* conversion from long may lose accuracy */
+     || Day < 1 || Day > mp[(int)Month]) {
+	return -1;
+    }
+
+    Julian = Day - 1 + (Year - EPOCH) * 365;
+    for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
+	if (Year <= *yp)
+	    break;
+    for (i = 1; i < Month; i++)
+	Julian += *++mp;
+    Julian *= SECSPERDAY;
+    Julian += yyTimezone * 60L;
+    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) {
+	return -1;
+    }
+    Julian += tod;
+    tod = Julian;
+    if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
+	Julian -= DST_OFFSET * 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) * DST_OFFSET * 60 * 60;
+}
+
+
+static time_t
+RelativeMonth(
+    time_t	Start,
+    time_t	RelMonth)
+{
+    struct tm	*tm;
+    time_t	Month;
+    time_t	Year;
+
+    tm = localtime(&Start);
+    Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+    Year = Month / 12 + 1900;
+    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,
+    int length)
+{
+    char	*p;
+    const char *q;
+    const TABLE *tp;
+    int	c;
+
+    p = buff;
+    c = p[0];
+
+    /* See if we have an abbreviation for a month. */
+    if (length == 3 || (length == 4 && p[3] == '.'))
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
+	    q = tp->name;
+	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    else
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+
+    /* Try for a timezone. */
+    for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
+	if (c == tp->name[0] && p[1] == tp->name[1]
+	 && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+
+    if (strcmp(buff, "dst") == 0)
+      return tDST;
+
+    /* Try the units table. */
+    for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
+	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+
+    /* Strip off any plural and try the units table again. */
+    if (--length > 0 && p[length] == 's') {
+	p[length] = '\0';
+	for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		p[length] = 's';
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	p[length] = 's';
+    }
+    length++;
+
+    /* Drop out any periods. */
+    for (p = buff, q = (STRING)buff; *q; q++)
+	if (*q != '.')
+	    *p++ = *q;
+    *p = '\0';
+
+    /* Try the meridians. */
+    if (buff[1] == 'm' && buff[2] == '\0') {
+	if (buff[0] == 'a') {
+	    yylval.Meridian = MERam;
+	    return tMERIDIAN;
+	}
+	if (buff[0] == 'p') {
+	    yylval.Meridian = MERpm;
+	    return tMERIDIAN;
+	}
+    }
+
+    /* If we saw any periods, try the timezones again. */
+    if (p - buff != length) {
+	c = buff[0];
+	for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
+	    if (c == tp->name[0] && p[1] == tp->name[1]
+	    && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+    }
+
+    /* Unknown word -- assume GMT timezone. */
+    yylval.Number = 0;
+    return tZONE;
+}
+
+
+static int
+date_lex(void)
+{
+    int	c;
+    char	*p;
+    char		buff[20];
+    int	sign;
+    int	i;
+    int	nesting;
+
+    for(;;) {
+	/* Get first character after the whitespace. */
+	for(;;) {
+	    while (CTYPE(isspace, *yyInput))
+		yyInput++;
+	    c = *yyInput;
+
+	    /* Ignore RFC 822 comments, typically time zone names. */
+	    if (c != LPAREN)
+		break;
+	    for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
+		if (c == LPAREN)
+		    nesting++;
+		else if (!IS7BIT(c) || c == '\0' || c == '\r'
+		     || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) {
+		    /* Lexical error: bad comment. */
+		    return '?';
+		}
+	    yyInput++;
+	}
+
+	/* A number? */
+	if (CTYPE(isdigit, c) || c == '-' || c == '+') {
+	    if (c == '-' || c == '+') {
+		sign = c == '-' ? -1 : 1;
+		yyInput++;
+		if (!CTYPE(isdigit, *yyInput)) {
+		    /* Return the isolated plus or minus sign. */
+		    --yyInput;
+		    return *yyInput++;
+		}
+	    }
+	    else
+		sign = 0;
+	    for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, c); )
+		i = 10 * i + c - '0';
+	    yyInput--;
+	    yylval.Number = sign < 0 ? -i : i;
+	    return sign ? tSNUMBER : tUNUMBER;
+	}
+
+	/* A word? */
+	if (CTYPE(isalpha, c)) {
+	    for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, c); )
+		if (p < &buff[sizeof buff - 1])
+		    *p++ = CTYPE(isupper, c) ? tolower(c) : c;
+	    *p = '\0';
+	    yyInput--;
+	    return LookupWord(buff, p - buff);
+	}
+
+	return *yyInput++;
+    }
+}
+
+
+static int
+GetTimeInfo(
+    TIMEINFO		*Now)
+{
+    static time_t	LastTime;
+    static long		LastTzone;
+    struct tm		*tm;
+#if	defined(HAVE_GETTIMEOFDAY)
+    struct timeval	tv;
+#endif	/* defined(HAVE_GETTIMEOFDAY) */
+#if	defined(DONT_HAVE_TM_GMTOFF)
+    struct tm		local;
+    struct tm		gmt;
+#endif	/* !defined(DONT_HAVE_TM_GMTOFF) */
+
+    /* Get the basic time. */
+#if defined(HAVE_GETTIMEOFDAY)
+    if (gettimeofday(&tv, (struct timezone *)NULL) == -1)
+	return -1;
+    Now->time = tv.tv_sec;
+    Now->usec = tv.tv_usec;
+#else
+    /* Can't check for -1 since that might be a time, I guess. */
+    (void)time(&Now->time);
+    Now->usec = 0;
+#endif /* defined(HAVE_GETTIMEOFDAY) */
+
+    /* Now get the timezone if it's been an hour since the last time. */
+    if (Now->time - LastTime > 60 * 60) {
+	LastTime = Now->time;
+	if ((tm = localtime(&Now->time)) == NULL)
+	    return -1;
+#if	defined(DONT_HAVE_TM_GMTOFF)
+	/* To get the timezone, compare localtime with GMT. */
+	local = *tm;
+	if ((tm = gmtime(&Now->time)) == NULL)
+	    return -1;
+	gmt = *tm;
+
+	/* Assume we are never more than 24 hours away. */
+	LastTzone = gmt.tm_yday - local.tm_yday;
+	if (LastTzone > 1)
+	    LastTzone = -24;
+	else if (LastTzone < -1)
+	    LastTzone = 24;
+	else
+	    LastTzone *= 24;
+
+	/* Scale in the hours and minutes; ignore seconds. */
+	LastTzone += gmt.tm_hour - local.tm_hour;
+	LastTzone *= 60;
+	LastTzone += gmt.tm_min - local.tm_min;
+#else
+	LastTzone =  (0 - tm->tm_gmtoff) / 60;
+#endif	/* defined(DONT_HAVE_TM_GMTOFF) */
+    }
+    Now->tzone = LastTzone;
+    return 0;
+}
+
+
+time_t
+parsedate(
+    char		*p,
+    TIMEINFO		*now)
+{
+    struct tm		*tm;
+    TIMEINFO		ti;
+    time_t		Start;
+
+    yyInput = p;
+    if (now == NULL) {
+	now = &ti;
+	(void)GetTimeInfo(&ti);
+    }
+
+    tm = localtime(&now->time);
+    yyYear = tm->tm_year + 1900;
+    yyMonth = tm->tm_mon + 1;
+    yyDay = tm->tm_mday;
+    yyTimezone = now->tzone;
+    if (tm->tm_isdst)                   /* Correct timezone offset for DST */
+      yyTimezone += DST_OFFSET * 60;
+    yyDSTmode = DSTmaybe;
+    yyHour = 0;
+    yyMinutes = 0;
+    yySeconds = 0;
+    yyMeridian = MER24;
+    yyRelSeconds = 0;
+    yyRelMonth = 0;
+    yyHaveDate = 0;
+    yyHaveRel = 0;
+    yyHaveTime = 0;
+
+    if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
+	return -1;
+
+    if (yyHaveDate || yyHaveTime) {
+	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+		    yyMeridian, yyDSTmode);
+	if (Start < 0)
+	    return -1;
+    } else {
+	Start = now->time;
+	if (!yyHaveRel)
+	    Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
+    }
+
+    Start += yyRelSeconds;
+    if (yyRelMonth)
+	Start += RelativeMonth(Start, yyRelMonth);
+
+    /* 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;
+}
+#line 931 "y.tab.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack(void)
+{
+    int newsize, i;
+    short *newss;
+    YYSTYPE *newvs;
+
+    if ((newsize = yystacksize) == 0)
+        newsize = YYINITSTACKSIZE;
+    else if (newsize >= YYMAXDEPTH)
+        return -1;
+    else if ((newsize *= 2) > YYMAXDEPTH)
+        newsize = YYMAXDEPTH;
+
+    i = yyssp - yyss;
+    newss = (yyss != 0)
+          ? (short *)realloc(yyss, newsize * sizeof(*newss))
+          : (short *)malloc(newsize * sizeof(*newss));
+    if (newss == 0)
+        return -1;
+
+    yyss  = newss;
+    yyssp = newss + i;
+    newvs = (yyvs != 0)
+          ? (YYSTYPE *)realloc(yyvs, newsize * sizeof(*newvs))
+          : (YYSTYPE *)malloc(newsize * sizeof(*newvs));
+    if (newvs == 0)
+        return -1;
+
+    yyvs = newvs;
+    yyvsp = newvs + i;
+    yystacksize = newsize;
+    yysslim = yyss + newsize - 1;
+    return 0;
+}
+
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse(void)
+{
+    register int yym, yyn, yystate;
+#if YYDEBUG
+    register const char *yys;
+
+    if ((yys = getenv("YYDEBUG")) != 0)
+    {
+        yyn = *yys;
+        if (yyn >= '0' && yyn <= '9')
+            yydebug = yyn - '0';
+    }
+#endif
+
+    yynerrs = 0;
+    yyerrflag = 0;
+    yychar = YYEMPTY;
+
+    if (yyss == NULL && yygrowstack()) goto yyoverflow;
+    yyssp = yyss;
+    yyvsp = yyvs;
+    *yyssp = yystate = 0;
+
+yyloop:
+    if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+    if (yychar < 0)
+    {
+        if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, reading %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+    }
+    if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: state %d, shifting to state %d\n",
+                    YYPREFIX, yystate, yytable[yyn]);
+#endif
+        if (yyssp >= yysslim && yygrowstack())
+        {
+            goto yyoverflow;
+        }
+        *++yyssp = yystate = yytable[yyn];
+        *++yyvsp = yylval;
+        yychar = YYEMPTY;
+        if (yyerrflag > 0)  --yyerrflag;
+        goto yyloop;
+    }
+    if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+        yyn = yytable[yyn];
+        goto yyreduce;
+    }
+    if (yyerrflag) goto yyinrecovery;
+
+    yyerror("syntax error");
+
+#ifdef lint
+    goto yyerrlab;
+#endif
+
+yyerrlab:
+    ++yynerrs;
+
+yyinrecovery:
+    if (yyerrflag < 3)
+    {
+        yyerrflag = 3;
+        for (;;)
+        {
+            if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+                    yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+                if (yyssp >= yysslim && yygrowstack())
+                {
+                    goto yyoverflow;
+                }
+                *++yyssp = yystate = yytable[yyn];
+                *++yyvsp = yylval;
+                goto yyloop;
+            }
+            else
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: error recovery discarding state %d\n",
+                            YYPREFIX, *yyssp);
+#endif
+                if (yyssp <= yyss) goto yyabort;
+                --yyssp;
+                --yyvsp;
+            }
+        }
+    }
+    else
+    {
+        if (yychar == 0) goto yyabort;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+        yychar = YYEMPTY;
+        goto yyloop;
+    }
+
+yyreduce:
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+                YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+    yym = yylen[yyn];
+    yyval = yyvsp[1-yym];
+    switch (yyn)
+    {
+case 3:
+#line 145 "parsdate.y"
+{
+	    yyHaveTime++;
+#if	defined(lint)
+	    /* I am compulsive about lint natterings... */
+	    if (yyHaveTime == -1) {
+		YYERROR;
+	    }
+#endif	/* defined(lint) */
+	}
+break;
+case 4:
+#line 154 "parsdate.y"
+{
+	    yyHaveTime++;
+	    yyTimezone = yyvsp[0].Number;
+	}
+break;
+case 5:
+#line 158 "parsdate.y"
+{
+	    yyHaveDate++;
+	}
+break;
+case 6:
+#line 161 "parsdate.y"
+{
+	    yyHaveDate++;
+	    yyHaveTime++;
+	}
+break;
+case 7:
+#line 165 "parsdate.y"
+{
+	    yyHaveDate++;
+	    yyHaveTime++;
+	    yyTimezone = yyvsp[0].Number;
+	}
+break;
+case 8:
+#line 170 "parsdate.y"
+{
+	    yyHaveRel = 1;
+	}
+break;
+case 9:
+#line 175 "parsdate.y"
+{
+	    if (yyvsp[-1].Number < 100) {
+		yyHour = yyvsp[-1].Number;
+		yyMinutes = 0;
+	    }
+	    else {
+		yyHour = yyvsp[-1].Number / 100;
+		yyMinutes = yyvsp[-1].Number % 100;
+	    }
+	    yySeconds = 0;
+	    yyMeridian = yyvsp[0].Meridian;
+	}
+break;
+case 10:
+#line 187 "parsdate.y"
+{
+	    yyHour = yyvsp[-3].Number;
+	    yyMinutes = yyvsp[-1].Number;
+	    yySeconds = 0;
+	    yyMeridian = yyvsp[0].Meridian;
+	}
+break;
+case 11:
+#line 193 "parsdate.y"
+{
+	    yyHour = yyvsp[-3].Number;
+	    yyMinutes = yyvsp[-1].Number;
+	    yyTimezone = yyvsp[0].Number;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 12:
+#line 200 "parsdate.y"
+{
+	    yyHour = yyvsp[-5].Number;
+	    yyMinutes = yyvsp[-3].Number;
+	    yySeconds = yyvsp[-1].Number;
+	    yyMeridian = yyvsp[0].Meridian;
+	}
+break;
+case 13:
+#line 206 "parsdate.y"
+{
+	    yyHour = yyvsp[-5].Number;
+	    yyMinutes = yyvsp[-3].Number;
+	    yySeconds = yyvsp[-1].Number;
+	    yyTimezone = yyvsp[0].Number;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 14:
+#line 216 "parsdate.y"
+{
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 15:
+#line 220 "parsdate.y"
+{
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSTon;
+	}
+break;
+case 16:
+#line 224 "parsdate.y"
+{
+	    yyTimezone = yyvsp[-1].Number;
+	    yyDSTmode = DSTon;
+	}
+break;
+case 17:
+#line 228 "parsdate.y"
+{
+	    /* Only allow "GMT+300" and "GMT-0800" */
+	    if (yyvsp[-1].Number != 0) {
+		YYABORT;
+	    }
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 18:
+#line 236 "parsdate.y"
+{
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 19:
+#line 242 "parsdate.y"
+{
+	    int	i;
+
+	    /* Unix and GMT and numeric timezones -- a little confusing. */
+	    if ((int)yyvsp[0].Number < 0) {
+		/* Don't work with negative modulus. */
+		yyvsp[0].Number = -(int)yyvsp[0].Number;
+		if (yyvsp[0].Number > 9999 || (i = yyvsp[0].Number % 100) >= 60) {
+			YYABORT;
+		}
+		yyval.Number = (yyvsp[0].Number / 100) * 60 + i;
+	    }
+	    else {
+		if (yyvsp[0].Number > 9999 || (i = yyvsp[0].Number % 100) >= 60) {
+			YYABORT;
+		}
+		yyval.Number = -((yyvsp[0].Number / 100) * 60 + i);
+	    }
+	}
+break;
+case 20:
+#line 263 "parsdate.y"
+{
+	    yyMonth = yyvsp[-2].Number;
+	    yyDay = yyvsp[0].Number;
+	}
+break;
+case 21:
+#line 267 "parsdate.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 279 "parsdate.y"
+{
+	    yyMonth = yyvsp[-1].Number;
+	    yyDay = yyvsp[0].Number;
+	}
+break;
+case 23:
+#line 283 "parsdate.y"
+{
+	    yyMonth = yyvsp[-3].Number;
+	    yyDay = yyvsp[-2].Number;
+	    yyYear = yyvsp[0].Number;
+	}
+break;
+case 24:
+#line 288 "parsdate.y"
+{
+	    yyDay = yyvsp[-1].Number;
+	    yyMonth = yyvsp[0].Number;
+	}
+break;
+case 25:
+#line 292 "parsdate.y"
+{
+	    yyDay = yyvsp[-2].Number;
+	    yyMonth = yyvsp[-1].Number;
+	    yyYear = yyvsp[0].Number;
+	}
+break;
+case 26:
+#line 297 "parsdate.y"
+{
+	    yyDay = yyvsp[-2].Number;
+	    yyMonth = yyvsp[-1].Number;
+	    yyYear = yyvsp[0].Number;
+	}
+break;
+case 27:
+#line 302 "parsdate.y"
+{
+	    yyDay = yyvsp[-3].Number;
+	    yyMonth = yyvsp[-1].Number;
+	    yyYear = -yyvsp[0].Number;
+	}
+break;
+case 28:
+#line 307 "parsdate.y"
+{
+	    yyDay = yyvsp[-2].Number;
+	    yyMonth = -yyvsp[-1].Number;
+	    yyYear = -yyvsp[0].Number;
+	    yyDSTmode = DSToff;	/* assume midnight if no time given */
+	    yyTimezone = 0;	/* Lynx assumes GMT for this format */
+	}
+break;
+case 29:
+#line 316 "parsdate.y"
+{
+	    yyMonth = yyvsp[-7].Number;
+	    yyDay = yyvsp[-6].Number;
+	    yyYear = yyvsp[0].Number;
+	    yyHour = yyvsp[-5].Number;
+	    yyMinutes = yyvsp[-3].Number;
+	    yySeconds = yyvsp[-1].Number;
+	}
+break;
+case 30:
+#line 326 "parsdate.y"
+{
+	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 31:
+#line 329 "parsdate.y"
+{
+	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 32:
+#line 332 "parsdate.y"
+{
+	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 33:
+#line 335 "parsdate.y"
+{
+	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 34:
+#line 340 "parsdate.y"
+{
+	    yyval.Meridian = MER24;
+	}
+break;
+case 35:
+#line 343 "parsdate.y"
+{
+	    yyval.Meridian = yyvsp[0].Meridian;
+	}
+break;
+#line 1395 "y.tab.c"
+    }
+    yyssp -= yym;
+    yystate = *yyssp;
+    yyvsp -= yym;
+    yym = yylhs[yyn];
+    if (yystate == 0 && yym == 0)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+        yystate = YYFINAL;
+        *++yyssp = YYFINAL;
+        *++yyvsp = yyval;
+        if (yychar < 0)
+        {
+            if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+            if (yydebug)
+            {
+                yys = 0;
+                if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+                if (!yys) yys = "illegal-symbol";
+                printf("%sdebug: state %d, reading %d (%s)\n",
+                        YYPREFIX, YYFINAL, yychar, yys);
+            }
+#endif
+        }
+        if (yychar == 0) goto yyaccept;
+        goto yyloop;
+    }
+    if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+        yystate = yytable[yyn];
+    else
+        yystate = yydgoto[yym];
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+    if (yyssp >= yysslim && yygrowstack())
+    {
+        goto yyoverflow;
+    }
+    *++yyssp = yystate;
+    *++yyvsp = yyval;
+    goto yyloop;
+
+yyoverflow:
+    yyerror("yacc stack overflow");
+
+yyabort:
+    return (1);
+
+yyaccept:
+    return (0);
+}
diff --git a/src/parsdate.h b/src/parsdate.h
new file mode 100644
index 00000000..ab24d318
--- /dev/null
+++ b/src/parsdate.h
@@ -0,0 +1,21 @@
+/* $LynxId: parsdate.h,v 1.1 2008/06/29 23:23:45 tom Exp $ */
+#ifndef PARSDATE_H
+#define PARSDATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <LYUtils.h>
+#define ARRAY_SIZE(array)       ((int) (sizeof(array) / sizeof(array[0])))
+     typedef struct _TIMEINFO {
+	time_t time;
+	long usec;
+	long tzone;
+    } TIMEINFO;
+
+    extern time_t parsedate(char *p, TIMEINFO * now);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* PARSDATE_H */
diff --git a/src/parsdate.y b/src/parsdate.y
new file mode 100644
index 00000000..c364f7a3
--- /dev/null
+++ b/src/parsdate.y
@@ -0,0 +1,919 @@
+%{
+/*
+ *  $LynxId: parsdate.y,v 1.4 2008/06/30 23:25:14 tom Exp $
+ *
+ *  This module is adapted and extended from tin, to use for LYmktime().
+ *
+ *  Project   : tin - a Usenet reader
+ *  Module    : parsedate.y
+ *  Author    : S. Bellovin, R. $alz, J. Berets, P. Eggert
+ *  Created   : 1990-08-01
+ *  Updated   : 2008-06-30 (by Thomas Dickey, for Lynx)
+ *  Notes     : This grammar has 8 shift/reduce conflicts.
+ *
+ *              Originally written by Steven M. Bellovin <smb@research.att.com>
+ *              while at the University of North Carolina at Chapel Hill.
+ *              Later tweaked by a couple of people on Usenet.  Completely
+ *              overhauled by Rich $alz <rsalz@osf.org> and Jim Berets
+ *              <jberets@bbn.com> in August, 1990.
+ *
+ *              Further revised (removed obsolete constructs and cleaned up
+ *              timezone names) in August, 1991, by Rich.
+ *              Paul Eggert <eggert@twinsun.com> helped in September 1992.
+ *              Roland Rosenfeld added MET DST code in April 1994.
+ *
+ *  Revision  : 1.13
+ *  Copyright : This code is in the public domain and has no copyright.
+ */
+
+/* SUPPRESS 530 */ /* Empty body for statement */
+/* SUPPRESS 593 on yyerrlab */ /* Label was not used */
+/* SUPPRESS 593 on yynewstate */ /* Label was not used */
+/* SUPPRESS 595 on yypvt */ /* Automatic variable may be used before set */
+
+#include <parsdate.h>
+
+/*
+**  Get the number of elements in a fixed-size array, or a pointer just
+**  past the end of it.
+*/
+#define ENDOF(array)	(&array[ARRAY_SIZE(array)])
+
+#define CTYPE(isXXXXX, c)	(((unsigned char)(c) < 128) && isXXXXX(((int)c)))
+
+typedef char	*STRING;
+
+extern int date_parse(void);
+
+#define yyparse		date_parse
+#define yylex		date_lex
+#define yyerror		date_error
+
+
+    /* See the LeapYears table in Convert. */
+#define EPOCH		1970
+#define END_OF_TIME	2038
+
+    /* Constants for general time calculations. */
+#define DST_OFFSET	1
+#define SECSPERDAY	(24L * 60L * 60L)
+    /* Readability for TABLE stuff. */
+#define HOUR(x)		(x * 60)
+
+#define LPAREN		'('
+#define RPAREN		')'
+#define IS7BIT(x)	((unsigned int)(x) < 0200)
+
+
+/*
+**  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 them by using a yacc
+**  union, but this is more efficient.  (This routine predates the
+**  yacc %union construct.)
+*/
+static char	*yyInput;
+static DSTMODE	yyDSTmode;
+static int	yyHaveDate;
+static int	yyHaveRel;
+static int	yyHaveTime;
+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;
+
+static time_t	ToSeconds(time_t, time_t, time_t, MERIDIAN);
+static time_t	Convert(time_t, time_t, time_t, time_t, time_t, time_t, MERIDIAN, DSTMODE);
+static time_t	DSTcorrect(time_t, time_t);
+static time_t	RelativeMonth(time_t, time_t);
+static int	LookupWord(char	*, int);
+static int	date_lex(void);
+static int	GetTimeInfo(TIMEINFO *Now);
+
+/*
+ * The 'date_error()' function is declared here to work around a defect in
+ * bison 1.22, which redefines 'const' further down in this file, making it
+ * impossible to put a prototype here, and the function later.  We're using
+ * 'const' on the parameter to quiet gcc's -Wwrite-strings warning.
+ */
+/*ARGSUSED*/
+static void
+date_error(const char GCC_UNUSED *s)
+{
+    /*NOTREACHED*/
+}
+
+%}
+
+%union {
+    time_t		Number;
+    enum _MERIDIAN	Meridian;
+}
+
+%token	tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
+%token	tUNUMBER tZONE tDST
+
+%type	<Number>	tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
+%type	<Number>	tSNUMBER tUNUMBER tZONE numzone zone
+%type	<Meridian>	tMERIDIAN o_merid
+
+%%
+
+spec	: /* NULL */
+	| spec item
+	;
+
+item	: time {
+	    yyHaveTime++;
+#if	defined(lint)
+	    /* I am compulsive about lint natterings... */
+	    if (yyHaveTime == -1) {
+		YYERROR;
+	    }
+#endif	/* defined(lint) */
+	}
+	| time zone {
+	    yyHaveTime++;
+	    yyTimezone = $2;
+	}
+	| date {
+	    yyHaveDate++;
+	}
+	| both {
+	    yyHaveDate++;
+	    yyHaveTime++;
+	}
+	| both zone {
+	    yyHaveDate++;
+	    yyHaveTime++;
+	    yyTimezone = $2;
+	}
+	| rel {
+	    yyHaveRel = 1;
+	}
+	;
+
+time	: tUNUMBER o_merid {
+	    if ($1 < 100) {
+		yyHour = $1;
+		yyMinutes = 0;
+	    }
+	    else {
+		yyHour = $1 / 100;
+		yyMinutes = $1 % 100;
+	    }
+	    yySeconds = 0;
+	    yyMeridian = $2;
+	}
+	| tUNUMBER ':' tUNUMBER o_merid {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yySeconds = 0;
+	    yyMeridian = $4;
+	}
+	| tUNUMBER ':' tUNUMBER numzone {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yyTimezone = $4;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yySeconds = $5;
+	    yyMeridian = $6;
+	}
+	| tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yySeconds = $5;
+	    yyTimezone = $6;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+	;
+
+zone	: tZONE {
+	    $$ = $1;
+	    yyDSTmode = DSToff;
+	}
+	| tDAYZONE {
+	    $$ = $1;
+	    yyDSTmode = DSTon;
+	}
+	| tDAYZONE tDST {
+	    yyTimezone = $1;
+	    yyDSTmode = DSTon;
+	}
+	| tZONE numzone {
+	    /* Only allow "GMT+300" and "GMT-0800" */
+	    if ($1 != 0) {
+		YYABORT;
+	    }
+	    $$ = $2;
+	    yyDSTmode = DSToff;
+	}
+	| numzone {
+	    $$ = $1;
+	    yyDSTmode = DSToff;
+	}
+	;
+
+numzone	: tSNUMBER {
+	    int	i;
+
+	    /* Unix and GMT and numeric timezones -- a little confusing. */
+	    if ((int)$1 < 0) {
+		/* Don't work with negative modulus. */
+		$1 = -(int)$1;
+		if ($1 > 9999 || (i = $1 % 100) >= 60) {
+			YYABORT;
+		}
+		$$ = ($1 / 100) * 60 + i;
+	    }
+	    else {
+		if ($1 > 9999 || (i = $1 % 100) >= 60) {
+			YYABORT;
+		}
+		$$ = -(($1 / 100) * 60 + i);
+	    }
+	}
+	;
+
+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;
+	    }
+	}
+	| tMONTH tUNUMBER {
+	    yyMonth = $1;
+	    yyDay = $2;
+	}
+	| tMONTH tUNUMBER ',' tUNUMBER {
+	    yyMonth = $1;
+	    yyDay = $2;
+	    yyYear = $4;
+	}
+	| tUNUMBER tMONTH {
+	    yyDay = $1;
+	    yyMonth = $2;
+	}
+	| tUNUMBER tMONTH tUNUMBER {
+	    yyDay = $1;
+	    yyMonth = $2;
+	    yyYear = $3;
+	}
+	| tDAY ',' tUNUMBER tMONTH tUNUMBER {
+	    yyDay = $3;
+	    yyMonth = $4;
+	    yyYear = $5;
+	}
+	| tDAY ',' tUNUMBER '-' tMONTH tSNUMBER {
+	    yyDay = $3;
+	    yyMonth = $5;
+	    yyYear = -$6;
+	}
+	| tUNUMBER tSNUMBER tSNUMBER {
+	    yyDay = $1;
+	    yyMonth = -$2;
+	    yyYear = -$3;
+	    yyDSTmode = DSToff;	/* assume midnight if no time given */
+	    yyTimezone = 0;	/* Lynx assumes GMT for this format */
+	}
+	;
+
+both	: tDAY tMONTH tUNUMBER tUNUMBER ':' tUNUMBER ':' tUNUMBER tUNUMBER {
+	    yyMonth = $2;
+	    yyDay = $3;
+	    yyYear = $9;
+	    yyHour = $4;
+	    yyMinutes = $6;
+	    yySeconds = $8;
+	}
+	;
+
+rel	: tSNUMBER tSEC_UNIT {
+	    yyRelSeconds += $1 * $2;
+	}
+	| tUNUMBER tSEC_UNIT {
+	    yyRelSeconds += $1 * $2;
+	}
+	| tSNUMBER tMONTH_UNIT {
+	    yyRelMonth += $1 * $2;
+	}
+	| tUNUMBER tMONTH_UNIT {
+	    yyRelMonth += $1 * $2;
+	}
+	;
+
+o_merid	: /* NULL */ {
+	    $$ = MER24;
+	}
+	| tMERIDIAN {
+	    $$ = $1;
+	}
+	;
+
+%%
+
+/*
+**  An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+    const char *name;
+    int		type;
+    time_t	value;
+} TABLE;
+
+/* Month and day table. */
+static const TABLE 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 },
+    { "october",	tMONTH, 10 },
+    { "november",	tMONTH, 11 },
+    { "december",	tMONTH, 12 },
+	/* The value of the day isn't used... */
+    { "sunday",		tDAY, 0 },
+    { "monday",		tDAY, 0 },
+    { "tuesday",	tDAY, 0 },
+    { "wednesday",	tDAY, 0 },
+    { "thursday",	tDAY, 0 },
+    { "friday",		tDAY, 0 },
+    { "saturday",	tDAY, 0 },
+};
+
+/* Time units table. */
+static const TABLE	UnitsTable[] = {
+    { "year",		tMONTH_UNIT,	12 },
+    { "month",		tMONTH_UNIT,	1 },
+    { "week",		tSEC_UNIT,	7 * 24 * 60 * 60 },
+    { "day",		tSEC_UNIT,	1 * 24 * 60 * 60 },
+    { "hour",		tSEC_UNIT,	60 * 60 },
+    { "minute",		tSEC_UNIT,	60 },
+    { "min",		tSEC_UNIT,	60 },
+    { "second",		tSEC_UNIT,	1 },
+    { "sec",		tSEC_UNIT,	1 },
+};
+
+/* Timezone table. */
+static const TABLE	TimezoneTable[] = {
+    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
+    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
+    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
+    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
+    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
+    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
+    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
+    { "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 */
+    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
+    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
+    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
+    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
+    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
+    { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
+    { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
+    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
+    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
+/* Additional aliases for MET / MET DST *************************************/
+    { "mez",    tZONE,     -HOUR(1) },  /* Middle European */
+    { "mewt",   tZONE,     -HOUR(1) },  /* Middle European Winter */
+    { "mest",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mes",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mesz",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "msz",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "metdst", tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+/****************************************************************************/
+    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
+    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
+    { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
+    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
+    { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
+    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
+    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
+    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
+    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
+    { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
+    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
+    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
+    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
+    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
+    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
+    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
+
+    /* For completeness we include the following entries. */
+#if	0
+
+    /* Duplicate names.  Either they conflict with a zone listed above
+     * (which is either more likely to be seen or just been in circulation
+     * longer), or they conflict with another zone in this section and
+     * we could not reasonably choose one over the other. */
+    { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
+    { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
+    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
+    { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
+    { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
+    { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
+    { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
+    { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
+    { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
+    { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
+    { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
+    { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
+    { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
+    { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
+    { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
+    { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
+    { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
+    { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
+    { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
+    { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
+    { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
+
+    /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
+    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
+    { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
+    { "at",	tZONE,     HOUR( 2) },	/* Azores */
+    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
+    { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
+    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
+    { "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 */
+    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
+    { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
+    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
+    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
+    { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
+    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
+    { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
+    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
+    { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
+    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
+    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
+    { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
+    { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
+    { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
+    { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
+#endif	/* 0 */
+};
+
+static time_t
+ToSeconds(
+    time_t	Hours,
+    time_t	Minutes,
+    time_t	Seconds,
+    MERIDIAN	Meridian)
+{
+    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
+	return -1;
+    if (Meridian == MER24) {
+	if (Hours < 0 || Hours > 23)
+	    return -1;
+    }
+    else {
+	if (Hours < 1 || Hours > 12)
+		return -1;
+	if (Hours == 12)
+		Hours = 0;
+	if (Meridian == MERpm)
+		Hours += 12;
+    }
+    return (Hours * 60L + Minutes) * 60L + Seconds;
+}
+
+
+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	dst)
+{
+    static const int	DaysNormal[13] = {
+	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	DaysLeap[13] = {
+	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	LeapYears[] = {
+	1972, 1976, 1980, 1984, 1988, 1992, 1996,
+	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
+    };
+    const int *yp;
+    const int *mp;
+    int i;
+    time_t Julian;
+    time_t tod;
+
+    if (Year < 0)
+	Year = -Year;
+    if (Year < 70)
+        Year += 2000;
+    if (Year < 100)
+	Year += 1900;
+    if (Year < EPOCH)
+	Year += 100;
+    for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
+	if (Year == *yp) {
+	    mp = DaysLeap;
+	    break;
+	}
+    if (Year < EPOCH || Year > END_OF_TIME
+     || Month < 1 || Month > 12
+     /* NOSTRICT */ /* conversion from long may lose accuracy */
+     || Day < 1 || Day > mp[(int)Month]) {
+	return -1;
+    }
+
+    Julian = Day - 1 + (Year - EPOCH) * 365;
+    for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
+	if (Year <= *yp)
+	    break;
+    for (i = 1; i < Month; i++)
+	Julian += *++mp;
+    Julian *= SECSPERDAY;
+    Julian += yyTimezone * 60L;
+    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) {
+	return -1;
+    }
+    Julian += tod;
+    tod = Julian;
+    if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
+	Julian -= DST_OFFSET * 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) * DST_OFFSET * 60 * 60;
+}
+
+
+static time_t
+RelativeMonth(
+    time_t	Start,
+    time_t	RelMonth)
+{
+    struct tm	*tm;
+    time_t	Month;
+    time_t	Year;
+
+    tm = localtime(&Start);
+    Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+    Year = Month / 12 + 1900;
+    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,
+    int length)
+{
+    char	*p;
+    const char *q;
+    const TABLE *tp;
+    int	c;
+
+    p = buff;
+    c = p[0];
+
+    /* See if we have an abbreviation for a month. */
+    if (length == 3 || (length == 4 && p[3] == '.'))
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
+	    q = tp->name;
+	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    else
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+
+    /* Try for a timezone. */
+    for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
+	if (c == tp->name[0] && p[1] == tp->name[1]
+	 && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+
+    if (strcmp(buff, "dst") == 0)
+      return tDST;
+
+    /* Try the units table. */
+    for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
+	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+
+    /* Strip off any plural and try the units table again. */
+    if (--length > 0 && p[length] == 's') {
+	p[length] = '\0';
+	for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		p[length] = 's';
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	p[length] = 's';
+    }
+    length++;
+
+    /* Drop out any periods. */
+    for (p = buff, q = (STRING)buff; *q; q++)
+	if (*q != '.')
+	    *p++ = *q;
+    *p = '\0';
+
+    /* Try the meridians. */
+    if (buff[1] == 'm' && buff[2] == '\0') {
+	if (buff[0] == 'a') {
+	    yylval.Meridian = MERam;
+	    return tMERIDIAN;
+	}
+	if (buff[0] == 'p') {
+	    yylval.Meridian = MERpm;
+	    return tMERIDIAN;
+	}
+    }
+
+    /* If we saw any periods, try the timezones again. */
+    if (p - buff != length) {
+	c = buff[0];
+	for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
+	    if (c == tp->name[0] && p[1] == tp->name[1]
+	    && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+    }
+
+    /* Unknown word -- assume GMT timezone. */
+    yylval.Number = 0;
+    return tZONE;
+}
+
+
+static int
+date_lex(void)
+{
+    int	c;
+    char	*p;
+    char		buff[20];
+    int	sign;
+    int	i;
+    int	nesting;
+
+    for(;;) {
+	/* Get first character after the whitespace. */
+	for(;;) {
+	    while (CTYPE(isspace, *yyInput))
+		yyInput++;
+	    c = *yyInput;
+
+	    /* Ignore RFC 822 comments, typically time zone names. */
+	    if (c != LPAREN)
+		break;
+	    for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
+		if (c == LPAREN)
+		    nesting++;
+		else if (!IS7BIT(c) || c == '\0' || c == '\r'
+		     || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) {
+		    /* Lexical error: bad comment. */
+		    return '?';
+		}
+	    yyInput++;
+	}
+
+	/* A number? */
+	if (CTYPE(isdigit, c) || c == '-' || c == '+') {
+	    if (c == '-' || c == '+') {
+		sign = c == '-' ? -1 : 1;
+		yyInput++;
+		if (!CTYPE(isdigit, *yyInput)) {
+		    /* Return the isolated plus or minus sign. */
+		    --yyInput;
+		    return *yyInput++;
+		}
+	    }
+	    else
+		sign = 0;
+	    for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, c); )
+		i = 10 * i + c - '0';
+	    yyInput--;
+	    yylval.Number = sign < 0 ? -i : i;
+	    return sign ? tSNUMBER : tUNUMBER;
+	}
+
+	/* A word? */
+	if (CTYPE(isalpha, c)) {
+	    for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, c); )
+		if (p < &buff[sizeof buff - 1])
+		    *p++ = CTYPE(isupper, c) ? tolower(c) : c;
+	    *p = '\0';
+	    yyInput--;
+	    return LookupWord(buff, p - buff);
+	}
+
+	return *yyInput++;
+    }
+}
+
+
+static int
+GetTimeInfo(
+    TIMEINFO		*Now)
+{
+    static time_t	LastTime;
+    static long		LastTzone;
+    struct tm		*tm;
+#if	defined(HAVE_GETTIMEOFDAY)
+    struct timeval	tv;
+#endif	/* defined(HAVE_GETTIMEOFDAY) */
+#if	defined(DONT_HAVE_TM_GMTOFF)
+    struct tm		local;
+    struct tm		gmt;
+#endif	/* !defined(DONT_HAVE_TM_GMTOFF) */
+
+    /* Get the basic time. */
+#if defined(HAVE_GETTIMEOFDAY)
+    if (gettimeofday(&tv, (struct timezone *)NULL) == -1)
+	return -1;
+    Now->time = tv.tv_sec;
+    Now->usec = tv.tv_usec;
+#else
+    /* Can't check for -1 since that might be a time, I guess. */
+    (void)time(&Now->time);
+    Now->usec = 0;
+#endif /* defined(HAVE_GETTIMEOFDAY) */
+
+    /* Now get the timezone if it's been an hour since the last time. */
+    if (Now->time - LastTime > 60 * 60) {
+	LastTime = Now->time;
+	if ((tm = localtime(&Now->time)) == NULL)
+	    return -1;
+#if	defined(DONT_HAVE_TM_GMTOFF)
+	/* To get the timezone, compare localtime with GMT. */
+	local = *tm;
+	if ((tm = gmtime(&Now->time)) == NULL)
+	    return -1;
+	gmt = *tm;
+
+	/* Assume we are never more than 24 hours away. */
+	LastTzone = gmt.tm_yday - local.tm_yday;
+	if (LastTzone > 1)
+	    LastTzone = -24;
+	else if (LastTzone < -1)
+	    LastTzone = 24;
+	else
+	    LastTzone *= 24;
+
+	/* Scale in the hours and minutes; ignore seconds. */
+	LastTzone += gmt.tm_hour - local.tm_hour;
+	LastTzone *= 60;
+	LastTzone += gmt.tm_min - local.tm_min;
+#else
+	LastTzone =  (0 - tm->tm_gmtoff) / 60;
+#endif	/* defined(DONT_HAVE_TM_GMTOFF) */
+    }
+    Now->tzone = LastTzone;
+    return 0;
+}
+
+
+time_t
+parsedate(
+    char		*p,
+    TIMEINFO		*now)
+{
+    struct tm		*tm;
+    TIMEINFO		ti;
+    time_t		Start;
+
+    yyInput = p;
+    if (now == NULL) {
+	now = &ti;
+	(void)GetTimeInfo(&ti);
+    }
+
+    tm = localtime(&now->time);
+    yyYear = tm->tm_year + 1900;
+    yyMonth = tm->tm_mon + 1;
+    yyDay = tm->tm_mday;
+    yyTimezone = now->tzone;
+    if (tm->tm_isdst)                   /* Correct timezone offset for DST */
+      yyTimezone += DST_OFFSET * 60;
+    yyDSTmode = DSTmaybe;
+    yyHour = 0;
+    yyMinutes = 0;
+    yySeconds = 0;
+    yyMeridian = MER24;
+    yyRelSeconds = 0;
+    yyRelMonth = 0;
+    yyHaveDate = 0;
+    yyHaveRel = 0;
+    yyHaveTime = 0;
+
+    if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
+	return -1;
+
+    if (yyHaveDate || yyHaveTime) {
+	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+		    yyMeridian, yyDSTmode);
+	if (Start < 0)
+	    return -1;
+    } else {
+	Start = now->time;
+	if (!yyHaveRel)
+	    Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
+    }
+
+    Start += yyRelSeconds;
+    if (yyRelMonth)
+	Start += RelativeMonth(Start, yyRelMonth);
+
+    /* 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;
+}