Extend the date type to support infinity and -infinity, analogously to
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 14 Oct 2008 17:12:33 +0000 (17:12 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 14 Oct 2008 17:12:33 +0000 (17:12 +0000)
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
doc/src/sgml/func.sgml
src/backend/utils/adt/date.c
src/backend/utils/adt/xml.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/date.h
src/test/regress/expected/date.out
src/test/regress/sql/date.sql

index d05c93058a0636ebbba50bdeac19944bb73c65eb..26521661b7f205db48eac5503c0d1bab69eac315 100644 (file)
@@ -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>
index e289326f2fb4b4f3e7395785f72534c1e2b0fef9..06ee6a0572fd42e6fa4a0ca92136429e3462c41b 100644 (file)
@@ -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>
index 6f10b666ca06322f8ceb2df05e5b6b12d47cf9f3..ffeb7541093d0cd82238d99578846a9d6cf03a32 100644 (file)
@@ -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 (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
-       ereport(ERROR,
-               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-                errmsg("timestamp out of range")));
+   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")));
 
-   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 (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
-       ereport(ERROR,
-               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-                errmsg("timestamp out of range")));
+   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")));
 
-   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);
 }
index 4ef1f97ea781e3c32dc6bdba89d7809cd5419da4..e728e1254f51710dbdce5783eb437826822d452b 100644 (file)
@@ -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);
index 0ab03e0578e12f424e37ba737f33f4a11c724be4..cd9d06f65d7ab4f7335f1bcc1a38211dee7dd17e 100644 (file)
@@ -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
index 70dc0a319a315e2017ba771e79976cbfe1d8bb50..9867f44f27357b3042732e86c62b472455a5a111 100644 (file)
@@ -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_ ));
index 5f235498363f8561a1bb7ca583685db6d2615149..9015c3db69baf1460619e404149630cde7cadf53 100644 (file)
@@ -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);
index a4acf3e9c9a4faede230345f4ca2942a3e08ca7f..b603745077ce3e30e35480eb9114e7c7e1db1e2c 100644 (file)
@@ -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)
+
index 97ddbe9e45aca8f398a08895cb14f624107cafa4..d179ddf09b3c41adb2c20955f5a87ac7b37e40a4 100644 (file)
@@ -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);