Prevent internal overflows in date-vs-timestamp and related comparisons.

The date-vs-timestamp, date-vs-timestamptz, and timestamp-vs-timestamptz
comparators all worked by promoting the first type to the second and
then doing a simple same-type comparison.  This works fine, except
when the conversion result is out of range, in which case we throw an
entirely avoidable error.  The sources of such failures are
(a) type date can represent dates much farther in the future than
the timestamp types can;
(b) timezone rotation might cause a just-in-range timestamp value to
become a just-out-of-range timestamptz value.

Up to now we just ignored these corner-case issues, but now we have
an actual user complaint (bug #16657 from Huss EL-Sheikh), so let's
do something about it.

It turns out that commit 52ad1e659 already built all the necessary
infrastructure to support error-free comparisons, but neglected to
actually use it in the main-line code paths.  Fix that, do a little
bit of code style review, and remove the now-duplicate logic in
jsonpath_exec.c.

Back-patch to v13 where 52ad1e659 came in.  We could take this back
further by back-patching said infrastructure, but given the small
number of complaints so far, I don't feel a great need to.

Discussion: https://postgr.es/m/16657-cde2f876d8cc7971@postgresql.org
This commit is contained in:
Tom Lane 2020-10-07 17:10:26 -04:00
parent 6c05e5b774
commit 3db322eaab
7 changed files with 260 additions and 252 deletions

View File

@ -554,15 +554,24 @@ date_mii(PG_FUNCTION_ARGS)
/* /*
* Promote date to timestamp. * Promote date to timestamp.
* *
* On overflow error is thrown if 'overflow' is NULL. Otherwise, '*overflow' * On successful conversion, *overflow is set to zero if it's not NULL.
* is set to -1 (+1) when result value exceed lower (upper) boundary and zero *
* returned. * If the date is finite but out of the valid range for timestamp, then:
* if overflow is NULL, we throw an out-of-range error.
* if overflow is not NULL, we store +1 or -1 there to indicate the sign
* of the overflow, and return the appropriate timestamp infinity.
*
* Note: *overflow = -1 is actually not possible currently, since both
* datatypes have the same lower bound, Julian day zero.
*/ */
Timestamp Timestamp
date2timestamp_opt_overflow(DateADT dateVal, int *overflow) date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
{ {
Timestamp result; Timestamp result;
if (overflow)
*overflow = 0;
if (DATE_IS_NOBEGIN(dateVal)) if (DATE_IS_NOBEGIN(dateVal))
TIMESTAMP_NOBEGIN(result); TIMESTAMP_NOBEGIN(result);
else if (DATE_IS_NOEND(dateVal)) else if (DATE_IS_NOEND(dateVal))
@ -570,7 +579,6 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
else else
{ {
/* /*
* Date's range is wider than timestamp's, so check for boundaries.
* Since dates have the same minimum values as timestamps, only upper * Since dates have the same minimum values as timestamps, only upper
* boundary need be checked for overflow. * boundary need be checked for overflow.
*/ */
@ -579,7 +587,8 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
if (overflow) if (overflow)
{ {
*overflow = 1; *overflow = 1;
return (Timestamp) 0; TIMESTAMP_NOEND(result);
return result;
} }
else else
{ {
@ -597,7 +606,7 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
} }
/* /*
* Single-argument version of date2timestamp_opt_overflow(). * Promote date to timestamp, throwing error for overflow.
*/ */
static TimestampTz static TimestampTz
date2timestamp(DateADT dateVal) date2timestamp(DateADT dateVal)
@ -608,9 +617,12 @@ date2timestamp(DateADT dateVal)
/* /*
* Promote date to timestamp with time zone. * Promote date to timestamp with time zone.
* *
* On overflow error is thrown if 'overflow' is NULL. Otherwise, '*overflow' * On successful conversion, *overflow is set to zero if it's not NULL.
* is set to -1 (+1) when result value exceed lower (upper) boundary and zero *
* returned. * If the date is finite but out of the valid range for timestamptz, then:
* if overflow is NULL, we throw an out-of-range error.
* if overflow is not NULL, we store +1 or -1 there to indicate the sign
* of the overflow, and return the appropriate timestamptz infinity.
*/ */
TimestampTz TimestampTz
date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
@ -620,6 +632,9 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
*tm = &tt; *tm = &tt;
int tz; int tz;
if (overflow)
*overflow = 0;
if (DATE_IS_NOBEGIN(dateVal)) if (DATE_IS_NOBEGIN(dateVal))
TIMESTAMP_NOBEGIN(result); TIMESTAMP_NOBEGIN(result);
else if (DATE_IS_NOEND(dateVal)) else if (DATE_IS_NOEND(dateVal))
@ -627,7 +642,6 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
else else
{ {
/* /*
* Date's range is wider than timestamp's, so check for boundaries.
* Since dates have the same minimum values as timestamps, only upper * Since dates have the same minimum values as timestamps, only upper
* boundary need be checked for overflow. * boundary need be checked for overflow.
*/ */
@ -636,7 +650,8 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
if (overflow) if (overflow)
{ {
*overflow = 1; *overflow = 1;
return (TimestampTz) 0; TIMESTAMP_NOEND(result);
return result;
} }
else else
{ {
@ -664,13 +679,15 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
if (overflow) if (overflow)
{ {
if (result < MIN_TIMESTAMP) if (result < MIN_TIMESTAMP)
{
*overflow = -1; *overflow = -1;
TIMESTAMP_NOBEGIN(result);
}
else else
{ {
Assert(result >= END_TIMESTAMP);
*overflow = 1; *overflow = 1;
TIMESTAMP_NOEND(result);
} }
return (TimestampTz) 0;
} }
else else
{ {
@ -685,7 +702,7 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
} }
/* /*
* Single-argument version of date2timestamptz_opt_overflow(). * Promote date to timestamptz, throwing error for overflow.
*/ */
static TimestampTz static TimestampTz
date2timestamptz(DateADT dateVal) date2timestamptz(DateADT dateVal)
@ -726,16 +743,30 @@ date2timestamp_no_overflow(DateADT dateVal)
* Crosstype comparison functions for dates * Crosstype comparison functions for dates
*/ */
int32
date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2)
{
Timestamp dt1;
int overflow;
dt1 = date2timestamp_opt_overflow(dateVal, &overflow);
if (overflow > 0)
{
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
Assert(overflow == 0); /* -1 case cannot occur */
return timestamp_cmp_internal(dt1, dt2);
}
Datum Datum
date_eq_timestamp(PG_FUNCTION_ARGS) date_eq_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) == 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
} }
Datum Datum
@ -743,11 +774,8 @@ date_ne_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) != 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
} }
Datum Datum
@ -755,11 +783,8 @@ date_lt_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) < 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
} }
Datum Datum
@ -767,11 +792,8 @@ date_gt_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) > 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
} }
Datum Datum
@ -779,11 +801,8 @@ date_le_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) <= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
} }
Datum Datum
@ -791,11 +810,8 @@ date_ge_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) >= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
} }
Datum Datum
@ -803,11 +819,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal); PG_RETURN_INT32(date_cmp_timestamp_internal(dateVal, dt2));
}
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); int32
date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2)
{
TimestampTz dt1;
int overflow;
dt1 = date2timestamptz_opt_overflow(dateVal, &overflow);
if (overflow > 0)
{
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
if (overflow < 0)
{
/* dt1 is less than any finite timestamp, but more than -infinity */
return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
}
return timestamptz_cmp_internal(dt1, dt2);
} }
Datum Datum
@ -815,11 +849,8 @@ date_eq_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) == 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
} }
Datum Datum
@ -827,11 +858,8 @@ date_ne_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) != 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
} }
Datum Datum
@ -839,11 +867,8 @@ date_lt_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) < 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
} }
Datum Datum
@ -851,11 +876,8 @@ date_gt_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) > 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
} }
Datum Datum
@ -863,11 +885,8 @@ date_le_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) <= 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
} }
Datum Datum
@ -875,11 +894,8 @@ date_ge_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) >= 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
} }
Datum Datum
@ -887,11 +903,8 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
{ {
DateADT dateVal = PG_GETARG_DATEADT(0); DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal); PG_RETURN_INT32(date_cmp_timestamptz_internal(dateVal, dt2));
PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
} }
Datum Datum
@ -899,11 +912,8 @@ timestamp_eq_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) == 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
} }
Datum Datum
@ -911,11 +921,8 @@ timestamp_ne_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) != 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
} }
Datum Datum
@ -923,11 +930,8 @@ timestamp_lt_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) > 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
} }
Datum Datum
@ -935,11 +939,8 @@ timestamp_gt_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) < 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
} }
Datum Datum
@ -947,11 +948,8 @@ timestamp_le_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) >= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
} }
Datum Datum
@ -959,11 +957,8 @@ timestamp_ge_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) <= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
} }
Datum Datum
@ -971,11 +966,8 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
{ {
Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal); PG_RETURN_INT32(-date_cmp_timestamp_internal(dateVal, dt1));
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
} }
Datum Datum
@ -983,11 +975,8 @@ timestamptz_eq_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) == 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
} }
Datum Datum
@ -995,11 +984,8 @@ timestamptz_ne_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) != 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
} }
Datum Datum
@ -1007,11 +993,8 @@ timestamptz_lt_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) > 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
} }
Datum Datum
@ -1019,11 +1002,8 @@ timestamptz_gt_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) < 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
} }
Datum Datum
@ -1031,11 +1011,8 @@ timestamptz_le_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) >= 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
} }
Datum Datum
@ -1043,11 +1020,8 @@ timestamptz_ge_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) <= 0);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
} }
Datum Datum
@ -1055,11 +1029,8 @@ timestamptz_cmp_date(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1); DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal); PG_RETURN_INT32(-date_cmp_timestamptz_internal(dateVal, dt1));
PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
} }
/* /*
@ -1079,6 +1050,7 @@ in_range_date_interval(PG_FUNCTION_ARGS)
Timestamp valStamp; Timestamp valStamp;
Timestamp baseStamp; Timestamp baseStamp;
/* XXX we could support out-of-range cases here, perhaps */
valStamp = date2timestamp(val); valStamp = date2timestamp(val);
baseStamp = date2timestamp(base); baseStamp = date2timestamp(base);

View File

@ -2602,93 +2602,36 @@ castTimeToTimeTz(Datum time, bool useTz)
return DirectFunctionCall1(time_timetz, time); return DirectFunctionCall1(time_timetz, time);
} }
/*---
* Compares 'ts1' and 'ts2' timestamp, assuming that ts1 might be overflowed
* during cast from another datatype.
*
* 'overflow1' specifies overflow of 'ts1' value:
* 0 - no overflow,
* -1 - exceed lower boundary,
* 1 - exceed upper boundary.
*/
static int
cmpTimestampWithOverflow(Timestamp ts1, int overflow1, Timestamp ts2)
{
/*
* All the timestamps we deal with in jsonpath are produced by
* to_datetime() method. So, they should be valid.
*/
Assert(IS_VALID_TIMESTAMP(ts2));
/*
* Timestamp, which exceed lower (upper) bound, is always lower (higher)
* than any valid timestamp except minus (plus) infinity.
*/
if (overflow1)
{
if (overflow1 < 0)
{
if (TIMESTAMP_IS_NOBEGIN(ts2))
return 1;
else
return -1;
}
if (overflow1 > 0)
{
if (TIMESTAMP_IS_NOEND(ts2))
return -1;
else
return 1;
}
}
return timestamp_cmp_internal(ts1, ts2);
}
/* /*
* Compare date to timestamptz without throwing overflow error during cast. * Compare date to timestamp.
* Note that this doesn't involve any timezone considerations.
*/ */
static int static int
cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz) cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
{ {
TimestampTz ts1; return date_cmp_timestamp_internal(date1, ts2);
int overflow = 0;
ts1 = date2timestamp_opt_overflow(date1, &overflow);
return cmpTimestampWithOverflow(ts1, overflow, ts2);
} }
/* /*
* Compare date to timestamptz without throwing overflow error during cast. * Compare date to timestamptz.
*/ */
static int static int
cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz) cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
{ {
TimestampTz tstz1;
int overflow = 0;
checkTimezoneIsUsedForCast(useTz, "date", "timestamptz"); checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
tstz1 = date2timestamptz_opt_overflow(date1, &overflow); return date_cmp_timestamptz_internal(date1, tstz2);
return cmpTimestampWithOverflow(tstz1, overflow, tstz2);
} }
/* /*
* Compare timestamp to timestamptz without throwing overflow error during cast. * Compare timestamp to timestamptz.
*/ */
static int static int
cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz) cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
{ {
TimestampTz tstz1;
int overflow = 0;
checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz"); checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
tstz1 = timestamp2timestamptz_opt_overflow(ts1, &overflow); return timestamp_cmp_timestamptz_internal(ts1, tstz2);
return cmpTimestampWithOverflow(tstz1, overflow, tstz2);
} }
/* /*

View File

@ -2156,16 +2156,34 @@ timestamp_hash_extended(PG_FUNCTION_ARGS)
* Cross-type comparison functions for timestamp vs timestamptz * Cross-type comparison functions for timestamp vs timestamptz
*/ */
int32
timestamp_cmp_timestamptz_internal(Timestamp timestampVal, TimestampTz dt2)
{
TimestampTz dt1;
int overflow;
dt1 = timestamp2timestamptz_opt_overflow(timestampVal, &overflow);
if (overflow > 0)
{
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
if (overflow < 0)
{
/* dt1 is less than any finite timestamp, but more than -infinity */
return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
}
return timestamptz_cmp_internal(dt1, dt2);
}
Datum Datum
timestamp_eq_timestamptz(PG_FUNCTION_ARGS) timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) == 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
} }
Datum Datum
@ -2173,11 +2191,8 @@ timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) != 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
} }
Datum Datum
@ -2185,11 +2200,8 @@ timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) < 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
} }
Datum Datum
@ -2197,11 +2209,8 @@ timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) > 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
} }
Datum Datum
@ -2209,11 +2218,8 @@ timestamp_le_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) <= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
} }
Datum Datum
@ -2221,11 +2227,8 @@ timestamp_ge_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) >= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
} }
Datum Datum
@ -2233,11 +2236,8 @@ timestamp_cmp_timestamptz(PG_FUNCTION_ARGS)
{ {
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal); PG_RETURN_INT32(timestamp_cmp_timestamptz_internal(timestampVal, dt2));
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
} }
Datum Datum
@ -2245,11 +2245,8 @@ timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) == 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
} }
Datum Datum
@ -2257,11 +2254,8 @@ timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) != 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
} }
Datum Datum
@ -2269,11 +2263,8 @@ timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) > 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
} }
Datum Datum
@ -2281,11 +2272,8 @@ timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) < 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
} }
Datum Datum
@ -2293,11 +2281,8 @@ timestamptz_le_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) >= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
} }
Datum Datum
@ -2305,11 +2290,8 @@ timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) <= 0);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
} }
Datum Datum
@ -2317,11 +2299,8 @@ timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
{ {
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal); PG_RETURN_INT32(-timestamp_cmp_timestamptz_internal(timestampVal, dt1));
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
} }
@ -5178,9 +5157,12 @@ timestamp_timestamptz(PG_FUNCTION_ARGS)
/* /*
* Convert timestamp to timestamp with time zone. * Convert timestamp to timestamp with time zone.
* *
* On overflow error is thrown if 'overflow' is NULL. Otherwise, '*overflow' * On successful conversion, *overflow is set to zero if it's not NULL.
* is set to -1 (+1) when result value exceed lower (upper) boundary and zero *
* returned. * If the timestamp is finite but out of the valid range for timestamptz, then:
* if overflow is NULL, we throw an out-of-range error.
* if overflow is not NULL, we store +1 or -1 there to indicate the sign
* of the overflow, and return the appropriate timestamptz infinity.
*/ */
TimestampTz TimestampTz
timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow) timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
@ -5191,10 +5173,14 @@ timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
fsec_t fsec; fsec_t fsec;
int tz; int tz;
if (overflow)
*overflow = 0;
if (TIMESTAMP_NOT_FINITE(timestamp)) if (TIMESTAMP_NOT_FINITE(timestamp))
return timestamp; return timestamp;
if (!timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL)) /* We don't expect this to fail, but check it pro forma */
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
{ {
tz = DetermineTimeZoneOffset(tm, session_timezone); tz = DetermineTimeZoneOffset(tm, session_timezone);
@ -5207,13 +5193,16 @@ timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
else if (overflow) else if (overflow)
{ {
if (result < MIN_TIMESTAMP) if (result < MIN_TIMESTAMP)
{
*overflow = -1; *overflow = -1;
TIMESTAMP_NOBEGIN(result);
}
else else
{ {
Assert(result >= END_TIMESTAMP);
*overflow = 1; *overflow = 1;
TIMESTAMP_NOEND(result);
} }
return (TimestampTz) 0; return result;
} }
} }
@ -5225,7 +5214,7 @@ timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
} }
/* /*
* Single-argument version of timestamp2timestamptz_opt_overflow(). * Promote timestamp to timestamptz, throwing error for overflow.
*/ */
static TimestampTz static TimestampTz
timestamp2timestamptz(Timestamp timestamp) timestamp2timestamptz(Timestamp timestamp)

View File

@ -72,6 +72,9 @@ extern int32 anytime_typmod_check(bool istz, int32 typmod);
extern double date2timestamp_no_overflow(DateADT dateVal); extern double date2timestamp_no_overflow(DateADT dateVal);
extern Timestamp date2timestamp_opt_overflow(DateADT dateVal, int *overflow); extern Timestamp date2timestamp_opt_overflow(DateADT dateVal, int *overflow);
extern TimestampTz date2timestamptz_opt_overflow(DateADT dateVal, int *overflow); extern TimestampTz date2timestamptz_opt_overflow(DateADT dateVal, int *overflow);
extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2);
extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2);
extern void EncodeSpecialDate(DateADT dt, char *str); extern void EncodeSpecialDate(DateADT dt, char *str);
extern DateADT GetSQLCurrentDate(void); extern DateADT GetSQLCurrentDate(void);
extern TimeTzADT *GetSQLCurrentTime(int32 typmod); extern TimeTzADT *GetSQLCurrentTime(int32 typmod);

View File

@ -99,6 +99,8 @@ extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
extern TimestampTz timestamp2timestamptz_opt_overflow(Timestamp timestamp, extern TimestampTz timestamp2timestamptz_opt_overflow(Timestamp timestamp,
int *overflow); int *overflow);
extern int32 timestamp_cmp_timestamptz_internal(Timestamp timestampVal,
TimestampTz dt2);
extern int isoweek2j(int year, int week); extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday); extern void isoweek2date(int woy, int *year, int *mon, int *mday);

View File

@ -2,6 +2,12 @@
-- HOROLOGY -- HOROLOGY
-- --
SET DateStyle = 'Postgres, MDY'; SET DateStyle = 'Postgres, MDY';
SHOW TimeZone; -- Many of these tests depend on the prevailing setting
TimeZone
----------
PST8PDT
(1 row)
-- --
-- Test various input formats -- Test various input formats
-- --
@ -2076,6 +2082,72 @@ SELECT '' AS "16", f1 AS "timestamp", date(f1) AS date
DROP TABLE TEMP_TIMESTAMP; DROP TABLE TEMP_TIMESTAMP;
-- --
-- Comparisons between datetime types, especially overflow cases
---
SELECT '2202020-10-05'::date::timestamp; -- fail
ERROR: date out of range for timestamp
SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f;
f
---
f
(1 row)
SELECT '2202020-10-05'::date::timestamptz; -- fail
ERROR: date out of range for timestamp
SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f;
f
---
f
(1 row)
-- This conversion may work depending on timezone
SELECT '4714-11-24 BC'::date::timestamptz;
timestamptz
---------------------------------
Mon Nov 24 00:00:00 4714 PST BC
(1 row)
SET TimeZone = 'UTC-2';
SELECT '4714-11-24 BC'::date::timestamptz; -- fail
ERROR: date out of range for timestamp
SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t;
t
---
t
(1 row)
SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t;
t
---
t
(1 row)
RESET TimeZone;
--
-- Formats -- Formats
-- --
SET DateStyle TO 'US,Postgres'; SET DateStyle TO 'US,Postgres';

View File

@ -3,6 +3,8 @@
-- --
SET DateStyle = 'Postgres, MDY'; SET DateStyle = 'Postgres, MDY';
SHOW TimeZone; -- Many of these tests depend on the prevailing setting
-- --
-- Test various input formats -- Test various input formats
-- --
@ -279,6 +281,31 @@ SELECT '' AS "16", f1 AS "timestamp", date(f1) AS date
DROP TABLE TEMP_TIMESTAMP; DROP TABLE TEMP_TIMESTAMP;
--
-- Comparisons between datetime types, especially overflow cases
---
SELECT '2202020-10-05'::date::timestamp; -- fail
SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t;
SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f;
SELECT '2202020-10-05'::date::timestamptz; -- fail
SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t;
SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f;
-- This conversion may work depending on timezone
SELECT '4714-11-24 BC'::date::timestamptz;
SET TimeZone = 'UTC-2';
SELECT '4714-11-24 BC'::date::timestamptz; -- fail
SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t;
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t;
SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t;
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t;
RESET TimeZone;
-- --
-- Formats -- Formats
-- --