From a303e4dc431627231238c9071ae79198c95b4581 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 14 Oct 2008 17:12:33 +0000
Subject: [PATCH] Extend the date type to support infinity and -infinity,
 analogously to the timestamp types.  Turns out this doesn't even reduce the
 available range of dates, since the restriction to dates that work for
 Julian-date arithmetic is much tighter than the int32 range anyway.  Per a
 longstanding TODO item.

---
 doc/src/sgml/datatype.sgml         |   6 +-
 doc/src/sgml/func.sgml             |  12 +-
 src/backend/utils/adt/date.c       | 187 ++++++++++++++++++++---------
 src/backend/utils/adt/xml.c        |   7 +-
 src/include/catalog/catversion.h   |   4 +-
 src/include/catalog/pg_proc.h      |   4 +-
 src/include/utils/date.h           |  17 ++-
 src/test/regress/expected/date.out |  27 +++++
 src/test/regress/sql/date.sql      |   7 ++
 9 files changed, 206 insertions(+), 65 deletions(-)

diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index d05c93058a..26521661b7 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.229 2008/10/03 15:37:18 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.230 2008/10/14 17:12:32 tgl Exp $ -->
 
  <chapter id="datatype">
   <title id="datatype-title">Data Types</title>
@@ -2032,12 +2032,12 @@ January 8 04:05:06 1999 PST
          </row>
          <row>
           <entry><literal>infinity</literal></entry>
-          <entry><type>timestamp</type></entry>
+          <entry><type>date</type>, <type>timestamp</type></entry>
           <entry>later than all other time stamps</entry>
          </row>
          <row>
           <entry><literal>-infinity</literal></entry>
-          <entry><type>timestamp</type></entry>
+          <entry><type>date</type>, <type>timestamp</type></entry>
           <entry>earlier than all other time stamps</entry>
          </row>
          <row>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index e289326f2f..06ee6a0572 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.449 2008/10/13 16:25:19 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.450 2008/10/14 17:12:32 tgl Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
@@ -5912,10 +5912,18 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
         <entry><literal>3</literal></entry>
        </row>
 
+       <row>
+        <entry><literal><function>isfinite</function>(<type>date</type>)</literal></entry>
+        <entry><type>boolean</type></entry>
+        <entry>Test for finite date (not +/-infinity)</entry>
+        <entry><literal>isfinite(date '2001-02-16')</literal></entry>
+        <entry><literal>true</literal></entry>
+       </row>
+
        <row>
         <entry><literal><function>isfinite</function>(<type>timestamp</type>)</literal></entry>
         <entry><type>boolean</type></entry>
-        <entry>Test for finite time stamp (not equal to infinity)</entry>
+        <entry>Test for finite time stamp (not +/-infinity)</entry>
         <entry><literal>isfinite(timestamp '2001-02-16 21:28:30')</literal></entry>
         <entry><literal>true</literal></entry>
        </row>
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 6f10b666ca..ffeb754109 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.142 2008/07/07 18:09:46 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.143 2008/10/14 17:12:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #endif
 
 
+static void EncodeSpecialDate(DateADT dt, char *str);
 static int	time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
 static int	timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
 static int	tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
@@ -147,6 +148,14 @@ date_in(PG_FUNCTION_ARGS)
 			GetEpochTime(tm);
 			break;
 
+		case DTK_LATE:
+			DATE_NOEND(date);
+			PG_RETURN_DATEADT(date);
+
+		case DTK_EARLY:
+			DATE_NOBEGIN(date);
+			PG_RETURN_DATEADT(date);
+
 		default:
 			DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
 			break;
@@ -174,10 +183,14 @@ date_out(PG_FUNCTION_ARGS)
 			   *tm = &tt;
 	char		buf[MAXDATELEN + 1];
 
-	j2date(date + POSTGRES_EPOCH_JDATE,
-		   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
-
-	EncodeDateOnly(tm, DateStyle, buf);
+	if (DATE_NOT_FINITE(date))
+		EncodeSpecialDate(date, buf);
+	else
+	{
+		j2date(date + POSTGRES_EPOCH_JDATE,
+			   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
+		EncodeDateOnly(tm, DateStyle, buf);
+	}
 
 	result = pstrdup(buf);
 	PG_RETURN_CSTRING(result);
@@ -208,6 +221,20 @@ date_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+/*
+ * Convert reserved date values to string.
+ */
+static void
+EncodeSpecialDate(DateADT dt, char *str)
+{
+	if (DATE_IS_NOBEGIN(dt))
+		strcpy(str, EARLY);
+	else if (DATE_IS_NOEND(dt))
+		strcpy(str, LATE);
+	else						/* shouldn't happen */
+		elog(ERROR, "invalid argument for EncodeSpecialDate");
+}
+
 
 /*
  * Comparison functions for dates
@@ -280,6 +307,14 @@ date_cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(0);
 }
 
+Datum
+date_finite(PG_FUNCTION_ARGS)
+{
+	DateADT		date = PG_GETARG_DATEADT(0);
+
+	PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
+}
+
 Datum
 date_larger(PG_FUNCTION_ARGS)
 {
@@ -306,6 +341,11 @@ date_mi(PG_FUNCTION_ARGS)
 	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
 	DateADT		dateVal2 = PG_GETARG_DATEADT(1);
 
+	if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("cannot subtract infinite dates")));
+
 	PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
 }
 
@@ -318,6 +358,9 @@ date_pli(PG_FUNCTION_ARGS)
 	DateADT		dateVal = PG_GETARG_DATEADT(0);
 	int32		days = PG_GETARG_INT32(1);
 
+	if (DATE_NOT_FINITE(dateVal))
+		days = 0;				/* can't change infinity */
+
 	PG_RETURN_DATEADT(dateVal + days);
 }
 
@@ -329,6 +372,9 @@ date_mii(PG_FUNCTION_ARGS)
 	DateADT		dateVal = PG_GETARG_DATEADT(0);
 	int32		days = PG_GETARG_INT32(1);
 
+	if (DATE_NOT_FINITE(dateVal))
+		days = 0;				/* can't change infinity */
+
 	PG_RETURN_DATEADT(dateVal - days);
 }
 
@@ -342,18 +388,25 @@ date2timestamp(DateADT dateVal)
 {
 	Timestamp	result;
 
+	if (DATE_IS_NOBEGIN(dateVal))
+		TIMESTAMP_NOBEGIN(result);
+	else if (DATE_IS_NOEND(dateVal))
+		TIMESTAMP_NOEND(result);
+	else
+	{
 #ifdef HAVE_INT64_TIMESTAMP
-	/* date is days since 2000, timestamp is microseconds since same... */
-	result = dateVal * USECS_PER_DAY;
-	/* Date's range is wider than timestamp's, so must check for overflow */
-	if (result / USECS_PER_DAY != dateVal)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-				 errmsg("date out of range for timestamp")));
+		/* date is days since 2000, timestamp is microseconds since same... */
+		result = dateVal * USECS_PER_DAY;
+		/* Date's range is wider than timestamp's, so check for overflow */
+		if (result / USECS_PER_DAY != dateVal)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("date out of range for timestamp")));
 #else
-	/* date is days since 2000, timestamp is seconds since same... */
-	result = dateVal * (double) SECS_PER_DAY;
+		/* date is days since 2000, timestamp is seconds since same... */
+		result = dateVal * (double) SECS_PER_DAY;
 #endif
+	}
 
 	return result;
 }
@@ -366,24 +419,30 @@ date2timestamptz(DateADT dateVal)
 			   *tm = &tt;
 	int			tz;
 
-	j2date(dateVal + POSTGRES_EPOCH_JDATE,
-		   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
-
-	tm->tm_hour = 0;
-	tm->tm_min = 0;
-	tm->tm_sec = 0;
-	tz = DetermineTimeZoneOffset(tm, session_timezone);
+	if (DATE_IS_NOBEGIN(dateVal))
+		TIMESTAMP_NOBEGIN(result);
+	else if (DATE_IS_NOEND(dateVal))
+		TIMESTAMP_NOEND(result);
+	else
+	{
+		j2date(dateVal + POSTGRES_EPOCH_JDATE,
+			   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
+		tm->tm_hour = 0;
+		tm->tm_min = 0;
+		tm->tm_sec = 0;
+		tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 #ifdef HAVE_INT64_TIMESTAMP
-	result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
-	/* Date's range is wider than timestamp's, so must check for overflow */
-	if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-				 errmsg("date out of range for timestamp")));
+		result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
+		/* Date's range is wider than timestamp's, so check for overflow */
+		if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("date out of range for timestamp")));
 #else
-	result = dateVal * (double) SECS_PER_DAY + tz;
+		result = dateVal * (double) SECS_PER_DAY + tz;
 #endif
+	}
 
 	return result;
 }
@@ -797,15 +856,19 @@ timestamp_date(PG_FUNCTION_ARGS)
 			   *tm = &tt;
 	fsec_t		fsec;
 
-	if (TIMESTAMP_NOT_FINITE(timestamp))
-		PG_RETURN_NULL();
+	if (TIMESTAMP_IS_NOBEGIN(timestamp))
+		DATE_NOBEGIN(result);
+	else if (TIMESTAMP_IS_NOEND(timestamp))
+		DATE_NOEND(result);
+	else
+	{
+		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("timestamp out of range")));
 
-	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-				 errmsg("timestamp out of range")));
-
-	result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+		result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+	}
 
 	PG_RETURN_DATEADT(result);
 }
@@ -840,15 +903,19 @@ timestamptz_date(PG_FUNCTION_ARGS)
 	int			tz;
 	char	   *tzn;
 
-	if (TIMESTAMP_NOT_FINITE(timestamp))
-		PG_RETURN_NULL();
+	if (TIMESTAMP_IS_NOBEGIN(timestamp))
+		DATE_NOBEGIN(result);
+	else if (TIMESTAMP_IS_NOEND(timestamp))
+		DATE_NOEND(result);
+	else
+	{
+		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("timestamp out of range")));
 
-	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-				 errmsg("timestamp out of range")));
-
-	result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+		result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+	}
 
 	PG_RETURN_DATEADT(result);
 }
@@ -869,16 +936,19 @@ abstime_date(PG_FUNCTION_ARGS)
 	switch (abstime)
 	{
 		case INVALID_ABSTIME:
-		case NOSTART_ABSTIME:
-		case NOEND_ABSTIME:
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				   errmsg("cannot convert reserved abstime value to date")));
+			result = 0;			/* keep compiler quiet */
+			break;
 
-			/*
-			 * pretend to drop through to make compiler think that result will
-			 * be set
-			 */
+		case NOSTART_ABSTIME:
+			DATE_NOBEGIN(result);
+			break;
+
+		case NOEND_ABSTIME:
+			DATE_NOEND(result);
+			break;
 
 		default:
 			abstime2tm(abstime, &tz, tm, NULL);
@@ -1452,9 +1522,9 @@ datetime_timestamp(PG_FUNCTION_ARGS)
 	TimeADT		time = PG_GETARG_TIMEADT(1);
 	Timestamp	result;
 
-	result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
-												   DateADTGetDatum(date)));
-	result += time;
+	result = date2timestamp(date);
+	if (!TIMESTAMP_NOT_FINITE(result))
+		result += time;
 
 	PG_RETURN_TIMESTAMP(result);
 }
@@ -2304,11 +2374,18 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
 	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
 	TimestampTz result;
 
+	if (DATE_IS_NOBEGIN(date))
+		TIMESTAMP_NOBEGIN(result);
+	else if (DATE_IS_NOEND(date))
+		TIMESTAMP_NOEND(result);
+	else
+	{
 #ifdef HAVE_INT64_TIMESTAMP
-	result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
+		result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
 #else
-	result = date * (double) SECS_PER_DAY + time->time + time->zone;
+		result = date * (double) SECS_PER_DAY + time->time + time->zone;
 #endif
+	}
 
 	PG_RETURN_TIMESTAMP(result);
 }
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 4ef1f97ea7..e728e1254f 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.78 2008/10/09 15:49:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.79 2008/10/14 17:12:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1632,6 +1632,11 @@ map_sql_value_to_xml_value(Datum value, Oid type)
 					char		buf[MAXDATELEN + 1];
 
 					date = DatumGetDateADT(value);
+					/* XSD doesn't support infinite values */
+					if (DATE_NOT_FINITE(date))
+						ereport(ERROR,
+								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+								 errmsg("date out of range")));
 					j2date(date + POSTGRES_EPOCH_JDATE,
 						   &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
 					EncodeDateOnly(&tm, USE_XSD_DATES, buf);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0ab03e0578..cd9d06f65d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.497 2008/10/13 16:25:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.498 2008/10/14 17:12:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200810131
+#define CATALOG_VERSION_NO	200810141
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 70dc0a319a..9867f44f27 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.519 2008/10/13 16:25:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.520 2008/10/14 17:12:33 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -1772,6 +1772,8 @@ DESCR("date difference from today preserving months and years");
 DATA(insert OID = 1388 (  timetz	   PGNSP PGUID 12 1 0 0 f f t f s 1 1266 "1184" _null_ _null_ _null_	timestamptz_timetz _null_ _null_ _null_ ));
 DESCR("convert timestamptz to timetz");
 
+DATA(insert OID = 1373 (  isfinite	   PGNSP PGUID 12 1 0 0 f f t f i 1 16 "1082" _null_ _null_ _null_	date_finite _null_ _null_ _null_ ));
+DESCR("finite date?");
 DATA(insert OID = 1389 (  isfinite	   PGNSP PGUID 12 1 0 0 f f t f i 1 16 "1184" _null_ _null_ _null_	timestamp_finite _null_ _null_ _null_ ));
 DESCR("finite timestamp?");
 DATA(insert OID = 1390 (  isfinite	   PGNSP PGUID 12 1 0 0 f f t f i 1 16 "1186" _null_ _null_ _null_	interval_finite _null_ _null_ _null_ ));
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 5f23549836..9015c3db69 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/date.h,v 1.40 2008/03/21 01:31:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/date.h,v 1.41 2008/10/14 17:12:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,20 @@ typedef struct
 	int32		zone;			/* numeric time zone, in seconds */
 } TimeTzADT;
 
+/*
+ * Infinity and minus infinity must be the max and min values of DateADT.
+ * We could use INT_MIN and INT_MAX here, but seems better to not assume that
+ * int32 == int.
+ */
+#define DATEVAL_NOBEGIN		((DateADT) (-0x7fffffff - 1))
+#define DATEVAL_NOEND		((DateADT) 0x7fffffff)
+
+#define DATE_NOBEGIN(j)		((j) = DATEVAL_NOBEGIN)
+#define DATE_IS_NOBEGIN(j)	((j) == DATEVAL_NOBEGIN)
+#define DATE_NOEND(j)		((j) = DATEVAL_NOEND)
+#define DATE_IS_NOEND(j)	((j) == DATEVAL_NOEND)
+#define DATE_NOT_FINITE(j)	(DATE_IS_NOBEGIN(j) || DATE_IS_NOEND(j))
+
 /*
  * Macros for fmgr-callable functions.
  *
@@ -90,6 +104,7 @@ extern Datum date_le(PG_FUNCTION_ARGS);
 extern Datum date_gt(PG_FUNCTION_ARGS);
 extern Datum date_ge(PG_FUNCTION_ARGS);
 extern Datum date_cmp(PG_FUNCTION_ARGS);
+extern Datum date_finite(PG_FUNCTION_ARGS);
 extern Datum date_larger(PG_FUNCTION_ARGS);
 extern Datum date_smaller(PG_FUNCTION_ARGS);
 extern Datum date_mi(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index a4acf3e9c9..b603745077 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1157,3 +1157,30 @@ SELECT DATE_TRUNC('DECADE', DATE '0002-12-31 BC'); -- 0011-01-01 BC
  Mon Jan 01 00:00:00 0011 PST BC
 (1 row)
 
+--
+-- test infinity
+--
+select 'infinity'::date, '-infinity'::date;
+   date   |   date    
+----------+-----------
+ infinity | -infinity
+(1 row)
+
+select 'infinity'::date > 'today'::date as t;
+ t 
+---
+ t
+(1 row)
+
+select '-infinity'::date < 'today'::date as t;
+ t 
+---
+ t
+(1 row)
+
+select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'::date);
+ isfinite | isfinite | isfinite 
+----------+----------+----------
+ f        | f        | t
+(1 row)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 97ddbe9e45..d179ddf09b 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -269,3 +269,10 @@ SELECT DATE_TRUNC('CENTURY', DATE '0055-08-10 BC'); -- 0100-01-01 BC
 SELECT DATE_TRUNC('DECADE', DATE '1993-12-25'); -- 1990-01-01
 SELECT DATE_TRUNC('DECADE', DATE '0004-12-25'); -- 0001-01-01 BC
 SELECT DATE_TRUNC('DECADE', DATE '0002-12-31 BC'); -- 0011-01-01 BC
+--
+-- test infinity
+--
+select 'infinity'::date, '-infinity'::date;
+select 'infinity'::date > 'today'::date as t;
+select '-infinity'::date < 'today'::date as t;
+select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'::date);