diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 4ca3850f790..7102f64a44f 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1,1303 +1,559 @@
 /*-------------------------------------------------------------------------
  *
  * date.c
- *	  Utilities for the built-in type "AbsoluteTime" (defined in nabstime).
- *	  Functions for the built-in type "RelativeTime".
- *	  Functions for the built-in type "TimeInterval".
+ *	  implements DATE and TIME data types specified in SQL-92 standard
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.41 2000/01/26 05:57:13 momjian Exp $
- *
- * NOTES
- *	 This code is actually (almost) unused.
- *	 It needs to be integrated with Time and struct trange.
- *
- * XXX	This code needs to be rewritten to work with the "new" definitions
- * XXX	in h/tim.h.  Look for int32's, int, long, etc. in the code.  The
- * XXX	definitions in h/tim.h may need to be rethought also.
- *
- * XXX	This code has been cleaned up some - avi 07/07/93
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.42 2000/02/16 18:17:02 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/types.h>
+#include <limits.h>
 
 #include "postgres.h"
 #ifdef HAVE_FLOAT_H
 #include <float.h>
 #endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#ifndef MAXINT
-#define MAXINT		  INT_MAX
-#endif
-#else
-#ifdef HAVE_VALUES_H
-#include <values.h>
-#endif
-#endif
-
-#include "access/xact.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
-#include "utils/dt.h"
 
-#define INVALID_RELTIME_STR		"Undefined RelTime"
-#define INVALID_RELTIME_STR_LEN (sizeof(INVALID_RELTIME_STR)-1)
-#define RELTIME_LABEL			'@'
-#define RELTIME_PAST			"ago"
-#define DIRMAXLEN				(sizeof(RELTIME_PAST)-1)
+static int
+date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn);
 
-/*
- *	Unix epoch is Jan  1 00:00:00 1970.  Postgres knows about times
- *	sixty-eight years on either side of that.
- */
-
-#define IsSpace(C)				((C) == ' ')
-
-#define T_INTERVAL_INVAL   0	/* data represents no valid interval */
-#define T_INTERVAL_VALID   1	/* data represents a valid interval */
-/*
- * ['Mon May 10 23:59:12 1943 PST' 'Sun Jan 14 03:14:21 1973 PST']
- * 0		1		  2			3		  4			5		  6
- * 1234567890123456789012345678901234567890123456789012345678901234
- *
- * we allocate some extra -- timezones are usually 3 characters but
- * this is not in the POSIX standard...
- */
-#define T_INTERVAL_LEN					80
-#define INVALID_INTERVAL_STR			"Undefined Range"
-#define INVALID_INTERVAL_STR_LEN		(sizeof(INVALID_INTERVAL_STR)-1)
-
-#define ABSTIMEMIN(t1, t2) abstimele((t1),(t2)) ? (t1) : (t2)
-#define ABSTIMEMAX(t1, t2) abstimelt((t1),(t2)) ? (t2) : (t1)
-
-#ifdef NOT_USED
-static char *unit_tab[] = {
-	"second", "seconds", "minute", "minutes",
-	"hour", "hours", "day", "days", "week", "weeks",
-"month", "months", "year", "years"};
-
-#define UNITMAXLEN 7			/* max length of a unit name */
-#define NUNITS 14				/* number of different units */
-
-/* table of seconds per unit (month = 30 days, year = 365 days)  */
-static int	sec_tab[] = {
-	1, 1, 60, 60,
-	3600, 3600, 86400, 86400, 604800, 604800,
-2592000, 2592000, 31536000, 31536000};
-
-#endif
-
-/*
- * Function prototypes -- internal to this file only
- */
-
-static void reltime2tm(RelativeTime time, struct tm * tm);
-
-#ifdef NOT_USED
-static int	correct_unit(char *unit, int *unptr);
-static int	correct_dir(char *direction, int *signptr);
-
-#endif
-
-static int istinterval(char *i_string,
-			AbsoluteTime *i_start,
-			AbsoluteTime *i_end);
 
 /*****************************************************************************
- *	 USER I/O ROUTINES														 *
+ *	 Date ADT
  *****************************************************************************/
 
-/*
- *		reltimein		- converts a reltime string in an internal format
- */
-RelativeTime
-reltimein(char *str)
-{
-	RelativeTime result;
 
+/* date_in()
+ * Given date text string, convert to internal date format.
+ */
+DateADT
+date_in(char *str)
+{
+	DateADT		date;
+	double		fsec;
 	struct tm	tt,
 			   *tm = &tt;
-	double		fsec;
+	int			tzp;
 	int			dtype;
+	int			nf;
 	char	   *field[MAXDATEFIELDS];
-	int			nf,
-				ftype[MAXDATEFIELDS];
+	int			ftype[MAXDATEFIELDS];
 	char		lowstr[MAXDATELEN + 1];
 
 	if (!PointerIsValid(str))
 		elog(ERROR, "Bad (null) date external representation");
 
-	if (strlen(str) > MAXDATELEN)
-		elog(ERROR, "Bad (length) reltime external representation '%s'", str);
-
 	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
-		|| (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0))
-		elog(ERROR, "Bad reltime external representation '%s'", str);
+	 || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
+		elog(ERROR, "Bad date external representation '%s'", str);
 
 	switch (dtype)
 	{
-		case DTK_DELTA:
-			result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
-			result += (((tm->tm_year * 365) + (tm->tm_mon * 30) + tm->tm_mday) * (24 * 60 * 60));
-			return result;
+		case DTK_DATE:
+			break;
+
+		case DTK_CURRENT:
+			GetCurrentTime(tm);
+			break;
+
+		case DTK_EPOCH:
+			tm->tm_year = 1970;
+			tm->tm_mon = 1;
+			tm->tm_mday = 1;
+			break;
 
 		default:
-			return INVALID_RELTIME;
+			elog(ERROR, "Unrecognized date external representation '%s'", str);
 	}
 
-	elog(ERROR, "Bad reltime (internal coding error) '%s'", str);
-	return INVALID_RELTIME;
-}	/* reltimein() */
+	date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
 
+	return date;
+}	/* date_in() */
 
-/*
- *		reltimeout		- converts the internal format to a reltime string
+/* date_out()
+ * Given internal format date, convert to text string.
  */
 char *
-reltimeout(RelativeTime time)
+date_out(DateADT date)
 {
 	char	   *result;
 	struct tm	tt,
 			   *tm = &tt;
 	char		buf[MAXDATELEN + 1];
 
-	if (time == INVALID_RELTIME)
-	{
-		strcpy(buf, INVALID_RELTIME_STR);
+	j2date((date + date2j(2000, 1, 1)),
+		   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
 
-	}
-	else
-	{
-		reltime2tm(time, tm);
-		EncodeTimeSpan(tm, 0, DateStyle, buf);
-	}
+	EncodeDateOnly(tm, DateStyle, buf);
 
 	result = palloc(strlen(buf) + 1);
+
 	strcpy(result, buf);
 
 	return result;
-}	/* reltimeout() */
+}	/* date_out() */
 
-
-#define TMODULO(t,q,u) \
-do { \
-	q = (t / u); \
-	if (q != 0) t -= (q * u); \
-} while(0)
-
-static void
-reltime2tm(RelativeTime time, struct tm * tm)
+bool
+date_eq(DateADT dateVal1, DateADT dateVal2)
 {
-	TMODULO(time, tm->tm_year, 31536000);
-	TMODULO(time, tm->tm_mon, 2592000);
-	TMODULO(time, tm->tm_mday, 86400);
-	TMODULO(time, tm->tm_hour, 3600);
-	TMODULO(time, tm->tm_min, 60);
-	TMODULO(time, tm->tm_sec, 1);
+	return dateVal1 == dateVal2;
+}
 
-	return;
-}	/* reltime2tm() */
+bool
+date_ne(DateADT dateVal1, DateADT dateVal2)
+{
+	return dateVal1 != dateVal2;
+}
+
+bool
+date_lt(DateADT dateVal1, DateADT dateVal2)
+{
+	return dateVal1 < dateVal2;
+}	/* date_lt() */
+
+bool
+date_le(DateADT dateVal1, DateADT dateVal2)
+{
+	return dateVal1 <= dateVal2;
+}	/* date_le() */
+
+bool
+date_gt(DateADT dateVal1, DateADT dateVal2)
+{
+	return dateVal1 > dateVal2;
+}	/* date_gt() */
+
+bool
+date_ge(DateADT dateVal1, DateADT dateVal2)
+{
+	return dateVal1 >= dateVal2;
+}	/* date_ge() */
 
-#ifdef NOT_USED
 int
-dummyfunc()
+date_cmp(DateADT dateVal1, DateADT dateVal2)
 {
-	char	   *timestring;
-	long		quantity;
-	int			i;
-	int			unitnr;
+	if (dateVal1 < dateVal2)
+		return -1;
+	else if (dateVal1 > dateVal2)
+		return 1;
+	return 0;
+}	/* date_cmp() */
 
-	timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR),
-									 UNITMAXLEN) + 1);
-	if (timevalue == INVALID_RELTIME)
-	{
-		strcpy(timestring, INVALID_RELTIME_STR);
-		return timestring;
-	}
+DateADT
+date_larger(DateADT dateVal1, DateADT dateVal2)
+{
+	return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
+}	/* date_larger() */
 
-	if (timevalue == 0)
-		i = 1;					/* unit = 'seconds' */
-	else
-		for (i = 12; i >= 0; i = i - 2)
-			if ((timevalue % sec_tab[i]) == 0)
-				break;			/* appropriate unit found */
-	unitnr = i;
-	quantity = (timevalue / sec_tab[unitnr]);
-	if (quantity > 1 || quantity < -1)
-		unitnr++;				/* adjust index for PLURAL of unit */
-	if (quantity >= 0)
-		sprintf(timestring, "%c %lu %s", RELTIME_LABEL,
-				quantity, unit_tab[unitnr]);
-	else
-		sprintf(timestring, "%c %lu %s %s", RELTIME_LABEL,
-				(quantity * -1), unit_tab[unitnr], RELTIME_PAST);
-	return timestring;
-}
+DateADT
+date_smaller(DateADT dateVal1, DateADT dateVal2)
+{
+	return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
+}	/* date_smaller() */
 
-#endif
-
-
-/*
- *		tintervalin		- converts an interval string to an internal format
+/* Compute difference between two dates in days.
  */
-TimeInterval
-tintervalin(char *intervalstr)
+int4
+date_mi(DateADT dateVal1, DateADT dateVal2)
 {
-	int			error;
-	AbsoluteTime i_start,
-				i_end,
-				t1,
-				t2;
-	TimeInterval interval;
+	return dateVal1 - dateVal2;
+}	/* date_mi() */
 
-	interval = (TimeInterval) palloc(sizeof(TimeIntervalData));
-	error = istinterval(intervalstr, &t1, &t2);
-	if (error == 0)
-		interval->status = T_INTERVAL_INVAL;
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		interval->status = T_INTERVAL_INVAL;	/* undefined  */
-	else
-	{
-		i_start = ABSTIMEMIN(t1, t2);
-		i_end = ABSTIMEMAX(t1, t2);
-		interval->data[0] = i_start;
-		interval->data[1] = i_end;
-		interval->status = T_INTERVAL_VALID;
-	}
-	return interval;
-}
-
-
-/*
- *		tintervalout	- converts an internal interval format to a string
- *
+/* Add a number of days to a date, giving a new date.
+ * Must handle both positive and negative numbers of days.
  */
-char *
-tintervalout(TimeInterval interval)
+DateADT
+date_pli(DateADT dateVal, int4 days)
 {
-	char	   *i_str,
-			   *p;
+	return dateVal + days;
+}	/* date_pli() */
 
-	i_str = (char *) palloc(T_INTERVAL_LEN);	/* ['...' '...'] */
-	strcpy(i_str, "[\"");
-	if (interval->status == T_INTERVAL_INVAL)
-		strcat(i_str, INVALID_INTERVAL_STR);
-	else
+/* Subtract a number of days from a date, giving a new date.
+ */
+DateADT
+date_mii(DateADT dateVal, int4 days)
+{
+	return date_pli(dateVal, -days);
+}	/* date_mii() */
+
+
+/* date_timestamp()
+ * Convert date to timestamp data type.
+ */
+Timestamp   *
+date_timestamp(DateADT dateVal)
+{
+	Timestamp   *result;
+	struct tm	tt,
+			   *tm = &tt;
+	int			tz;
+	double		fsec = 0;
+	char	   *tzn;
+
+	result = palloc(sizeof(Timestamp));
+
+	if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0)
+		elog(ERROR, "Unable to convert date to timestamp");
+
+	if (tm2timestamp(tm, fsec, &tz, result) != 0)
+		elog(ERROR, "Timestamp out of range");
+
+	return result;
+}	/* date_timestamp() */
+
+
+/* timestamp_date()
+ * Convert timestamp to date data type.
+ */
+DateADT
+timestamp_date(Timestamp *timestamp)
+{
+	DateADT		result;
+	struct tm	tt,
+			   *tm = &tt;
+	int			tz;
+	double		fsec;
+	char	   *tzn;
+
+	if (!PointerIsValid(timestamp))
+		elog(ERROR, "Unable to convert null timestamp to date");
+
+	if (TIMESTAMP_NOT_FINITE(*timestamp))
+		elog(ERROR, "Unable to convert timestamp to date");
+
+	if (TIMESTAMP_IS_EPOCH(*timestamp))
 	{
-		p = nabstimeout(interval->data[0]);
-		strcat(i_str, p);
-		pfree(p);
-		strcat(i_str, "\" \"");
-		p = nabstimeout(interval->data[1]);
-		strcat(i_str, p);
-		pfree(p);
+		timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
+
 	}
-	strcat(i_str, "\"]\0");
-	return i_str;
-}
-
-
-/*****************************************************************************
- *	 PUBLIC ROUTINES														 *
- *****************************************************************************/
-
-RelativeTime
-timespan_reltime(TimeSpan *timespan)
-{
-	RelativeTime time;
-	int			year,
-				month;
-	double		span;
-
-	if (!PointerIsValid(timespan))
-		time = INVALID_RELTIME;
-
-	if (TIMESPAN_IS_INVALID(*timespan))
+	else if (TIMESTAMP_IS_CURRENT(*timestamp))
 	{
-		time = INVALID_RELTIME;
+		timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
 
 	}
 	else
 	{
-		if (timespan->month == 0)
-		{
-			year = 0;
-			month = 0;
-
-		}
-		else if (abs(timespan->month) >= 12)
-		{
-			year = (timespan->month / 12);
-			month = (timespan->month % 12);
-
-		}
-		else
-		{
-			year = 0;
-			month = timespan->month;
-		}
-
-		span = (((((double) 365 * year) + ((double) 30 * month)) * 86400) + timespan->time);
-
-		time = (((span > INT_MIN) && (span < INT_MAX)) ? span : INVALID_RELTIME);
+		if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
+			elog(ERROR, "Unable to convert timestamp to date");
 	}
 
-	return time;
-}	/* timespan_reltime() */
+	result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
+
+	return result;
+}	/* timestamp_date() */
 
 
-TimeSpan   *
-reltime_timespan(RelativeTime reltime)
+/* abstime_date()
+ * Convert abstime to date data type.
+ */
+DateADT
+abstime_date(AbsoluteTime abstime)
 {
-	TimeSpan   *result;
-	int			year,
-				month;
+	DateADT		result;
+	struct tm	tt,
+			   *tm = &tt;
+	int			tz;
 
-	if (!PointerIsValid(result = palloc(sizeof(TimeSpan))))
-		elog(ERROR, "Memory allocation failed, can't convert reltime to timespan");
-
-	switch (reltime)
+	switch (abstime)
 	{
-		case INVALID_RELTIME:
-			TIMESPAN_INVALID(*result);
+		case INVALID_ABSTIME:
+		case NOSTART_ABSTIME:
+		case NOEND_ABSTIME:
+			elog(ERROR, "Unable to convert reserved abstime value to date");
+
+			/*
+			 * pretend to drop through to make compiler think that result
+			 * will be set
+			 */
+
+		case EPOCH_ABSTIME:
+			result = date2j(1970, 1, 1) - date2j(2000, 1, 1);
+			break;
+
+		case CURRENT_ABSTIME:
+			GetCurrentTime(tm);
+			result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
 			break;
 
 		default:
-			TMODULO(reltime, year, 31536000);
-			TMODULO(reltime, month, 2592000);
-
-			result->time = reltime;
-			result->month = ((12 * year) + month);
+			abstime2tm(abstime, &tz, tm, NULL);
+			result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
+			break;
 	}
 
 	return result;
-}	/* reltime_timespan() */
+}	/* abstime_date() */
 
 
-/*
- *		mktinterval		- creates a time interval with endpoints t1 and t2
+/* date2tm()
+ * Convert date to time structure.
+ * Note that date is an implicit local time, but the system calls assume
+ *	that everything is GMT. So, convert to GMT, rotate to local time,
+ *	and then convert again to try to get the time zones correct.
  */
-TimeInterval
-mktinterval(AbsoluteTime t1, AbsoluteTime t2)
+static int
+date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn)
 {
-	AbsoluteTime tstart = ABSTIMEMIN(t1, t2),
-				tend = ABSTIMEMAX(t1, t2);
-	TimeInterval interval;
+	struct tm  *tx;
+	time_t		utime;
 
-	interval = (TimeInterval) palloc(sizeof(TimeIntervalData));
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		interval->status = T_INTERVAL_INVAL;
+	*fsec = 0;
+
+	j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
+	tm->tm_hour = 0;
+	tm->tm_min = 0;
+	tm->tm_sec = 0;
+	tm->tm_isdst = -1;
+
+	if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+	{
+
+		/* convert to system time */
+		utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400);
+		/* rotate to noon to get the right day in time zone */
+		utime += (12 * 60 * 60);
+
+#ifdef USE_POSIX_TIME
+		tx = localtime(&utime);
+
+		tm->tm_year = tx->tm_year + 1900;
+		tm->tm_mon = tx->tm_mon + 1;
+		tm->tm_mday = tx->tm_mday;
+		tm->tm_isdst = tx->tm_isdst;
+
+#if defined(HAVE_TM_ZONE)
+		tm->tm_gmtoff = tx->tm_gmtoff;
+		tm->tm_zone = tx->tm_zone;
+
+		/* tm_gmtoff is Sun/DEC-ism */
+		*tzp = -(tm->tm_gmtoff);
+		if (tzn != NULL)
+			*tzn = (char *) tm->tm_zone;
+#elif defined(HAVE_INT_TIMEZONE)
+#ifdef __CYGWIN__
+		*tzp = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
+#else
+		*tzp = (tm->tm_isdst ? (timezone - 3600) : timezone);
+#endif
+		if (tzn != NULL)
+			*tzn = tzname[(tm->tm_isdst > 0)];
+#else
+#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
+#endif
+#else							/* !USE_POSIX_TIME */
+		*tzp = CTimeZone;		/* V7 conventions; don't know timezone? */
+		if (tzn != NULL)
+			*tzn = CTZName;
+#endif
+
+		/* otherwise, outside of timezone range so convert to GMT... */
+	}
 	else
 	{
-		interval->status = T_INTERVAL_VALID;
-		interval->data[0] = tstart;
-		interval->data[1] = tend;
+		*tzp = 0;
+		tm->tm_isdst = 0;
+		if (tzn != NULL)
+			*tzn = NULL;
 	}
 
-	return interval;
-}
-
-/*
- *		timepl, timemi and abstimemi use the formula
- *				abstime + reltime = abstime
- *		so		abstime - reltime = abstime
- *		and		abstime - abstime = reltime
- */
-
-/*
- *		timepl			- returns the value of (abstime t1 + relime t2)
- */
-AbsoluteTime
-timepl(AbsoluteTime t1, RelativeTime t2)
-{
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-
-	if (AbsoluteTimeIsReal(t1) &&
-		RelativeTimeIsValid(t2) &&
-		((t2 > 0) ? (t1 < NOEND_ABSTIME - t2)
-		 : (t1 > NOSTART_ABSTIME - t2)))		/* prevent overflow */
-		return t1 + t2;
-
-	return INVALID_ABSTIME;
-}
-
-
-/*
- *		timemi			- returns the value of (abstime t1 - reltime t2)
- */
-AbsoluteTime
-timemi(AbsoluteTime t1, RelativeTime t2)
-{
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-
-	if (AbsoluteTimeIsReal(t1) &&
-		RelativeTimeIsValid(t2) &&
-		((t2 > 0) ? (t1 > NOSTART_ABSTIME + t2)
-		 : (t1 < NOEND_ABSTIME + t2)))	/* prevent overflow */
-		return t1 - t2;
-
-	return INVALID_ABSTIME;
-}
-
-
-/*
- *		abstimemi		- returns the value of (abstime t1 - abstime t2)
- */
-static RelativeTime
-abstimemi(AbsoluteTime t1, AbsoluteTime t2)
-{
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
-
-	if (AbsoluteTimeIsReal(t1) &&
-		AbsoluteTimeIsReal(t2))
-		return t1 - t2;
-
-	return INVALID_RELTIME;
-}
-
-
-/*
- *		ininterval		- returns 1, iff absolute date is in the interval
- */
-int
-ininterval(AbsoluteTime t, TimeInterval interval)
-{
-	if (interval->status == T_INTERVAL_VALID && t != INVALID_ABSTIME)
-		return (abstimege(t, interval->data[0]) &&
-				abstimele(t, interval->data[1]));
 	return 0;
-}
-
-/*
- *		intervalrel		- returns  relative time corresponding to interval
- */
-RelativeTime
-intervalrel(TimeInterval interval)
-{
-	if (interval->status == T_INTERVAL_VALID)
-		return abstimemi(interval->data[1], interval->data[0]);
-	else
-		return INVALID_RELTIME;
-}
-
-/*
- *		timenow			- returns  time "now", internal format
- *
- *		Now AbsoluteTime is time since Jan 1 1970 -mer 7 Feb 1992
- */
-AbsoluteTime
-timenow()
-{
-	time_t		sec;
-
-	if (time(&sec) < 0)
-		return INVALID_ABSTIME;
-	return (AbsoluteTime) sec;
-}
-
-/*
- *		reltimeeq		- returns 1, iff arguments are equal
- *		reltimene		- returns 1, iff arguments are not equal
- *		reltimelt		- returns 1, iff t1 less than t2
- *		reltimegt		- returns 1, iff t1 greater than t2
- *		reltimele		- returns 1, iff t1 less than or equal to t2
- *		reltimege		- returns 1, iff t1 greater than or equal to t2
- */
-bool
-reltimeeq(RelativeTime t1, RelativeTime t2)
-{
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		return 0;
-	return t1 == t2;
-}
-
-bool
-reltimene(RelativeTime t1, RelativeTime t2)
-{
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		return 0;
-	return t1 != t2;
-}
-
-bool
-reltimelt(RelativeTime t1, RelativeTime t2)
-{
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		return 0;
-	return t1 < t2;
-}
-
-bool
-reltimegt(RelativeTime t1, RelativeTime t2)
-{
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		return 0;
-	return t1 > t2;
-}
-
-bool
-reltimele(RelativeTime t1, RelativeTime t2)
-{
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		return 0;
-	return t1 <= t2;
-}
-
-bool
-reltimege(RelativeTime t1, RelativeTime t2)
-{
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		return 0;
-	return t1 >= t2;
-}
-
-
-/*
- *		intervalsame	- returns 1, iff interval i1 is same as interval i2
- *		Check begin and end time.
- */
-bool
-intervalsame(TimeInterval i1, TimeInterval i2)
-{
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-	return (abstimeeq(i1->data[0], i2->data[0]) &&
-			abstimeeq(i1->data[1], i2->data[1]));
-}	/* intervalsame() */
-
-
-/*
- *		intervaleq		- returns 1, iff interval i1 is equal to interval i2
- *		Check length of intervals.
- */
-bool
-intervaleq(TimeInterval i1, TimeInterval i2)
-{
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		return FALSE;
-
-	if (t10 == CURRENT_ABSTIME)
-		t10 = GetCurrentTransactionStartTime();
-	if (t11 == CURRENT_ABSTIME)
-		t11 = GetCurrentTransactionStartTime();
-	if (t20 == CURRENT_ABSTIME)
-		t20 = GetCurrentTransactionStartTime();
-	if (t21 == CURRENT_ABSTIME)
-		t21 = GetCurrentTransactionStartTime();
-
-	return (t11 - t10) == (t21 - t20);
-}	/* intervaleq() */
-
-/*
- *		intervalne		- returns 1, iff interval i1 is not equal to interval i2
- *		Check length of intervals.
- */
-bool
-intervalne(TimeInterval i1, TimeInterval i2)
-{
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		return FALSE;
-
-	if (t10 == CURRENT_ABSTIME)
-		t10 = GetCurrentTransactionStartTime();
-	if (t11 == CURRENT_ABSTIME)
-		t11 = GetCurrentTransactionStartTime();
-	if (t20 == CURRENT_ABSTIME)
-		t20 = GetCurrentTransactionStartTime();
-	if (t21 == CURRENT_ABSTIME)
-		t21 = GetCurrentTransactionStartTime();
-
-	return (t11 - t10) != (t21 - t20);
-}	/* intervalne() */
-
-/*
- *		intervallt		- returns TRUE, iff interval i1 is less than interval i2
- *		Check length of intervals.
- */
-bool
-intervallt(TimeInterval i1, TimeInterval i2)
-{
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		return FALSE;
-
-	if (t10 == CURRENT_ABSTIME)
-		t10 = GetCurrentTransactionStartTime();
-	if (t11 == CURRENT_ABSTIME)
-		t11 = GetCurrentTransactionStartTime();
-	if (t20 == CURRENT_ABSTIME)
-		t20 = GetCurrentTransactionStartTime();
-	if (t21 == CURRENT_ABSTIME)
-		t21 = GetCurrentTransactionStartTime();
-
-	return (t11 - t10) < (t21 - t20);
-}	/* intervallt() */
-
-/*
- *		intervalle		- returns TRUE, iff interval i1 is less than or equal to interval i2
- *		Check length of intervals.
- */
-bool
-intervalle(TimeInterval i1, TimeInterval i2)
-{
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		return FALSE;
-
-	if (t10 == CURRENT_ABSTIME)
-		t10 = GetCurrentTransactionStartTime();
-	if (t11 == CURRENT_ABSTIME)
-		t11 = GetCurrentTransactionStartTime();
-	if (t20 == CURRENT_ABSTIME)
-		t20 = GetCurrentTransactionStartTime();
-	if (t21 == CURRENT_ABSTIME)
-		t21 = GetCurrentTransactionStartTime();
-
-	return (t11 - t10) <= (t21 - t20);
-}	/* intervalle() */
-
-/*
- *		intervalgt		- returns TRUE, iff interval i1 is less than interval i2
- *		Check length of intervals.
- */
-bool
-intervalgt(TimeInterval i1, TimeInterval i2)
-{
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		return FALSE;
-
-	if (t10 == CURRENT_ABSTIME)
-		t10 = GetCurrentTransactionStartTime();
-	if (t11 == CURRENT_ABSTIME)
-		t11 = GetCurrentTransactionStartTime();
-	if (t20 == CURRENT_ABSTIME)
-		t20 = GetCurrentTransactionStartTime();
-	if (t21 == CURRENT_ABSTIME)
-		t21 = GetCurrentTransactionStartTime();
-
-	return (t11 - t10) > (t21 - t20);
-}	/* intervalgt() */
-
-/*
- *		intervalge		- returns TRUE, iff interval i1 is less than or equal to interval i2
- *		Check length of intervals.
- */
-bool
-intervalge(TimeInterval i1, TimeInterval i2)
-{
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return FALSE;			/* invalid interval */
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t20 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		return FALSE;
-
-	if (t10 == CURRENT_ABSTIME)
-		t10 = GetCurrentTransactionStartTime();
-	if (t11 == CURRENT_ABSTIME)
-		t11 = GetCurrentTransactionStartTime();
-	if (t20 == CURRENT_ABSTIME)
-		t20 = GetCurrentTransactionStartTime();
-	if (t21 == CURRENT_ABSTIME)
-		t21 = GetCurrentTransactionStartTime();
-
-	return (t11 - t10) >= (t21 - t20);
-}	/* intervalge() */
-
-
-/*
- *		intervalleneq	- returns 1, iff length of interval i is equal to
- *								reltime t
- */
-bool
-intervalleneq(TimeInterval i, RelativeTime t)
-{
-	RelativeTime rt;
-
-	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
-		return 0;
-	rt = intervalrel(i);
-	return rt != INVALID_RELTIME && rt == t;
-}
-
-/*
- *		intervallenne	- returns 1, iff length of interval i is not equal
- *								to reltime t
- */
-bool
-intervallenne(TimeInterval i, RelativeTime t)
-{
-	RelativeTime rt;
-
-	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
-		return 0;
-	rt = intervalrel(i);
-	return rt != INVALID_RELTIME && rt != t;
-}
-
-/*
- *		intervallenlt	- returns 1, iff length of interval i is less than
- *								reltime t
- */
-bool
-intervallenlt(TimeInterval i, RelativeTime t)
-{
-	RelativeTime rt;
-
-	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
-		return 0;
-	rt = intervalrel(i);
-	return rt != INVALID_RELTIME && rt < t;
-}
-
-/*
- *		intervallengt	- returns 1, iff length of interval i is greater than
- *								reltime t
- */
-bool
-intervallengt(TimeInterval i, RelativeTime t)
-{
-	RelativeTime rt;
-
-	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
-		return 0;
-	rt = intervalrel(i);
-	return rt != INVALID_RELTIME && rt > t;
-}
-
-/*
- *		intervallenle	- returns 1, iff length of interval i is less or equal
- *									than reltime t
- */
-bool
-intervallenle(TimeInterval i, RelativeTime t)
-{
-	RelativeTime rt;
-
-	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
-		return 0;
-	rt = intervalrel(i);
-	return rt != INVALID_RELTIME && rt <= t;
-}
-
-/*
- *		intervallenge	- returns 1, iff length of interval i is greater or
- *								equal than reltime t
- */
-bool
-intervallenge(TimeInterval i, RelativeTime t)
-{
-	RelativeTime rt;
-
-	if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
-		return 0;
-	rt = intervalrel(i);
-	return rt != INVALID_RELTIME && rt >= t;
-}
-
-/*
- *		intervalct		- returns 1, iff interval i1 contains interval i2
- */
-bool
-intervalct(TimeInterval i1, TimeInterval i2)
-{
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return 0;
-	return (abstimele(i1->data[0], i2->data[0]) &&
-			abstimege(i1->data[1], i2->data[1]));
-}
-
-/*
- *		intervalov		- returns 1, iff interval i1 (partially) overlaps i2
- */
-bool
-intervalov(TimeInterval i1, TimeInterval i2)
-{
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		return 0;
-	return (!(abstimelt(i1->data[1], i2->data[0]) ||
-			  abstimegt(i1->data[0], i2->data[1])));
-}
-
-/*
- *		intervalstart	- returns  the start of interval i
- */
-AbsoluteTime
-intervalstart(TimeInterval i)
-{
-	if (i->status == T_INTERVAL_INVAL)
-		return INVALID_ABSTIME;
-	return i->data[0];
-}
-
-/*
- *		intervalend		- returns  the end of interval i
- */
-AbsoluteTime
-intervalend(TimeInterval i)
-{
-	if (i->status == T_INTERVAL_INVAL)
-		return INVALID_ABSTIME;
-	return i->data[1];
-}
+}	/* date2tm() */
 
 
 /*****************************************************************************
- *	 PRIVATE ROUTINES														 *
+ *	 Time ADT
  *****************************************************************************/
 
-#ifdef NOT_USED
-/*
- *		isreltime		- returns 1, iff datestring is of type reltime
- *								  2, iff datestring is 'invalid time' identifier
- *								  0, iff datestring contains a syntax error
- *		VALID time	less or equal +/-  `@ 68 years'
- *
- */
-int
-isreltime(char *str)
+
+TimeADT    *
+time_in(char *str)
 {
+	TimeADT    *time;
+
+	double		fsec;
 	struct tm	tt,
 			   *tm = &tt;
-	double		fsec;
-	int			dtype;
-	char	   *field[MAXDATEFIELDS];
-	int			nf,
-				ftype[MAXDATEFIELDS];
+
+	int			nf;
 	char		lowstr[MAXDATELEN + 1];
+	char	   *field[MAXDATEFIELDS];
+	int			dtype;
+	int			ftype[MAXDATEFIELDS];
 
 	if (!PointerIsValid(str))
-		return 0;
-
-	if (strlen(str) > MAXDATELEN)
-		return 0;
+		elog(ERROR, "Bad (null) time external representation");
 
 	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
-		|| (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0))
-		return 0;
+		|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec) != 0))
+		elog(ERROR, "Bad time external representation '%s'", str);
 
-	switch (dtype)
-	{
-		case (DTK_DELTA):
-			return (abs(tm->tm_year) <= 68) ? 1 : 0;
-			break;
+	time = palloc(sizeof(TimeADT));
 
-		case (DTK_INVALID):
-			return 2;
-			break;
+	*time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
 
-		default:
-			return 0;
-			break;
-	}
+	return time;
+}	/* time_in() */
 
-	return 0;
-}	/* isreltime() */
 
-#endif
+char *
+time_out(TimeADT *time)
+{
+	char	   *result;
+	struct tm	tt,
+			   *tm = &tt;
+
+	double		fsec;
+	char		buf[MAXDATELEN + 1];
+
+	if (!PointerIsValid(time))
+		return NULL;
+
+	tm->tm_hour = (*time / (60 * 60));
+	tm->tm_min = (((int) (*time / 60)) % 60);
+	tm->tm_sec = (((int) *time) % 60);
+
+	fsec = 0;
+
+	EncodeTimeOnly(tm, fsec, DateStyle, buf);
+
+	result = palloc(strlen(buf) + 1);
+
+	strcpy(result, buf);
+
+	return result;
+}	/* time_out() */
+
+
+bool
+time_eq(TimeADT *time1, TimeADT *time2)
+{
+	if (!PointerIsValid(time1) || !PointerIsValid(time2))
+		return FALSE;
+
+	return *time1 == *time2;
+}	/* time_eq() */
+
+bool
+time_ne(TimeADT *time1, TimeADT *time2)
+{
+	if (!PointerIsValid(time1) || !PointerIsValid(time2))
+		return FALSE;
+
+	return *time1 != *time2;
+}	/* time_eq() */
+
+bool
+time_lt(TimeADT *time1, TimeADT *time2)
+{
+	if (!PointerIsValid(time1) || !PointerIsValid(time2))
+		return FALSE;
+
+	return *time1 < *time2;
+}	/* time_eq() */
+
+bool
+time_le(TimeADT *time1, TimeADT *time2)
+{
+	if (!PointerIsValid(time1) || !PointerIsValid(time2))
+		return FALSE;
+
+	return *time1 <= *time2;
+}	/* time_eq() */
+
+bool
+time_gt(TimeADT *time1, TimeADT *time2)
+{
+	if (!PointerIsValid(time1) || !PointerIsValid(time2))
+		return FALSE;
+
+	return *time1 > *time2;
+}	/* time_eq() */
+
+bool
+time_ge(TimeADT *time1, TimeADT *time2)
+{
+	if (!PointerIsValid(time1) || !PointerIsValid(time2))
+		return FALSE;
+
+	return *time1 >= *time2;
+}	/* time_eq() */
 
-#ifdef NOT_USED
 int
-dummyfunc()
+time_cmp(TimeADT *time1, TimeADT *time2)
 {
-	char	   *p;
-	char		c;
-	int			i;
-	char		unit[UNITMAXLEN];
-	char		direction[DIRMAXLEN];
-	int			localSign;
-	int			localUnitNumber;
-	long		localQuantity;
+	return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0));
+}	/* time_cmp() */
 
-	if (!PointerIsValid(sign))
-		sign = &localSign;
 
-	if (!PointerIsValid(unitnr))
-		unitnr = &localUnitNumber;
-
-	if (!PointerIsValid(quantity))
-		quantity = &localQuantity;
-
-	unit[0] = '\0';
-	direction[0] = '\0';
-	p = timestring;
-	/* skip leading blanks */
-	while ((c = *p) != '\0')
-	{
-		if (c != ' ')
-			break;
-		p++;
-	}
-
-	/* Test whether 'invalid time' identifier or not */
-	if (!strncmp(INVALID_RELTIME_STR, p, strlen(INVALID_RELTIME_STR) + 1))
-		return 2;				/* correct 'invalid time' identifier found */
-
-	/* handle label of relative time */
-	if (c != RELTIME_LABEL)
-		return 0;				/* syntax error */
-	c = *++p;
-	if (c != ' ')
-		return 0;				/* syntax error */
-	p++;
-	/* handle the quantity */
-	*quantity = 0;
-	for (;;)
-	{
-		c = *p;
-		if (isdigit(c))
-		{
-			*quantity = *quantity * 10 + (c - '0');
-			p++;
-		}
-		else
-		{
-			if (c == ' ')
-				break;			/* correct quantity found */
-			else
-				return 0;		/* syntax error */
-		}
-	}
-
-	/* handle unit */
-	p++;
-	i = 0;
-	for (;;)
-	{
-		c = *p;
-		if (c >= 'a' && c <= 'z' && i <= (UNITMAXLEN - 1))
-		{
-			unit[i] = c;
-			p++;
-			i++;
-		}
-		else
-		{
-			if ((c == ' ' || c == '\0')
-				&& correct_unit(unit, unitnr))
-				break;			/* correct unit found */
-			else
-				return 0;		/* syntax error */
-		}
-	}
-
-	/* handle optional direction */
-	if (c == ' ')
-		p++;
-	i = 0;
-	*sign = 1;
-	for (;;)
-	{
-		c = *p;
-		if (c >= 'a' && c <= 'z' && i <= (DIRMAXLEN - 1))
-		{
-			direction[i] = c;
-			p++;
-			i++;
-		}
-		else
-		{
-			if ((c == ' ' || c == '\0') && i == 0)
-			{
-				*sign = 1;
-				break;			/* no direction specified */
-			}
-			if ((c == ' ' || c == '\0') && i != 0)
-			{
-				direction[i] = '\0';
-				correct_dir(direction, sign);
-				break;			/* correct direction found */
-			}
-			else
-				return 0;		/* syntax error */
-		}
-	}
-
-	return 1;
-}
-
-/*
- *		correct_unit	- returns 1, iff unit is a correct unit description
- *
- *		output parameter:
- *				unptr: points to an integer which is the appropriate unit number
- *					   (see function isreltime())
+/* timestamp_time()
+ * Convert timestamp to time data type.
  */
-static int
-correct_unit(char *unit, int *unptr)
+TimeADT    *
+timestamp_time(Timestamp *timestamp)
 {
-	int			j = 0;
+	TimeADT    *result;
+	struct tm	tt,
+			   *tm = &tt;
+	int			tz;
+	double		fsec;
+	char	   *tzn;
 
-	while (j < NUNITS)
+	if (!PointerIsValid(timestamp))
+		elog(ERROR, "Unable to convert null timestamp to date");
+
+	if (TIMESTAMP_NOT_FINITE(*timestamp))
+		elog(ERROR, "Unable to convert timestamp to date");
+
+	if (TIMESTAMP_IS_EPOCH(*timestamp))
 	{
-		if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0)
-		{
-			*unptr = j;
-			return 1;
-		}
-		j++;
+		timestamp2tm(SetTimestamp(*timestamp), NULL, tm, &fsec, NULL);
+
 	}
-	return 0;					/* invalid unit descriptor */
-}
-
-/*
- *		correct_dir		- returns 1, iff direction is a correct identifier
- *
- *		output parameter:
- *				signptr: points to -1 if dir corresponds to past tense
- *						 else  to 1
- */
-static int
-correct_dir(char *direction, int *signptr)
-{
-	*signptr = 1;
-	if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST) + 1) == 0)
+	else if (TIMESTAMP_IS_CURRENT(*timestamp))
 	{
-		*signptr = -1;
-		return 1;
+		timestamp2tm(SetTimestamp(*timestamp), &tz, tm, &fsec, &tzn);
+
 	}
 	else
-		return 0;				/* invalid direction descriptor */
-}
+	{
+		if (timestamp2tm(*timestamp, &tz, tm, &fsec, &tzn) != 0)
+			elog(ERROR, "Unable to convert timestamp to date");
+	}
 
-#endif
+	result = palloc(sizeof(TimeADT));
 
-/*
- *		istinterval		- returns 1, iff i_string is a valid interval descr.
- *								  0, iff i_string is NOT a valid interval desc.
- *								  2, iff any time is INVALID_ABSTIME
- *
- *		output parameter:
- *				i_start, i_end: interval margins
- *
- *		Time interval:
- *		`[' {` '} `'' <AbsTime> `'' {` '} `'' <AbsTime> `'' {` '} `]'
- *
- *		OR	`Undefined Range'	(see also INVALID_INTERVAL_STR)
- *
- *		where <AbsTime> satisfies the syntax of absolute time.
- *
- *		e.g.  [  '  Jan 18 1902'   'Jan 1 00:00:00 1970']
+	*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
+
+	return result;
+}	/* timestamp_time() */
+
+
+/* datetime_timestamp()
+ * Convert date and time to timestamp data type.
  */
-static int
-istinterval(char *i_string,
-			AbsoluteTime *i_start,
-			AbsoluteTime *i_end)
+Timestamp   *
+datetime_timestamp(DateADT date, TimeADT *time)
 {
-	char	   *p,
-			   *p1;
-	char		c;
+	Timestamp   *result;
 
-	p = i_string;
-	/* skip leading blanks up to '[' */
-	while ((c = *p) != '\0')
+	if (!PointerIsValid(time))
 	{
-		if (IsSpace(c))
-			p++;
-		else if (c != '[')
-			return 0;			/* syntax error */
-		else
-			break;
+		result = palloc(sizeof(Timestamp));
+		TIMESTAMP_INVALID(*result);
 	}
-	p++;
-	/* skip leading blanks up to "'" */
-	while ((c = *p) != '\0')
+	else
 	{
-		if (IsSpace(c))
-			p++;
-		else if (c != '"')
-			return 0;			/* syntax error */
-		else
-			break;
+		result = date_timestamp(date);
+		*result += *time;
 	}
-	p++;
-	if (strncmp(INVALID_INTERVAL_STR, p, strlen(INVALID_INTERVAL_STR)) == 0)
-		return 0;				/* undefined range, handled like a syntax
-								 * err. */
-	/* search for the end of the first date and change it to a NULL */
-	p1 = p;
-	while ((c = *p1) != '\0')
-	{
-		if (c == '"')
-		{
-			*p1 = '\0';
-			break;
-		}
-		p1++;
-	}
-	/* get the first date */
-	*i_start = nabstimein(p);	/* first absolute date */
-	/* rechange NULL at the end of the first date to a "'" */
-	*p1 = '"';
-	p = ++p1;
-	/* skip blanks up to "'", beginning of second date */
-	while ((c = *p) != '\0')
-	{
-		if (IsSpace(c))
-			p++;
-		else if (c != '"')
-			return 0;			/* syntax error */
-		else
-			break;
-	}
-	p++;
-	/* search for the end of the second date and change it to a NULL */
-	p1 = p;
-	while ((c = *p1) != '\0')
-	{
-		if (c == '"')
-		{
-			*p1 = '\0';
-			break;
-		}
-		p1++;
-	}
-	/* get the second date */
-	*i_end = nabstimein(p);		/* second absolute date */
-	/* rechange NULL at the end of the first date to a ''' */
-	*p1 = '"';
-	p = ++p1;
-	/* skip blanks up to ']' */
-	while ((c = *p) != '\0')
-	{
-		if (IsSpace(c))
-			p++;
-		else if (c != ']')
-			return 0;			/* syntax error */
-		else
-			break;
-	}
-	p++;
-	c = *p;
-	if (c != '\0')
-		return 0;				/* syntax error */
-	/* it seems to be a valid interval */
-	return 1;
-}
 
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-/*
- * timeofday -
- *	   returns the current time as a text. similar to timenow() but returns
- *	   seconds with more precision (up to microsecs). (I need this to compare
- *	   the Wisconsin benchmark with Illustra whose TimeNow() shows current
- *	   time with precision up to microsecs.)			  - ay 3/95
- */
-text *
-timeofday(void)
-{
-
-	struct timeval tp;
-	struct timezone tpz;
-	char		templ[500];
-	char		buf[500];
-	text	   *tm;
-	int			len = 0;
-
-	gettimeofday(&tp, &tpz);
-	strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%d %Y %Z",
-			 localtime((time_t *) &tp.tv_sec));
-	sprintf(buf, templ, tp.tv_usec);
-
-	len = VARHDRSZ + strlen(buf);
-	tm = (text *) palloc(len);
-	VARSIZE(tm) = len;
-	strncpy(VARDATA(tm), buf, strlen(buf));
-	return tm;
-}
+	return result;
+}	/* datetime_timestamp() */
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 7a9df275fa7..1418a0c34c2 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -1,565 +1,2184 @@
 /*-------------------------------------------------------------------------
  *
  * datetime.c
- *	  implements DATE and TIME data types specified in SQL-92 standard
+ *	  Support functions for date/time types.
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994-5, Regents of the University of California
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.41 2000/01/26 05:57:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.42 2000/02/16 18:17:02 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <limits.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/types.h>
+#include <errno.h>
 
 #include "postgres.h"
 #ifdef HAVE_FLOAT_H
 #include <float.h>
 #endif
-#include "miscadmin.h"
-#include "utils/builtins.h"
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifndef USE_POSIX_TIME
+#include <sys/timeb.h>
+#endif
 
-static int	date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn);
+#include "miscadmin.h"
+#include "utils/datetime.h"
+
+
+#define USE_DATE_CACHE 1
+#define ROUND_ALL 0
+
+int			day_tab[2][13] = {
+	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
+{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
+
+
+char	   *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+
+char	   *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
+"Thursday", "Friday", "Saturday", NULL};
+
+
+#if 0
+
+
+static void GetEpochTime(struct tm * tm);
+
+
+#endif
+
+
+#define UTIME_MINYEAR (1901)
+#define UTIME_MINMONTH (12)
+#define UTIME_MINDAY (14)
+#define UTIME_MAXYEAR (2038)
+#define UTIME_MAXMONTH (01)
+#define UTIME_MAXDAY (18)
+
+#define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
+ || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
+  || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
+ && ((y < UTIME_MAXYEAR) \
+ || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
+  || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
 
 
 /*****************************************************************************
- *	 Date ADT
+ *	 PRIVATE ROUTINES														 *
  *****************************************************************************/
 
+/* definitions for squeezing values into "value" */
+#define ABS_SIGNBIT		(char) 0200
+#define VALMASK			(char) 0177
+#define NEG(n)			((n)|ABS_SIGNBIT)
+#define SIGNEDCHAR(c)	((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
+#define FROMVAL(tp)		(-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
+#define TOVAL(tp, v)	((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
 
-/* date_in()
- * Given date text string, convert to internal date format.
+/*
+ * to keep this table reasonably small, we divide the lexval for TZ and DTZ
+ * entries by 10 and truncate the text field at MAXTOKLEN characters.
+ * the text field is not guaranteed to be NULL-terminated.
  */
-DateADT
-date_in(char *str)
-{
-	DateADT		date;
-	double		fsec;
-	struct tm	tt,
-			   *tm = &tt;
-	int			tzp;
-	int			dtype;
-	int			nf;
-	char	   *field[MAXDATEFIELDS];
-	int			ftype[MAXDATEFIELDS];
-	char		lowstr[MAXDATELEN + 1];
+static datetkn datetktbl[] = {
+/*		text			token	lexval */
+	{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
+	{"acsst", DTZ, 63},			/* Cent. Australia */
+	{"acst", TZ, 57},			/* Cent. Australia */
+	{DA_D, ADBC, AD},			/* "ad" for years >= 0 */
+	{"abstime", IGNORE, 0},		/* "abstime" for pre-v6.1 "Invalid
+								 * Abstime" */
+	{"adt", DTZ, NEG(18)},		/* Atlantic Daylight Time */
+	{"aesst", DTZ, 66},			/* E. Australia */
+	{"aest", TZ, 60},			/* Australia Eastern Std Time */
+	{"ahst", TZ, NEG(60)},		/* Alaska-Hawaii Std Time */
+	{"allballs", RESERV, DTK_ZULU},		/* 00:00:00 */
+	{"am", AMPM, AM},
+	{"apr", MONTH, 4},
+	{"april", MONTH, 4},
+	{"ast", TZ, NEG(24)},		/* Atlantic Std Time (Canada) */
+	{"at", IGNORE, 0},			/* "at" (throwaway) */
+	{"aug", MONTH, 8},
+	{"august", MONTH, 8},
+	{"awsst", DTZ, 54},			/* W. Australia */
+	{"awst", TZ, 48},			/* W. Australia */
+	{DB_C, ADBC, BC},			/* "bc" for years < 0 */
+	{"bst", TZ, 6},				/* British Summer Time */
+	{"bt", TZ, 18},				/* Baghdad Time */
+	{"cadt", DTZ, 63},			/* Central Australian DST */
+	{"cast", TZ, 57},			/* Central Australian ST */
+	{"cat", TZ, NEG(60)},		/* Central Alaska Time */
+	{"cct", TZ, 48},			/* China Coast */
+	{"cdt", DTZ, NEG(30)},		/* Central Daylight Time */
+	{"cet", TZ, 6},				/* Central European Time */
+	{"cetdst", DTZ, 12},		/* Central European Dayl.Time */
+#if USE_AUSTRALIAN_RULES
+	{"cst", TZ, 63},			/* Australia Eastern Std Time */
+#else
+	{"cst", TZ, NEG(36)},		/* Central Standard Time */
+#endif
+	{DCURRENT, RESERV, DTK_CURRENT},	/* "current" is always now */
+	{"dec", MONTH, 12},
+	{"december", MONTH, 12},
+	{"dnt", TZ, 6},				/* Dansk Normal Tid */
+	{"dow", RESERV, DTK_DOW},	/* day of week */
+	{"doy", RESERV, DTK_DOY},	/* day of year */
+	{"dst", DTZMOD, 6},
+	{"east", TZ, 60},			/* East Australian Std Time */
+	{"edt", DTZ, NEG(24)},		/* Eastern Daylight Time */
+	{"eet", TZ, 12},			/* East. Europe, USSR Zone 1 */
+	{"eetdst", DTZ, 18},		/* Eastern Europe */
+	{EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
+#if USE_AUSTRALIAN_RULES
+	{"est", TZ, 60},			/* Australia Eastern Std Time */
+#else
+	{"est", TZ, NEG(30)},		/* Eastern Standard Time */
+#endif
+	{"feb", MONTH, 2},
+	{"february", MONTH, 2},
+	{"fri", DOW, 5},
+	{"friday", DOW, 5},
+	{"fst", TZ, 6},				/* French Summer Time */
+	{"fwt", DTZ, 12},			/* French Winter Time  */
+	{"gmt", TZ, 0},				/* Greenwish Mean Time */
+	{"gst", TZ, 60},			/* Guam Std Time, USSR Zone 9 */
+	{"hdt", DTZ, NEG(54)},		/* Hawaii/Alaska */
+	{"hmt", DTZ, 18},			/* Hellas ? ? */
+	{"hst", TZ, NEG(60)},		/* Hawaii Std Time */
+	{"idle", TZ, 72},			/* Intl. Date Line, East */
+	{"idlw", TZ, NEG(72)},		/* Intl. Date Line, West */
+	{LATE, RESERV, DTK_LATE},	/* "infinity" reserved for "late time" */
+	{INVALID, RESERV, DTK_INVALID},
+	/* "invalid" reserved for invalid time */
+	{"ist", TZ, 12},			/* Israel */
+	{"it", TZ, 21},				/* Iran Time */
+	{"jan", MONTH, 1},
+	{"january", MONTH, 1},
+	{"jst", TZ, 54},			/* Japan Std Time,USSR Zone 8 */
+	{"jt", TZ, 45},				/* Java Time */
+	{"jul", MONTH, 7},
+	{"july", MONTH, 7},
+	{"jun", MONTH, 6},
+	{"june", MONTH, 6},
+	{"kst", TZ, 54},			/* Korea Standard Time */
+	{"ligt", TZ, 60},			/* From Melbourne, Australia */
+	{"mar", MONTH, 3},
+	{"march", MONTH, 3},
+	{"may", MONTH, 5},
+	{"mdt", DTZ, NEG(36)},		/* Mountain Daylight Time */
+	{"mest", DTZ, 12},			/* Middle Europe Summer Time */
+	{"met", TZ, 6},				/* Middle Europe Time */
+	{"metdst", DTZ, 12},		/* Middle Europe Daylight Time */
+	{"mewt", TZ, 6},			/* Middle Europe Winter Time */
+	{"mez", TZ, 6},				/* Middle Europe Zone */
+	{"mon", DOW, 1},
+	{"monday", DOW, 1},
+	{"mst", TZ, NEG(42)},		/* Mountain Standard Time */
+	{"mt", TZ, 51},				/* Moluccas Time */
+	{"ndt", DTZ, NEG(15)},		/* Nfld. Daylight Time */
+	{"nft", TZ, NEG(21)},		/* Newfoundland Standard Time */
+	{"nor", TZ, 6},				/* Norway Standard Time */
+	{"nov", MONTH, 11},
+	{"november", MONTH, 11},
+	{NOW, RESERV, DTK_NOW},		/* current transaction time */
+	{"nst", TZ, NEG(21)},		/* Nfld. Standard Time */
+	{"nt", TZ, NEG(66)},		/* Nome Time */
+	{"nzdt", DTZ, 78},			/* New Zealand Daylight Time */
+	{"nzst", TZ, 72},			/* New Zealand Standard Time */
+	{"nzt", TZ, 72},			/* New Zealand Time */
+	{"oct", MONTH, 10},
+	{"october", MONTH, 10},
+	{"on", IGNORE, 0},			/* "on" (throwaway) */
+	{"pdt", DTZ, NEG(42)},		/* Pacific Daylight Time */
+	{"pm", AMPM, PM},
+	{"pst", TZ, NEG(48)},		/* Pacific Standard Time */
+	{"sadt", DTZ, 63},			/* S. Australian Dayl. Time */
+	{"sast", TZ, 57},			/* South Australian Std Time */
+	{"sat", DOW, 6},
+	{"saturday", DOW, 6},
+	{"sep", MONTH, 9},
+	{"sept", MONTH, 9},
+	{"september", MONTH, 9},
+	{"set", TZ, NEG(6)},		/* Seychelles Time ?? */
+	{"sst", DTZ, 12},			/* Swedish Summer Time */
+	{"sun", DOW, 0},
+	{"sunday", DOW, 0},
+	{"swt", TZ, 6},				/* Swedish Winter Time	*/
+	{"thu", DOW, 4},
+	{"thur", DOW, 4},
+	{"thurs", DOW, 4},
+	{"thursday", DOW, 4},
+	{TODAY, RESERV, DTK_TODAY}, /* midnight */
+	{TOMORROW, RESERV, DTK_TOMORROW},	/* tomorrow midnight */
+	{"tue", DOW, 2},
+	{"tues", DOW, 2},
+	{"tuesday", DOW, 2},
+	{"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
+										 * time */
+	{"ut", TZ, 0},
+	{"utc", TZ, 0},
+	{"wadt", DTZ, 48},			/* West Australian DST */
+	{"wast", TZ, 42},			/* West Australian Std Time */
+	{"wat", TZ, NEG(6)},		/* West Africa Time */
+	{"wdt", DTZ, 54},			/* West Australian DST */
+	{"wed", DOW, 3},
+	{"wednesday", DOW, 3},
+	{"weds", DOW, 3},
+	{"wet", TZ, 0},				/* Western Europe */
+	{"wetdst", DTZ, 6},			/* Western Europe */
+	{"wst", TZ, 48},			/* West Australian Std Time */
+	{"ydt", DTZ, NEG(48)},		/* Yukon Daylight Time */
+	{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
+	{"yst", TZ, NEG(54)},		/* Yukon Standard Time */
+	{"zp4", TZ, NEG(24)},		/* GMT +4  hours. */
+	{"zp5", TZ, NEG(30)},		/* GMT +5  hours. */
+	{"zp6", TZ, NEG(36)},		/* GMT +6  hours. */
+	{"z", RESERV, DTK_ZULU},	/* 00:00:00 */
+	{ZULU, RESERV, DTK_ZULU},	/* 00:00:00 */
+};
 
-	if (!PointerIsValid(str))
-		elog(ERROR, "Bad (null) date external representation");
+static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
 
-	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
-	 || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
-		elog(ERROR, "Bad date external representation '%s'", str);
+static datetkn deltatktbl[] = {
+/*		text			token	lexval */
+	{"@", IGNORE, 0},			/* postgres relative time prefix */
+	{DAGO, AGO, 0},				/* "ago" indicates negative time offset */
+	{"c", UNITS, DTK_CENTURY},	/* "century" relative time units */
+	{"cent", UNITS, DTK_CENTURY},		/* "century" relative time units */
+	{"centuries", UNITS, DTK_CENTURY},	/* "centuries" relative time units */
+	{DCENTURY, UNITS, DTK_CENTURY},		/* "century" relative time units */
+	{"d", UNITS, DTK_DAY},		/* "day" relative time units */
+	{DDAY, UNITS, DTK_DAY},		/* "day" relative time units */
+	{"days", UNITS, DTK_DAY},	/* "days" relative time units */
+	{"dec", UNITS, DTK_DECADE}, /* "decade" relative time units */
+	{"decs", UNITS, DTK_DECADE},/* "decades" relative time units */
+	{DDECADE, UNITS, DTK_DECADE},		/* "decade" relative time units */
+	{"decades", UNITS, DTK_DECADE},		/* "decades" relative time units */
+	{"h", UNITS, DTK_HOUR},		/* "hour" relative time units */
+	{DHOUR, UNITS, DTK_HOUR},	/* "hour" relative time units */
+	{"hours", UNITS, DTK_HOUR}, /* "hours" relative time units */
+	{"hr", UNITS, DTK_HOUR},	/* "hour" relative time units */
+	{"hrs", UNITS, DTK_HOUR},	/* "hours" relative time units */
+	{INVALID, RESERV, DTK_INVALID},		/* "invalid" reserved for invalid
+										 * time */
+	{"m", UNITS, DTK_MINUTE},	/* "minute" relative time units */
+	{"microsecon", UNITS, DTK_MICROSEC},		/* "microsecond" relative
+												 * time units */
+	{"mil", UNITS, DTK_MILLENIUM},		/* "millenium" relative time units */
+	{"mils", UNITS, DTK_MILLENIUM},		/* "millenia" relative time units */
+	{"millenia", UNITS, DTK_MILLENIUM}, /* "millenia" relative time units */
+	{DMILLENIUM, UNITS, DTK_MILLENIUM}, /* "millenium" relative time units */
+	{"millisecon", UNITS, DTK_MILLISEC},		/* relative time units */
+	{"min", UNITS, DTK_MINUTE}, /* "minute" relative time units */
+	{"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */
+	{"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */
+	{DMINUTE, UNITS, DTK_MINUTE},		/* "minute" relative time units */
+	{"minutes", UNITS, DTK_MINUTE},		/* "minutes" relative time units */
+	{"mon", UNITS, DTK_MONTH},	/* "months" relative time units */
+	{"mons", UNITS, DTK_MONTH}, /* "months" relative time units */
+	{DMONTH, UNITS, DTK_MONTH}, /* "month" relative time units */
+	{"months", UNITS, DTK_MONTH},
+	{"ms", UNITS, DTK_MILLISEC},
+	{"msec", UNITS, DTK_MILLISEC},
+	{DMILLISEC, UNITS, DTK_MILLISEC},
+	{"mseconds", UNITS, DTK_MILLISEC},
+	{"msecs", UNITS, DTK_MILLISEC},
+	{"qtr", UNITS, DTK_QUARTER},/* "quarter" relative time */
+	{DQUARTER, UNITS, DTK_QUARTER},		/* "quarter" relative time */
+	{"reltime", IGNORE, 0},		/* for pre-v6.1 "Undefined Reltime" */
+	{"s", UNITS, DTK_SECOND},
+	{"sec", UNITS, DTK_SECOND},
+	{DSECOND, UNITS, DTK_SECOND},
+	{"seconds", UNITS, DTK_SECOND},
+	{"secs", UNITS, DTK_SECOND},
+	{DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
+	{"tz", UNITS, DTK_TZ},		/* "timezone" time offset */
+	{"tz_hour", UNITS, DTK_TZ_HOUR},	/* timezone hour units */
+	{"tz_minute", UNITS, DTK_TZ_MINUTE},		/* timezone minutes units */
+	{"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
+	{"us", UNITS, DTK_MICROSEC},/* "microsecond" relative time units */
+	{"usec", UNITS, DTK_MICROSEC},		/* "microsecond" relative time
+										 * units */
+	{DMICROSEC, UNITS, DTK_MICROSEC},	/* "microsecond" relative time
+										 * units */
+	{"useconds", UNITS, DTK_MICROSEC},	/* "microseconds" relative time
+										 * units */
+	{"usecs", UNITS, DTK_MICROSEC},		/* "microseconds" relative time
+										 * units */
+	{"w", UNITS, DTK_WEEK},		/* "week" relative time units */
+	{DWEEK, UNITS, DTK_WEEK},	/* "week" relative time units */
+	{"weeks", UNITS, DTK_WEEK}, /* "weeks" relative time units */
+	{"y", UNITS, DTK_YEAR},		/* "year" relative time units */
+	{DYEAR, UNITS, DTK_YEAR},	/* "year" relative time units */
+	{"years", UNITS, DTK_YEAR}, /* "years" relative time units */
+	{"yr", UNITS, DTK_YEAR},	/* "year" relative time units */
+	{"yrs", UNITS, DTK_YEAR},	/* "years" relative time units */
+};
 
-	switch (dtype)
-	{
-		case DTK_DATE:
-			break;
+static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
 
-		case DTK_CURRENT:
-			GetCurrentTime(tm);
-			break;
+#if USE_DATE_CACHE
+datetkn    *datecache[MAXDATEFIELDS] = {NULL};
 
-		case DTK_EPOCH:
-			tm->tm_year = 1970;
-			tm->tm_mon = 1;
-			tm->tm_mday = 1;
-			break;
+datetkn    *deltacache[MAXDATEFIELDS] = {NULL};
 
-		default:
-			elog(ERROR, "Unrecognized date external representation '%s'", str);
-	}
+#endif
 
-	date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
 
-	return date;
-}	/* date_in() */
-
-/* date_out()
- * Given internal format date, convert to text string.
+/*
+ * Calendar time to Julian date conversions.
+ * Julian date is commonly used in astronomical applications,
+ *	since it is numerically accurate and computationally simple.
+ * The algorithms here will accurately convert between Julian day
+ *	and calendar date for all non-negative Julian days
+ *	(i.e. from Nov 23, -4713 on).
+ *
+ * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
+ *	University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
+ *
+ * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
+ *	now at Aerospace Corp. (hi, Henry!)
+ *
+ * These routines will be used by other date/time packages - tgl 97/02/25
  */
-char *
-date_out(DateADT date)
-{
-	char	   *result;
-	struct tm	tt,
-			   *tm = &tt;
-	char		buf[MAXDATELEN + 1];
-
-	j2date((date + date2j(2000, 1, 1)),
-		   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
-
-	EncodeDateOnly(tm, DateStyle, buf);
-
-	result = palloc(strlen(buf) + 1);
-
-	strcpy(result, buf);
-
-	return result;
-}	/* date_out() */
-
-bool
-date_eq(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 == dateVal2;
-}
-
-bool
-date_ne(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 != dateVal2;
-}
-
-bool
-date_lt(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 < dateVal2;
-}	/* date_lt() */
-
-bool
-date_le(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 <= dateVal2;
-}	/* date_le() */
-
-bool
-date_gt(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 > dateVal2;
-}	/* date_gt() */
-
-bool
-date_ge(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 >= dateVal2;
-}	/* date_ge() */
 
 int
-date_cmp(DateADT dateVal1, DateADT dateVal2)
+date2j(int y, int m, int d)
 {
-	if (dateVal1 < dateVal2)
-		return -1;
-	else if (dateVal1 > dateVal2)
-		return 1;
-	return 0;
-}	/* date_cmp() */
+	int			m12 = (m - 14) / 12;
 
-DateADT
-date_larger(DateADT dateVal1, DateADT dateVal2)
+	return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
+			- (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
+}	/* date2j() */
+
+void
+j2date(int jd, int *year, int *month, int *day)
 {
-	return date_gt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
-}	/* date_larger() */
+	int			j,
+				y,
+				m,
+				d;
 
-DateADT
-date_smaller(DateADT dateVal1, DateADT dateVal2)
+	int			i,
+				l,
+				n;
+
+	l = jd + 68569;
+	n = (4 * l) / 146097;
+	l -= (146097 * n + 3) / 4;
+	i = (4000 * (l + 1)) / 1461001;
+	l += 31 - (1461 * i) / 4;
+	j = (80 * l) / 2447;
+	d = l - (2447 * j) / 80;
+	l = j / 11;
+	m = (j + 2) - (12 * l);
+	y = 100 * (n - 49) + i + l;
+
+	*year = y;
+	*month = m;
+	*day = d;
+	return;
+}	/* j2date() */
+
+int
+j2day(int date)
 {
-	return date_lt(dateVal1, dateVal2) ? dateVal1 : dateVal2;
-}	/* date_smaller() */
+	int			day;
 
-/* Compute difference between two dates in days.
+	day = (date + 1) % 7;
+
+	return day;
+}	/* j2day() */
+
+
+#if 0
+
+
+static double
+time2t(const int hour, const int min, const double sec)
+{
+	return (((hour * 60) + min) * 60) + sec;
+}	/* time2t() */
+
+static void
+dt2time(Timestamp jd, int *hour, int *min, double *sec)
+{
+	double		time;
+
+	time = jd;
+
+	*hour = (time / 3600);
+	time -= ((*hour) * 3600);
+	*min = (time / 60);
+	time -= ((*min) * 60);
+	*sec = JROUND(time);
+
+	return;
+}	/* dt2time() */
+
+
+#endif
+
+
+/*
+ * parse and convert date in timestr (the normal interface)
+ *
+ * Returns the number of seconds since epoch (J2000)
  */
-int4
-date_mi(DateADT dateVal1, DateADT dateVal2)
-{
-	return dateVal1 - dateVal2;
-}	/* date_mi() */
 
-/* Add a number of days to a date, giving a new date.
- * Must handle both positive and negative numbers of days.
+/* ParseDateTime()
+ * Break string into tokens based on a date/time context.
  */
-DateADT
-date_pli(DateADT dateVal, int4 days)
+int
+ParseDateTime(char *timestr, char *lowstr,
+			  char **field, int *ftype, int maxfields, int *numfields)
 {
-	return dateVal + days;
-}	/* date_pli() */
+	int			nf = 0;
+	char	   *cp = timestr;
+	char	   *lp = lowstr;
 
-/* Subtract a number of days from a date, giving a new date.
- */
-DateADT
-date_mii(DateADT dateVal, int4 days)
-{
-	return date_pli(dateVal, -days);
-}	/* date_mii() */
-
-
-/* date_datetime()
- * Convert date to datetime data type.
- */
-DateTime   *
-date_datetime(DateADT dateVal)
-{
-	DateTime   *result;
-	struct tm	tt,
-			   *tm = &tt;
-	int			tz;
-	double		fsec = 0;
-	char	   *tzn;
-
-	result = palloc(sizeof(DateTime));
-
-	if (date2tm(dateVal, &tz, tm, &fsec, &tzn) != 0)
-		elog(ERROR, "Unable to convert date to datetime");
-
-	if (tm2datetime(tm, fsec, &tz, result) != 0)
-		elog(ERROR, "Datetime out of range");
-
-	return result;
-}	/* date_datetime() */
-
-
-/* datetime_date()
- * Convert datetime to date data type.
- */
-DateADT
-datetime_date(DateTime *datetime)
-{
-	DateADT		result;
-	struct tm	tt,
-			   *tm = &tt;
-	int			tz;
-	double		fsec;
-	char	   *tzn;
-
-	if (!PointerIsValid(datetime))
-		elog(ERROR, "Unable to convert null datetime to date");
-
-	if (DATETIME_NOT_FINITE(*datetime))
-		elog(ERROR, "Unable to convert datetime to date");
-
-	if (DATETIME_IS_EPOCH(*datetime))
+	/* outer loop through fields */
+	while (*cp != '\0')
 	{
-		datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL);
+		field[nf] = lp;
 
-	}
-	else if (DATETIME_IS_CURRENT(*datetime))
-	{
-		datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn);
+		/* leading digit? then date or time */
+		if (isdigit(*cp) || (*cp == '.'))
+		{
+			*lp++ = *cp++;
+			while (isdigit(*cp))
+				*lp++ = *cp++;
+			/* time field? */
+			if (*cp == ':')
+			{
+				ftype[nf] = DTK_TIME;
+				while (isdigit(*cp) || (*cp == ':') || (*cp == '.'))
+					*lp++ = *cp++;
 
-	}
-	else
-	{
-		if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0)
-			elog(ERROR, "Unable to convert datetime to date");
-	}
+			}
+			/* date field? allow embedded text month */
+			else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
+			{
+				ftype[nf] = DTK_DATE;
+				while (isalnum(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
+					*lp++ = tolower(*cp++);
 
-	result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
-
-	return result;
-}	/* datetime_date() */
-
-
-/* abstime_date()
- * Convert abstime to date data type.
- */
-DateADT
-abstime_date(AbsoluteTime abstime)
-{
-	DateADT		result;
-	struct tm	tt,
-			   *tm = &tt;
-	int			tz;
-
-	switch (abstime)
-	{
-		case INVALID_ABSTIME:
-		case NOSTART_ABSTIME:
-		case NOEND_ABSTIME:
-			elog(ERROR, "Unable to convert reserved abstime value to date");
+			}
 
 			/*
-			 * pretend to drop through to make compiler think that result
-			 * will be set
+			 * otherwise, number only and will determine year, month, or
+			 * day later
 			 */
+			else
+				ftype[nf] = DTK_NUMBER;
 
-		case EPOCH_ABSTIME:
-			result = date2j(1970, 1, 1) - date2j(2000, 1, 1);
-			break;
+		}
 
-		case CURRENT_ABSTIME:
-			GetCurrentTime(tm);
-			result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
-			break;
+		/*
+		 * text? then date string, month, day of week, special, or
+		 * timezone
+		 */
+		else if (isalpha(*cp))
+		{
+			ftype[nf] = DTK_STRING;
+			*lp++ = tolower(*cp++);
+			while (isalpha(*cp))
+				*lp++ = tolower(*cp++);
 
-		default:
-			abstime2tm(abstime, &tz, tm, NULL);
-			result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
-			break;
+			/* full date string with leading text month? */
+			if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
+			{
+				/*
+				 * special case of Posix timezone "GMT-0800"
+				 * Note that other sign (e.g. "GMT+0800"
+				 * is recognized as two separate fields and handled later.
+				 * XXX There is no room for a delimiter between
+				 * the "GMT" and the "-0800", so we are going to just swallow the "GMT".
+				 * But this leads to other troubles with the definition of signs,
+				 * so we have to flip
+				 * - thomas 2000-02-06
+				 */
+				if ((*cp == '-') && isdigit(*(cp+1))
+					&& (strncmp(field[nf], "gmt", 3) == 0))
+				{
+					*cp = '+';
+					continue;
+				}
+
+				ftype[nf] = DTK_DATE;
+				while (isdigit(*cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
+					*lp++ = tolower(*cp++);
+			}
+
+			/* skip leading spaces */
+		}
+		else if (isspace(*cp))
+		{
+			cp++;
+			continue;
+
+			/* sign? then special or numeric timezone */
+		}
+		else if ((*cp == '+') || (*cp == '-'))
+		{
+			*lp++ = *cp++;
+			/* soak up leading whitespace */
+			while (isspace(*cp))
+				cp++;
+			/* numeric timezone? */
+			if (isdigit(*cp))
+			{
+				ftype[nf] = DTK_TZ;
+				*lp++ = *cp++;
+				while (isdigit(*cp) || (*cp == ':'))
+					*lp++ = *cp++;
+
+				/* special? */
+			}
+			else if (isalpha(*cp))
+			{
+				ftype[nf] = DTK_SPECIAL;
+				*lp++ = tolower(*cp++);
+				while (isalpha(*cp))
+					*lp++ = tolower(*cp++);
+
+				/* otherwise something wrong... */
+			}
+			else
+				return -1;
+
+			/* ignore punctuation but use as delimiter */
+		}
+		else if (ispunct(*cp))
+		{
+			cp++;
+			continue;
+
+		}
+		else
+			return -1;
+
+		/* force in a delimiter */
+		*lp++ = '\0';
+		nf++;
+		if (nf > MAXDATEFIELDS)
+			return -1;
 	}
 
-	return result;
-}	/* abstime_date() */
+	*numfields = nf;
+
+	return 0;
+}	/* ParseDateTime() */
 
 
-/* date2tm()
- * Convert date to time structure.
- * Note that date is an implicit local time, but the system calls assume
- *	that everything is GMT. So, convert to GMT, rotate to local time,
- *	and then convert again to try to get the time zones correct.
+/* DecodeDateTime()
+ * Interpret previously parsed fields for general date and time.
+ * Return 0 if full date, 1 if only time, and -1 if problems.
+ *		External format(s):
+ *				"<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
+ *				"Fri Feb-7-1997 15:23:27"
+ *				"Feb-7-1997 15:23:27"
+ *				"2-7-1997 15:23:27"
+ *				"1997-2-7 15:23:27"
+ *				"1997.038 15:23:27"		(day of year 1-366)
+ *		Also supports input in compact time:
+ *				"970207 152327"
+ *				"97038 152327"
+ *
+ * Use the system-provided functions to get the current time zone
+ *	if not specified in the input string.
+ * If the date is outside the time_t system-supported time range,
+ *	then assume GMT time zone. - tgl 97/05/27
  */
-static int
-date2tm(DateADT dateVal, int *tzp, struct tm * tm, double *fsec, char **tzn)
+int
+DecodeDateTime(char **field, int *ftype, int nf,
+			   int *dtype, struct tm * tm, double *fsec, int *tzp)
 {
-	struct tm  *tx;
-	time_t		utime;
+	int			fmask = 0,
+				tmask,
+				type;
+	int			i;
+	int			flen,
+				val;
+	int			mer = HR24;
+	int			haveTextMonth = FALSE;
+	int			is2digits = FALSE;
+	int			bc = FALSE;
 
-	*fsec = 0;
-
-	j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
+	*dtype = DTK_DATE;
 	tm->tm_hour = 0;
 	tm->tm_min = 0;
 	tm->tm_sec = 0;
-	tm->tm_isdst = -1;
+	*fsec = 0;
+	tm->tm_isdst = -1;			/* don't know daylight savings time status
+								 * apriori */
+	if (tzp != NULL)
+		*tzp = 0;
 
-	if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+	for (i = 0; i < nf; i++)
 	{
+		switch (ftype[i])
+		{
+			case DTK_DATE:
+				if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
+					return -1;
+				break;
 
-		/* convert to system time */
-		utime = ((dateVal + (date2j(2000, 1, 1) - date2j(1970, 1, 1))) * 86400);
-		/* rotate to noon to get the right day in time zone */
-		utime += (12 * 60 * 60);
+			case DTK_TIME:
+				if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
+					return -1;
 
+				/*
+				 * check upper limit on hours; other limits checked in
+				 * DecodeTime()
+				 */
+				if (tm->tm_hour > 23)
+					return -1;
+				break;
+
+			case DTK_TZ:
+				if (tzp == NULL)
+					return -1;
+				if (DecodeTimezone(field[i], tzp) != 0)
+					return -1;
+				tmask = DTK_M(TZ);
+				break;
+
+			case DTK_NUMBER:
+				flen = strlen(field[i]);
+
+				/*
+				 * long numeric string and either no date or no time read
+				 * yet? then interpret as a concatenated date or time...
+				 */
+				if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M)))
+				{
+					if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
+						return -1;
+
+				}
+				/* otherwise it is a single date/time field... */
+				else
+				{
+					if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
+						return -1;
+				}
+				break;
+
+			case DTK_STRING:
+			case DTK_SPECIAL:
+				type = DecodeSpecial(i, field[i], &val);
+				if (type == IGNORE)
+					continue;
+
+				tmask = DTK_M(type);
+				switch (type)
+				{
+					case RESERV:
+						switch (val)
+						{
+							case DTK_NOW:
+								tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
+								*dtype = DTK_DATE;
+								GetCurrentTime(tm);
+								if (tzp != NULL)
+									*tzp = CTimeZone;
+								break;
+
+							case DTK_YESTERDAY:
+								tmask = DTK_DATE_M;
+								*dtype = DTK_DATE;
+								GetCurrentTime(tm);
+								j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
+								&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+								tm->tm_hour = 0;
+								tm->tm_min = 0;
+								tm->tm_sec = 0;
+								break;
+
+							case DTK_TODAY:
+								tmask = DTK_DATE_M;
+								*dtype = DTK_DATE;
+								GetCurrentTime(tm);
+								tm->tm_hour = 0;
+								tm->tm_min = 0;
+								tm->tm_sec = 0;
+								break;
+
+							case DTK_TOMORROW:
+								tmask = DTK_DATE_M;
+								*dtype = DTK_DATE;
+								GetCurrentTime(tm);
+								j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
+								&tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+								tm->tm_hour = 0;
+								tm->tm_min = 0;
+								tm->tm_sec = 0;
+								break;
+
+							case DTK_ZULU:
+								tmask = (DTK_TIME_M | DTK_M(TZ));
+								*dtype = DTK_DATE;
+								tm->tm_hour = 0;
+								tm->tm_min = 0;
+								tm->tm_sec = 0;
+								if (tzp != NULL)
+									*tzp = 0;
+								break;
+
+							default:
+								*dtype = val;
+						}
+
+						break;
+
+					case MONTH:
+						/*
+						 * already have a (numeric) month? then see if we
+						 * can substitute...
+						 */
+						if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
+							&& (!(fmask & DTK_M(DAY)))
+							&& ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
+						{
+							tm->tm_mday = tm->tm_mon;
+							tmask = DTK_M(DAY);
+						}
+						haveTextMonth = TRUE;
+						tm->tm_mon = val;
+						break;
+
+					case DTZMOD:
+
+						/*
+						 * daylight savings time modifier (solves "MET
+						 * DST" syntax)
+						 */
+						tmask |= DTK_M(DTZ);
+						tm->tm_isdst = 1;
+						if (tzp == NULL)
+							return -1;
+						*tzp += val * 60;
+						break;
+
+					case DTZ:
+
+						/*
+						 * set mask for TZ here _or_ check for DTZ later
+						 * when getting default timezone
+						 */
+						tmask |= DTK_M(TZ);
+						tm->tm_isdst = 1;
+						if (tzp == NULL)
+							return -1;
+						*tzp = val * 60;
+						break;
+
+					case TZ:
+						tm->tm_isdst = 0;
+						if (tzp == NULL)
+							return -1;
+						*tzp = val * 60;
+
+						/* Swallow an immediately succeeding timezone if this is GMT
+						 * This handles the odd case in FreeBSD of "GMT+0800"
+						 * but note that we need to flip the sign on this too.
+						 * Claims to be some sort of POSIX standard format :(
+						 * - thomas 2000-01-20
+						 */
+						if ((i < (nf-1)) && (ftype[i+1] == DTK_TZ)
+							&& (strcmp(field[i], "gmt") == 0))
+						{
+							i++;
+							if (DecodeTimezone(field[i], tzp) != 0)
+								return -1;
+
+							/* flip the sign per POSIX standard */
+							*tzp = -(*tzp);
+						}
+
+
+						break;
+
+					case IGNORE:
+						break;
+
+					case AMPM:
+						mer = val;
+						break;
+
+					case ADBC:
+						bc = (val == BC);
+						break;
+
+					case DOW:
+						tm->tm_wday = val;
+						break;
+
+					default:
+						return -1;
+				}
+				break;
+
+			default:
+				return -1;
+		}
+
+		if (tmask & fmask)
+			return -1;
+		fmask |= tmask;
+	}
+
+	/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
+	if (bc)
+	{
+		if (tm->tm_year > 0)
+			tm->tm_year = -(tm->tm_year - 1);
+		else
+			elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year);
+	}
+	else if (is2digits)
+	{
+		if (tm->tm_year < 70)
+			tm->tm_year += 2000;
+		else if (tm->tm_year < 100)
+			tm->tm_year += 1900;
+	}
+
+	if ((mer != HR24) && (tm->tm_hour > 12))
+		return -1;
+	if ((mer == AM) && (tm->tm_hour == 12))
+		tm->tm_hour = 0;
+	else if ((mer == PM) && (tm->tm_hour != 12))
+		tm->tm_hour += 12;
+
+	/* do additional checking for full date specs... */
+	if (*dtype == DTK_DATE)
+	{
+		if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+			return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
+
+		/*
+		 * check for valid day of month, now that we know for sure the
+		 * month and year...
+		 */
+		if ((tm->tm_mday < 1)
+		 || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
+			return -1;
+
+		/* timezone not specified? then find local timezone if possible */
+		if (((fmask & DTK_DATE_M) == DTK_DATE_M)
+			&& (tzp != NULL) && (!(fmask & DTK_M(TZ))))
+		{
+
+			/*
+			 * daylight savings time modifier but no standard timezone?
+			 * then error
+			 */
+			if (fmask & DTK_M(DTZMOD))
+				return -1;
+
+			if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+			{
 #ifdef USE_POSIX_TIME
-		tx = localtime(&utime);
-
-		tm->tm_year = tx->tm_year + 1900;
-		tm->tm_mon = tx->tm_mon + 1;
-		tm->tm_mday = tx->tm_mday;
-		tm->tm_isdst = tx->tm_isdst;
+				tm->tm_year -= 1900;
+				tm->tm_mon -= 1;
+				tm->tm_isdst = -1;
+				mktime(tm);
+				tm->tm_year += 1900;
+				tm->tm_mon += 1;
 
 #if defined(HAVE_TM_ZONE)
-		tm->tm_gmtoff = tx->tm_gmtoff;
-		tm->tm_zone = tx->tm_zone;
-
-		/* tm_gmtoff is Sun/DEC-ism */
-		*tzp = -(tm->tm_gmtoff);
-		if (tzn != NULL)
-			*tzn = (char *) tm->tm_zone;
+				*tzp = -(tm->tm_gmtoff);		/* tm_gmtoff is
+												 * Sun/DEC-ism */
 #elif defined(HAVE_INT_TIMEZONE)
 #ifdef __CYGWIN__
-		*tzp = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
+				*tzp = ((tm->tm_isdst > 0) ? (_timezone - 3600) : _timezone);
 #else
-		*tzp = (tm->tm_isdst ? (timezone - 3600) : timezone);
+				*tzp = ((tm->tm_isdst > 0) ? (timezone - 3600) : timezone);
 #endif
-		if (tzn != NULL)
-			*tzn = tzname[(tm->tm_isdst > 0)];
 #else
 #error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
-#endif
-#else							/* !USE_POSIX_TIME */
-		*tzp = CTimeZone;		/* V7 conventions; don't know timezone? */
-		if (tzn != NULL)
-			*tzn = CTZName;
 #endif
 
-		/* otherwise, outside of timezone range so convert to GMT... */
-	}
-	else
-	{
-		*tzp = 0;
-		tm->tm_isdst = 0;
-		if (tzn != NULL)
-			*tzn = NULL;
+#else							/* !USE_POSIX_TIME */
+				*tzp = CTimeZone;
+#endif
+			}
+			else
+			{
+				tm->tm_isdst = 0;
+				*tzp = 0;
+			}
+		}
 	}
 
 	return 0;
-}	/* date2tm() */
+}	/* DecodeDateTime() */
 
 
-/*****************************************************************************
- *	 Time ADT
- *****************************************************************************/
-
-
-TimeADT    *
-time_in(char *str)
+/* DecodeTimeOnly()
+ * Interpret parsed string as time fields only.
+ */
+int
+DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
 {
-	TimeADT    *time;
+	int			fmask,
+				tmask,
+				type;
+	int			i;
+	int			flen,
+				val;
+	int			is2digits = FALSE;
+	int			mer = HR24;
 
+	*dtype = DTK_TIME;
+	tm->tm_hour = 0;
+	tm->tm_min = 0;
+	tm->tm_sec = 0;
+	tm->tm_isdst = -1;			/* don't know daylight savings time status
+								 * apriori */
+	*fsec = 0;
+
+	fmask = DTK_DATE_M;
+
+	for (i = 0; i < nf; i++)
+	{
+		switch (ftype[i])
+		{
+			case DTK_TIME:
+				if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
+					return -1;
+				break;
+
+			case DTK_NUMBER:
+				flen = strlen(field[i]);
+
+				if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
+					return -1;
+				break;
+
+			case DTK_STRING:
+			case DTK_SPECIAL:
+				type = DecodeSpecial(i, field[i], &val);
+				if (type == IGNORE)
+					continue;
+
+				tmask = DTK_M(type);
+				switch (type)
+				{
+					case RESERV:
+						switch (val)
+						{
+							case DTK_NOW:
+								tmask = DTK_TIME_M;
+								*dtype = DTK_TIME;
+								GetCurrentTime(tm);
+								break;
+
+							case DTK_ZULU:
+								tmask = (DTK_TIME_M | DTK_M(TZ));
+								*dtype = DTK_TIME;
+								tm->tm_hour = 0;
+								tm->tm_min = 0;
+								tm->tm_sec = 0;
+								tm->tm_isdst = 0;
+								break;
+
+							default:
+								return -1;
+						}
+
+						break;
+
+					case IGNORE:
+						break;
+
+					case AMPM:
+						mer = val;
+						break;
+
+					default:
+						return -1;
+				}
+				break;
+
+			default:
+				return -1;
+		}
+
+		if (tmask & fmask)
+			return -1;
+		fmask |= tmask;
+	}
+
+	if ((mer != HR24) && (tm->tm_hour > 12))
+		return -1;
+	if ((mer == AM) && (tm->tm_hour == 12))
+		tm->tm_hour = 0;
+	else if ((mer == PM) && (tm->tm_hour != 12))
+		tm->tm_hour += 12;
+
+	if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
+		|| ((tm->tm_min < 0) || (tm->tm_min > 59))
+		|| ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60)))
+		return -1;
+
+	if ((fmask & DTK_TIME_M) != DTK_TIME_M)
+		return -1;
+
+	return 0;
+}	/* DecodeTimeOnly() */
+
+
+/* DecodeDate()
+ * Decode date string which includes delimiters.
+ * Insist on a complete set of fields.
+ */
+int
+DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
+{
 	double		fsec;
-	struct tm	tt,
-			   *tm = &tt;
 
-	int			nf;
-	char		lowstr[MAXDATELEN + 1];
+	int			nf = 0;
+	int			i,
+				len;
+	int			bc = FALSE;
+	int			is2digits = FALSE;
+	int			type,
+				val,
+				dmask = 0;
 	char	   *field[MAXDATEFIELDS];
-	int			dtype;
-	int			ftype[MAXDATEFIELDS];
 
-	if (!PointerIsValid(str))
-		elog(ERROR, "Bad (null) time external representation");
+	/* parse this string... */
+	while ((*str != '\0') && (nf < MAXDATEFIELDS))
+	{
+		/* skip field separators */
+		while (!isalnum(*str))
+			str++;
 
-	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
-		|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec) != 0))
-		elog(ERROR, "Bad time external representation '%s'", str);
+		field[nf] = str;
+		if (isdigit(*str))
+		{
+			while (isdigit(*str))
+				str++;
+		}
+		else if (isalpha(*str))
+		{
+			while (isalpha(*str))
+				str++;
+		}
 
-	time = palloc(sizeof(TimeADT));
+		if (*str != '\0')
+			*str++ = '\0';
+		nf++;
+	}
 
-	*time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
+#if 0
+	/* don't allow too many fields */
+	if (nf > 3)
+		return -1;
+#endif
 
-	return time;
-}	/* time_in() */
+	*tmask = 0;
+
+	/* look first for text fields, since that will be unambiguous month */
+	for (i = 0; i < nf; i++)
+	{
+		if (isalpha(*field[i]))
+		{
+			type = DecodeSpecial(i, field[i], &val);
+			if (type == IGNORE)
+				continue;
+
+			dmask = DTK_M(type);
+			switch (type)
+			{
+				case MONTH:
+					tm->tm_mon = val;
+					break;
+
+				case ADBC:
+					bc = (val == BC);
+					break;
+
+				default:
+					return -1;
+			}
+			if (fmask & dmask)
+				return -1;
+
+			fmask |= dmask;
+			*tmask |= dmask;
+
+			/* mark this field as being completed */
+			field[i] = NULL;
+		}
+	}
+
+	/* now pick up remaining numeric fields */
+	for (i = 0; i < nf; i++)
+	{
+		if (field[i] == NULL)
+			continue;
+
+		if ((len = strlen(field[i])) <= 0)
+			return -1;
+
+		if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
+			return -1;
+
+		if (fmask & dmask)
+			return -1;
+
+		fmask |= dmask;
+		*tmask |= dmask;
+	}
+
+	if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
+		return -1;
+
+	/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
+	if (bc)
+	{
+		if (tm->tm_year > 0)
+			tm->tm_year = -(tm->tm_year - 1);
+		else
+			elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year);
+	}
+	else if (is2digits)
+	{
+		if (tm->tm_year < 70)
+			tm->tm_year += 2000;
+		else if (tm->tm_year < 100)
+			tm->tm_year += 1900;
+	}
+
+	return 0;
+}	/* DecodeDate() */
 
 
-char *
-time_out(TimeADT *time)
+/* DecodeTime()
+ * Decode time string which includes delimiters.
+ * Only check the lower limit on hours, since this same code
+ *	can be used to represent time spans.
+ */
+int
+DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
 {
-	char	   *result;
-	struct tm	tt,
-			   *tm = &tt;
+	char	   *cp;
 
-	double		fsec;
-	char		buf[MAXDATELEN + 1];
+	*tmask = DTK_TIME_M;
 
-	if (!PointerIsValid(time))
-		return NULL;
+	tm->tm_hour = strtol(str, &cp, 10);
+	if (*cp != ':')
+		return -1;
+	str = cp + 1;
+	tm->tm_min = strtol(str, &cp, 10);
+	if (*cp == '\0')
+	{
+		tm->tm_sec = 0;
+		*fsec = 0;
 
-	tm->tm_hour = (*time / (60 * 60));
-	tm->tm_min = (((int) (*time / 60)) % 60);
-	tm->tm_sec = (((int) *time) % 60);
+	}
+	else if (*cp != ':')
+	{
+		return -1;
 
-	fsec = 0;
+	}
+	else
+	{
+		str = cp + 1;
+		tm->tm_sec = strtol(str, &cp, 10);
+		if (*cp == '\0')
+			*fsec = 0;
+		else if (*cp == '.')
+		{
+			str = cp;
+			*fsec = strtod(str, &cp);
+			if (cp == str)
+				return -1;
+		}
+		else
+			return -1;
+	}
 
-	EncodeTimeOnly(tm, fsec, DateStyle, buf);
+	/* do a sanity check */
+	if ((tm->tm_hour < 0)
+		|| (tm->tm_min < 0) || (tm->tm_min > 59)
+		|| (tm->tm_sec < 0) || (tm->tm_sec > 59))
+		return -1;
 
-	result = palloc(strlen(buf) + 1);
-
-	strcpy(result, buf);
-
-	return result;
-}	/* time_out() */
+	return 0;
+}	/* DecodeTime() */
 
 
-bool
-time_eq(TimeADT *time1, TimeADT *time2)
+/* DecodeNumber()
+ * Interpret numeric field as a date value in context.
+ */
+int
+DecodeNumber(int flen, char *str, int fmask,
+			 int *tmask, struct tm * tm, double *fsec, int *is2digits)
 {
-	if (!PointerIsValid(time1) || !PointerIsValid(time2))
-		return FALSE;
+	int			val;
+	char	   *cp;
 
-	return *time1 == *time2;
-}	/* time_eq() */
+	*tmask = 0;
 
-bool
-time_ne(TimeADT *time1, TimeADT *time2)
+	val = strtol(str, &cp, 10);
+	if (cp == str)
+		return -1;
+	if (*cp == '.')
+	{
+		*fsec = strtod(cp, &cp);
+		if (*cp != '\0')
+			return -1;
+	}
+
+	/* Special case day of year? */
+	if ((flen == 3) && (fmask & DTK_M(YEAR))
+		&& ((val >= 1) && (val <= 366)))
+	{
+		*tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
+		tm->tm_yday = val;
+		j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
+			   &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+
+	}
+
+	/*
+	 * Enough digits to be unequivocal year? Used to test for 4 digits or
+	 * more, but we now test first for a three-digit doy so anything
+	 * bigger than two digits had better be an explicit year. - thomas
+	 * 1999-01-09
+	 */
+	else if (flen > 2)
+	{
+		*tmask = DTK_M(YEAR);
+
+		/* already have a year? then see if we can substitute... */
+		if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
+			&& ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
+		{
+			tm->tm_mday = tm->tm_year;
+			*tmask = DTK_M(DAY);
+		}
+
+		tm->tm_year = val;
+	}
+	/* already have year? then could be month */
+	else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
+			 && ((val >= 1) && (val <= 12)))
+	{
+		*tmask = DTK_M(MONTH);
+		tm->tm_mon = val;
+		/* no year and EuroDates enabled? then could be day */
+	}
+	else if ((EuroDates || (fmask & DTK_M(MONTH)))
+			 && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
+			 && ((val >= 1) && (val <= 31)))
+	{
+		*tmask = DTK_M(DAY);
+		tm->tm_mday = val;
+	}
+	else if ((!(fmask & DTK_M(MONTH)))
+			 && ((val >= 1) && (val <= 12)))
+	{
+		*tmask = DTK_M(MONTH);
+		tm->tm_mon = val;
+	}
+	else if ((!(fmask & DTK_M(DAY)))
+			 && ((val >= 1) && (val <= 31)))
+	{
+		*tmask = DTK_M(DAY);
+		tm->tm_mday = val;
+	}
+	else if (!(fmask & DTK_M(YEAR)))
+	{
+		*tmask = DTK_M(YEAR);
+		tm->tm_year = val;
+
+		/* adjust ONLY if exactly two digits... */
+		*is2digits = (flen == 2);
+	}
+	else
+		return -1;
+
+	return 0;
+}	/* DecodeNumber() */
+
+
+/* DecodeNumberField()
+ * Interpret numeric string as a concatenated date field.
+ */
+int
+DecodeNumberField(int len, char *str, int fmask,
+				int *tmask, struct tm * tm, double *fsec, int *is2digits)
 {
-	if (!PointerIsValid(time1) || !PointerIsValid(time2))
-		return FALSE;
+	char	   *cp;
 
-	return *time1 != *time2;
-}	/* time_eq() */
+	/* yyyymmdd? */
+	if (len == 8)
+	{
+		*tmask = DTK_DATE_M;
 
-bool
-time_lt(TimeADT *time1, TimeADT *time2)
+		tm->tm_mday = atoi(str + 6);
+		*(str + 6) = '\0';
+		tm->tm_mon = atoi(str + 4);
+		*(str + 4) = '\0';
+		tm->tm_year = atoi(str + 0);
+		/* yymmdd or hhmmss? */
+	}
+	else if (len == 6)
+	{
+		if (fmask & DTK_DATE_M)
+		{
+			*tmask = DTK_TIME_M;
+			tm->tm_sec = atoi(str + 4);
+			*(str + 4) = '\0';
+			tm->tm_min = atoi(str + 2);
+			*(str + 2) = '\0';
+			tm->tm_hour = atoi(str + 0);
+		}
+		else
+		{
+			*tmask = DTK_DATE_M;
+			tm->tm_mday = atoi(str + 4);
+			*(str + 4) = '\0';
+			tm->tm_mon = atoi(str + 2);
+			*(str + 2) = '\0';
+			tm->tm_year = atoi(str + 0);
+			*is2digits = TRUE;
+		}
+
+	}
+	else if ((len == 5) && !(fmask & DTK_DATE_M))
+	{
+		*tmask = DTK_DATE_M;
+		tm->tm_mday = atoi(str + 2);
+		*(str + 2) = '\0';
+		tm->tm_mon = 1;
+		tm->tm_year = atoi(str + 0);
+		*is2digits = TRUE;
+	}
+	else if (strchr(str, '.') != NULL)
+	{
+		*tmask = DTK_TIME_M;
+		tm->tm_sec = strtod((str + 4), &cp);
+		if (cp == (str + 4))
+			return -1;
+		if (*cp == '.')
+			*fsec = strtod(cp, NULL);
+		*(str + 4) = '\0';
+		tm->tm_min = strtod((str + 2), &cp);
+		*(str + 2) = '\0';
+		tm->tm_hour = strtod((str + 0), &cp);
+
+	}
+	else
+		return -1;
+
+	return 0;
+}	/* DecodeNumberField() */
+
+
+/* DecodeTimezone()
+ * Interpret string as a numeric timezone.
+ */
+int
+DecodeTimezone(char *str, int *tzp)
 {
-	if (!PointerIsValid(time1) || !PointerIsValid(time2))
-		return FALSE;
+	int			tz;
+	int			hr,
+				min;
+	char	   *cp;
+	int			len;
 
-	return *time1 < *time2;
-}	/* time_eq() */
+	/* assume leading character is "+" or "-" */
+	hr = strtol((str + 1), &cp, 10);
 
-bool
-time_le(TimeADT *time1, TimeADT *time2)
+	/* explicit delimiter? */
+	if (*cp == ':')
+	{
+		min = strtol((cp + 1), &cp, 10);
+
+		/* otherwise, might have run things together... */
+	}
+	else if ((*cp == '\0') && ((len = strlen(str)) > 3))
+	{
+		min = strtol((str + len - 2), &cp, 10);
+		*(str + len - 2) = '\0';
+		hr = strtol((str + 1), &cp, 10);
+
+	}
+	else
+		min = 0;
+
+	tz = (hr * 60 + min) * 60;
+	if (*str == '-')
+		tz = -tz;
+
+	*tzp = -tz;
+	return *cp != '\0';
+}	/* DecodeTimezone() */
+
+
+/* DecodeSpecial()
+ * Decode text string using lookup table.
+ * Implement a cache lookup since it is likely that dates
+ *	will be related in format.
+ */
+int
+DecodeSpecial(int field, char *lowtoken, int *val)
 {
-	if (!PointerIsValid(time1) || !PointerIsValid(time2))
-		return FALSE;
+	int			type;
+	datetkn    *tp;
 
-	return *time1 <= *time2;
-}	/* time_eq() */
+#if USE_DATE_CACHE
+	if ((datecache[field] != NULL)
+		&& (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
+		tp = datecache[field];
+	else
+	{
+#endif
+		tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
+#if USE_DATE_CACHE
+	}
+	datecache[field] = tp;
+#endif
+	if (tp == NULL)
+	{
+		type = IGNORE;
+		*val = 0;
+	}
+	else
+	{
+		type = tp->type;
+		switch (type)
+		{
+			case TZ:
+			case DTZ:
+			case DTZMOD:
+				*val = FROMVAL(tp);
+				break;
 
-bool
-time_gt(TimeADT *time1, TimeADT *time2)
+			default:
+				*val = tp->value;
+				break;
+		}
+	}
+
+	return type;
+}	/* DecodeSpecial() */
+
+
+/* DecodeDateDelta()
+ * Interpret previously parsed fields for general time interval.
+ * Return 0 if decoded and -1 if problems.
+ *
+ * Allow "date" field DTK_DATE since this could be just
+ *	an unsigned floating point number. - thomas 1997-11-16
+ *
+ * Allow ISO-style time span, with implicit units on number of days
+ *	preceeding an hh:mm:ss field. - thomas 1998-04-30
+ */
+int
+DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
 {
-	if (!PointerIsValid(time1) || !PointerIsValid(time2))
-		return FALSE;
+	int			is_before = FALSE;
 
-	return *time1 > *time2;
-}	/* time_eq() */
+	char	   *cp;
+	int			fmask = 0,
+				tmask,
+				type;
+	int			i;
+	int			flen,
+				val;
+	double		fval;
+	double		sec;
 
-bool
-time_ge(TimeADT *time1, TimeADT *time2)
+	*dtype = DTK_DELTA;
+
+	type = DTK_SECOND;
+	tm->tm_year = 0;
+	tm->tm_mon = 0;
+	tm->tm_mday = 0;
+	tm->tm_hour = 0;
+	tm->tm_min = 0;
+	tm->tm_sec = 0;
+	*fsec = 0;
+
+	/* read through list backwards to pick up units before values */
+	for (i = nf - 1; i >= 0; i--)
+	{
+		switch (ftype[i])
+		{
+			case DTK_TIME:
+				if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
+					return -1;
+				type = DTK_DAY;
+				break;
+
+			case DTK_TZ:
+
+				/*
+				 * Timezone is a token with a leading sign character and
+				 * otherwise the same as a non-signed numeric field
+				 */
+			case DTK_DATE:
+			case DTK_NUMBER:
+				val = strtol(field[i], &cp, 10);
+				if (*cp == '.')
+				{
+					fval = strtod(cp, &cp);
+					if (*cp != '\0')
+						return -1;
+
+					if (val < 0)
+						fval = -(fval);
+				}
+				else if (*cp == '\0')
+					fval = 0;
+				else
+					return -1;
+
+				flen = strlen(field[i]);
+				tmask = 0;		/* DTK_M(type); */
+
+				switch (type)
+				{
+					case DTK_MICROSEC:
+						*fsec += ((val + fval) * 1e-6);
+						break;
+
+					case DTK_MILLISEC:
+						*fsec += ((val + fval) * 1e-3);
+						break;
+
+					case DTK_SECOND:
+						tm->tm_sec += val;
+						*fsec += fval;
+						tmask = DTK_M(SECOND);
+						break;
+
+					case DTK_MINUTE:
+						tm->tm_min += val;
+						if (fval != 0)
+							tm->tm_sec += (fval * 60);
+						tmask = DTK_M(MINUTE);
+						break;
+
+					case DTK_HOUR:
+						tm->tm_hour += val;
+						if (fval != 0)
+							tm->tm_sec += (fval * 3600);
+						tmask = DTK_M(HOUR);
+						break;
+
+					case DTK_DAY:
+						tm->tm_mday += val;
+						if (fval != 0)
+							tm->tm_sec += (fval * 86400);
+						tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
+						break;
+
+					case DTK_WEEK:
+						tm->tm_mday += val * 7;
+						if (fval != 0)
+							tm->tm_sec += (fval * (7 * 86400));
+						tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
+						break;
+
+					case DTK_MONTH:
+						tm->tm_mon += val;
+						if (fval != 0)
+							tm->tm_sec += (fval * (30 * 86400));
+						tmask = DTK_M(MONTH);
+						break;
+
+					case DTK_YEAR:
+						tm->tm_year += val;
+						if (fval != 0)
+							tm->tm_mon += (fval * 12);
+						tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
+						break;
+
+					case DTK_DECADE:
+						tm->tm_year += val * 10;
+						if (fval != 0)
+							tm->tm_mon += (fval * 120);
+						tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
+						break;
+
+					case DTK_CENTURY:
+						tm->tm_year += val * 100;
+						if (fval != 0)
+							tm->tm_mon += (fval * 1200);
+						tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
+						break;
+
+					case DTK_MILLENIUM:
+						tm->tm_year += val * 1000;
+						if (fval != 0)
+							tm->tm_mon += (fval * 12000);
+						tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
+						break;
+
+					default:
+						return -1;
+				}
+				break;
+
+			case DTK_STRING:
+			case DTK_SPECIAL:
+				type = DecodeUnits(i, field[i], &val);
+				if (type == IGNORE)
+					continue;
+
+				tmask = 0;		/* DTK_M(type); */
+				switch (type)
+				{
+					case UNITS:
+						type = val;
+						break;
+
+					case AGO:
+						is_before = TRUE;
+						type = val;
+						break;
+
+					case RESERV:
+						tmask = (DTK_DATE_M || DTK_TIME_M);
+						*dtype = val;
+						break;
+
+					default:
+						return -1;
+				}
+				break;
+
+			default:
+				return -1;
+		}
+
+		if (tmask & fmask)
+			return -1;
+		fmask |= tmask;
+	}
+
+	if (*fsec != 0)
+	{
+		TMODULO(*fsec, sec, 1e0);
+		tm->tm_sec += sec;
+	}
+
+	if (is_before)
+	{
+		*fsec = -(*fsec);
+		tm->tm_sec = -(tm->tm_sec);
+		tm->tm_min = -(tm->tm_min);
+		tm->tm_hour = -(tm->tm_hour);
+		tm->tm_mday = -(tm->tm_mday);
+		tm->tm_mon = -(tm->tm_mon);
+		tm->tm_year = -(tm->tm_year);
+	}
+
+	/* ensure that at least one time field has been found */
+	return (fmask != 0) ? 0 : -1;
+}	/* DecodeDateDelta() */
+
+
+/* DecodeUnits()
+ * Decode text string using lookup table.
+ * This routine supports time interval decoding.
+ */
+int
+DecodeUnits(int field, char *lowtoken, int *val)
 {
-	if (!PointerIsValid(time1) || !PointerIsValid(time2))
-		return FALSE;
+	int			type;
+	datetkn    *tp;
 
-	return *time1 >= *time2;
-}	/* time_eq() */
+#if USE_DATE_CACHE
+	if ((deltacache[field] != NULL)
+		&& (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0))
+		tp = deltacache[field];
+	else
+	{
+#endif
+		tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
+#if USE_DATE_CACHE
+	}
+	deltacache[field] = tp;
+#endif
+	if (tp == NULL)
+	{
+		type = IGNORE;
+		*val = 0;
+	}
+	else
+	{
+		type = tp->type;
+		if ((type == TZ) || (type == DTZ))
+			*val = FROMVAL(tp);
+		else
+			*val = tp->value;
+	}
+
+	return type;
+}	/* DecodeUnits() */
+
+
+/* datebsearch()
+ * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
+ * is WAY faster than the generic bsearch().
+ */
+datetkn *
+datebsearch(char *key, datetkn *base, unsigned int nel)
+{
+	datetkn    *last = base + nel - 1,
+			   *position;
+	int			result;
+
+	while (last >= base)
+	{
+		position = base + ((last - base) >> 1);
+		result = key[0] - position->token[0];
+		if (result == 0)
+		{
+			result = strncmp(key, position->token, TOKMAXLEN);
+			if (result == 0)
+				return position;
+		}
+		if (result < 0)
+			last = position - 1;
+		else
+			base = position + 1;
+	}
+	return NULL;
+}
+
+
+/* EncodeDateOnly()
+ * Encode date as local time.
+ */
+int
+EncodeDateOnly(struct tm * tm, int style, char *str)
+{
+	if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
+		return -1;
+
+	switch (style)
+	{
+			/* compatible with ISO date formats */
+		case USE_ISO_DATES:
+			if (tm->tm_year > 0)
+				sprintf(str, "%04d-%02d-%02d",
+						tm->tm_year, tm->tm_mon, tm->tm_mday);
+			else
+				sprintf(str, "%04d-%02d-%02d %s",
+					  -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
+			break;
+
+			/* compatible with Oracle/Ingres date formats */
+		case USE_SQL_DATES:
+			if (EuroDates)
+				sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
+			else
+				sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
+			if (tm->tm_year > 0)
+				sprintf((str + 5), "/%04d", tm->tm_year);
+			else
+				sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
+			break;
+
+			/* German-style date format */
+		case USE_GERMAN_DATES:
+			sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
+			if (tm->tm_year > 0)
+				sprintf((str + 5), ".%04d", tm->tm_year);
+			else
+				sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
+			break;
+
+			/* traditional date-only style for Postgres */
+		case USE_POSTGRES_DATES:
+		default:
+			if (EuroDates)
+				sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
+			else
+				sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
+			if (tm->tm_year > 0)
+				sprintf((str + 5), "-%04d", tm->tm_year);
+			else
+				sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
+			break;
+	}
+
+	return TRUE;
+}	/* EncodeDateOnly() */
+
+
+/* EncodeTimeOnly()
+ * Encode time fields only.
+ */
+int
+EncodeTimeOnly(struct tm * tm, double fsec, int style, char *str)
+{
+	double		sec;
+
+	if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
+		return -1;
+
+	sec = (tm->tm_sec + fsec);
+
+	sprintf(str, "%02d:%02d:", tm->tm_hour, tm->tm_min);
+	sprintf((str + 6), ((fsec != 0) ? "%05.2f" : "%02.0f"), sec);
+
+	return TRUE;
+}	/* EncodeTimeOnly() */
+
+
+/* EncodeDateTime()
+ * Encode date and time interpreted as local time.
+ * Support several date styles:
+ *	Postgres - day mon hh:mm:ss yyyy tz
+ *	SQL - mm/dd/yyyy hh:mm:ss.ss tz
+ *	ISO - yyyy-mm-dd hh:mm:ss+/-tz
+ *	German - dd.mm/yyyy hh:mm:ss tz
+ * Variants (affects order of month and day for Postgres and SQL styles):
+ *	US - mm/dd/yyyy
+ *	European - dd/mm/yyyy
+ */
+int
+EncodeDateTime(struct tm * tm, double fsec, int *tzp, char **tzn, int style, char *str)
+{
+	int			day,
+				hour,
+				min;
+	double		sec;
+
+	if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
+		return -1;
+
+	sec = (tm->tm_sec + fsec);
+
+	switch (style)
+	{
+			/* compatible with ISO date formats */
+
+		case USE_ISO_DATES:
+			if (tm->tm_year > 0)
+			{
+				sprintf(str, "%04d-%02d-%02d %02d:%02d:",
+						tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
+				sprintf((str + 17), ((fsec != 0) ? "%05.2f" : "%02.0f"), sec);
+
+				if ((*tzn != NULL) && (tm->tm_isdst >= 0))
+				{
+					if (tzp != NULL)
+					{
+						hour = -(*tzp / 3600);
+						min = ((abs(*tzp) / 60) % 60);
+					}
+					else
+					{
+						hour = 0;
+						min = 0;
+					}
+					sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
+				}
+
+			}
+			else
+			{
+				if (tm->tm_hour || tm->tm_min)
+					sprintf(str, "%04d-%02d-%02d %02d:%02d %s",
+							-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, "BC");
+				else
+					sprintf(str, "%04d-%02d-%02d %s",
+					  -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
+			}
+			break;
+
+			/* compatible with Oracle/Ingres date formats */
+		case USE_SQL_DATES:
+			if (EuroDates)
+				sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
+			else
+				sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
+
+			if (tm->tm_year > 0)
+			{
+				sprintf((str + 5), "/%04d %02d:%02d:%05.2f",
+						tm->tm_year, tm->tm_hour, tm->tm_min, sec);
+
+				if ((*tzn != NULL) && (tm->tm_isdst >= 0))
+				{
+					strcpy((str + 22), " ");
+					strcpy((str + 23), *tzn);
+				}
+
+			}
+			else
+				sprintf((str + 5), "/%04d %02d:%02d %s",
+					  -(tm->tm_year - 1), tm->tm_hour, tm->tm_min, "BC");
+			break;
+
+			/* German variant on European style */
+		case USE_GERMAN_DATES:
+			sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
+			if (tm->tm_year > 0)
+			{
+				sprintf((str + 5), ".%04d %02d:%02d:%05.2f",
+						tm->tm_year, tm->tm_hour, tm->tm_min, sec);
+
+				if ((*tzn != NULL) && (tm->tm_isdst >= 0))
+				{
+					strcpy((str + 22), " ");
+					strcpy((str + 23), *tzn);
+				}
+
+			}
+			else
+				sprintf((str + 5), ".%04d %02d:%02d %s",
+					  -(tm->tm_year - 1), tm->tm_hour, tm->tm_min, "BC");
+			break;
+
+			/* backward-compatible with traditional Postgres abstime dates */
+		case USE_POSTGRES_DATES:
+		default:
+			day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
+			tm->tm_wday = j2day(day);
+
+			strncpy(str, days[tm->tm_wday], 3);
+			strcpy((str + 3), " ");
+
+			if (EuroDates)
+				sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
+			else
+				sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
+
+			if (tm->tm_year > 0)
+			{
+				sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
+				if (fsec != 0)
+				{
+					sprintf((str + 16), ":%05.2f %04d", sec, tm->tm_year);
+					if ((*tzn != NULL) && (tm->tm_isdst >= 0))
+					{
+						strcpy((str + 27), " ");
+						strncpy((str + 28), *tzn, MAXTZLEN);
+					}
+				}
+				else
+				{
+					sprintf((str + 16), ":%02.0f %04d", sec, tm->tm_year);
+					if ((*tzn != NULL) && (tm->tm_isdst >= 0))
+					{
+						strcpy((str + 24), " ");
+						strncpy((str + 25), *tzn, MAXTZLEN);
+					}
+				}
+
+			}
+			else
+			{
+				sprintf((str + 10), " %02d:%02d %04d %s",
+					  tm->tm_hour, tm->tm_min, -(tm->tm_year - 1), "BC");
+			}
+			break;
+	}
+
+	return TRUE;
+}	/* EncodeDateTime() */
+
+
+/* EncodeTimeSpan()
+ * Interpret time structure as a delta time and convert to string.
+ *
+ * Support "traditional Postgres" and ISO-8601 styles.
+ * Actually, afaik ISO does not address time interval formatting,
+ *	but this looks similar to the spec for absolute date/time.
+ * - thomas 1998-04-30
+ */
+int
+EncodeTimeSpan(struct tm * tm, double fsec, int style, char *str)
+{
+	int			is_before = FALSE;
+	int			is_nonzero = FALSE;
+	char	   *cp = str;
+
+	switch (style)
+	{
+			/* compatible with ISO date formats */
+		case USE_ISO_DATES:
+			break;
+
+		default:
+			strcpy(cp, "@ ");
+			cp += strlen(cp);
+			break;
+	}
+
+	if (tm->tm_year != 0)
+	{
+		is_before |= (tm->tm_year < 0);
+		sprintf(cp, "%d year%s",
+				abs(tm->tm_year), ((abs(tm->tm_year) != 1) ? "s" : ""));
+		cp += strlen(cp);
+		is_nonzero = TRUE;
+	}
+
+	if (tm->tm_mon != 0)
+	{
+		is_before |= (tm->tm_mon < 0);
+		sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
+				abs(tm->tm_mon), ((abs(tm->tm_mon) != 1) ? "s" : ""));
+		cp += strlen(cp);
+		is_nonzero = TRUE;
+	}
+
+	switch (style)
+	{
+			/* compatible with ISO date formats */
+		case USE_ISO_DATES:
+			if (tm->tm_mday != 0)
+			{
+				is_before |= (tm->tm_mday < 0);
+				sprintf(cp, "%s%d", (is_nonzero ? " " : ""), abs(tm->tm_mday));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+			}
+			is_before |= ((tm->tm_hour < 0) || (tm->tm_min < 0));
+			sprintf(cp, "%s%02d:%02d", (is_nonzero ? " " : ""),
+					abs(tm->tm_hour), abs(tm->tm_min));
+			cp += strlen(cp);
+			if ((tm->tm_hour != 0) || (tm->tm_min != 0))
+				is_nonzero = TRUE;
+
+			/* fractional seconds? */
+			if (fsec != 0)
+			{
+				fsec += tm->tm_sec;
+				is_before |= (fsec < 0);
+				sprintf(cp, ":%05.2f", fabs(fsec));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+
+				/* otherwise, integer seconds only? */
+			}
+			else if (tm->tm_sec != 0)
+			{
+				is_before |= (tm->tm_sec < 0);
+				sprintf(cp, ":%02d", abs(tm->tm_sec));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+			}
+			break;
+
+		case USE_POSTGRES_DATES:
+		default:
+			if (tm->tm_mday != 0)
+			{
+				is_before |= (tm->tm_mday < 0);
+				sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""),
+				 abs(tm->tm_mday), ((abs(tm->tm_mday) != 1) ? "s" : ""));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+			}
+			if (tm->tm_hour != 0)
+			{
+				is_before |= (tm->tm_hour < 0);
+				sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""),
+				 abs(tm->tm_hour), ((abs(tm->tm_hour) != 1) ? "s" : ""));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+			}
+
+			if (tm->tm_min != 0)
+			{
+				is_before |= (tm->tm_min < 0);
+				sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""),
+				   abs(tm->tm_min), ((abs(tm->tm_min) != 1) ? "s" : ""));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+			}
+
+			/* fractional seconds? */
+			if (fsec != 0)
+			{
+				fsec += tm->tm_sec;
+				is_before |= (fsec < 0);
+				sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), fabs(fsec));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+
+				/* otherwise, integer seconds only? */
+			}
+			else if (tm->tm_sec != 0)
+			{
+				is_before |= (tm->tm_sec < 0);
+				sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""),
+				   abs(tm->tm_sec), ((abs(tm->tm_sec) != 1) ? "s" : ""));
+				cp += strlen(cp);
+				is_nonzero = TRUE;
+			}
+			break;
+	}
+
+	/* identically zero? then put in a unitless zero... */
+	if (!is_nonzero)
+	{
+		strcat(cp, "0");
+		cp += strlen(cp);
+	}
+
+	if (is_before)
+	{
+		strcat(cp, " ago");
+		cp += strlen(cp);
+	}
+
+	return 0;
+}	/* EncodeTimeSpan() */
+
+
+#if defined(linux) && defined(__powerpc__)
 
 int
-time_cmp(TimeADT *time1, TimeADT *time2)
+timestamp_is_epoch(double j)
 {
-	return (*time1 < *time2) ? -1 : (((*time1 > *time2) ? 1 : 0));
-}	/* time_cmp() */
-
-
-/* datetime_time()
- * Convert datetime to time data type.
- */
-TimeADT    *
-datetime_time(DateTime *datetime)
-{
-	TimeADT    *result;
-	struct tm	tt,
-			   *tm = &tt;
-	int			tz;
-	double		fsec;
-	char	   *tzn;
-
-	if (!PointerIsValid(datetime))
-		elog(ERROR, "Unable to convert null datetime to date");
-
-	if (DATETIME_NOT_FINITE(*datetime))
-		elog(ERROR, "Unable to convert datetime to date");
-
-	if (DATETIME_IS_EPOCH(*datetime))
+	static union
 	{
-		datetime2tm(SetDateTime(*datetime), NULL, tm, &fsec, NULL);
+		double		epoch;
+		unsigned char c[8];
+	}			u;
 
-	}
-	else if (DATETIME_IS_CURRENT(*datetime))
-	{
-		datetime2tm(SetDateTime(*datetime), &tz, tm, &fsec, &tzn);
+	u.c[0] = 0x80;				/* sign bit */
+	u.c[1] = 0x10;				/* DBL_MIN */
 
-	}
-	else
-	{
-		if (datetime2tm(*datetime, &tz, tm, &fsec, &tzn) != 0)
-			elog(ERROR, "Unable to convert datetime to date");
-	}
-
-	result = palloc(sizeof(TimeADT));
-
-	*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
-
-	return result;
-}	/* datetime_time() */
-
-
-/* datetime_datetime()
- * Convert date and time to datetime data type.
- */
-DateTime   *
-datetime_datetime(DateADT date, TimeADT *time)
-{
-	DateTime   *result;
-
-	if (!PointerIsValid(time))
-	{
-		result = palloc(sizeof(DateTime));
-		DATETIME_INVALID(*result);
-	}
-	else
-	{
-		result = date_datetime(date);
-		*result += *time;
-	}
-
-	return result;
-}	/* datetime_datetime() */
-
-
-int32							/* RelativeTime */
-int4reltime(int32 timevalue)
-{
-	return timevalue;
+	return j == u.epoch;
 }
+int
+timestamp_is_current(double j)
+{
+	static union
+	{
+		double		current;
+		unsigned char c[8];
+	}			u;
+
+	u.c[1] = 0x10;				/* DBL_MIN */
+
+	return j == u.current;
+}
+
+#endif