diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 9e291b5..fa50d79 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -550,13 +550,15 @@ date_mii(PG_FUNCTION_ARGS)
 	PG_RETURN_DATEADT(result);
 }
 
+
 /*
- * Internal routines for promoting date to timestamp and timestamp with
- * time zone
+ * Promote date to timestamp.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and zero is returned.
  */
-
-static Timestamp
-date2timestamp(DateADT dateVal)
+Timestamp
+date2timestamp_opt_error(DateADT dateVal, bool *have_error)
 {
 	Timestamp	result;
 
@@ -572,9 +574,19 @@ date2timestamp(DateADT dateVal)
 		 * boundary need be checked for overflow.
 		 */
 		if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-					 errmsg("date out of range for timestamp")));
+		{
+			if (have_error)
+			{
+				*have_error = true;
+				return (Timestamp) 0;
+			}
+			else
+			{
+				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;
@@ -583,8 +595,23 @@ date2timestamp(DateADT dateVal)
 	return result;
 }
 
+/*
+ * Single-argument version of date2timestamp_opt_error().
+ */
 static TimestampTz
-date2timestamptz(DateADT dateVal)
+date2timestamp(DateADT dateVal)
+{
+	return date2timestamp_opt_error(dateVal, NULL);
+}
+
+/*
+ * Promote date to timestamp with time zone.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and zero is returned.
+ */
+TimestampTz
+date2timestamptz_opt_error(DateADT dateVal, bool *have_error)
 {
 	TimestampTz result;
 	struct pg_tm tt,
@@ -603,9 +630,19 @@ date2timestamptz(DateADT dateVal)
 		 * boundary need be checked for overflow.
 		 */
 		if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-					 errmsg("date out of range for timestamp")));
+		{
+			if (have_error)
+			{
+				*have_error = true;
+				return (TimestampTz) 0;
+			}
+			else
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+						 errmsg("date out of range for timestamp")));
+			}
+		}
 
 		j2date(dateVal + POSTGRES_EPOCH_JDATE,
 			   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
@@ -621,15 +658,34 @@ date2timestamptz(DateADT dateVal)
 		 * of time zone, check for allowed timestamp range after adding tz.
 		 */
 		if (!IS_VALID_TIMESTAMP(result))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-					 errmsg("date out of range for timestamp")));
+		{
+			if (have_error)
+			{
+				*have_error = true;
+				return (TimestampTz) 0;
+			}
+			else
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+						 errmsg("date out of range for timestamp")));
+			}
+		}
 	}
 
 	return result;
 }
 
 /*
+ * Single-argument version of date2timestamptz_opt_error().
+ */
+static TimestampTz
+date2timestamptz(DateADT dateVal)
+{
+	return date2timestamptz_opt_error(dateVal, NULL);
+}
+
+/*
  * date2timestamp_no_overflow
  *
  * This is chartered to produce a double value that is numerically
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 9031432..a3b15b5 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -99,6 +99,43 @@
 #include "utils/pg_locale.h"
 
 /* ----------
+ * Convenience macros for error handling
+ * ----------
+ *
+ * Two macros below help to handle errors in functions that take
+ * 'bool *have_error' argument.  When this argument is not NULL, it's expected
+ * that function will suppress ereports when possible.  Instead it should
+ * return some default value and set *have_error flag.
+ *
+ * RETURN_ERROR() macro intended to wrap ereport() calls.  When have_error
+ * function argument is not NULL, then instead of ereport'ing we set
+ * *have_error flag and go to on_error label.  It's supposed that jump
+ * resources will be freed and some 'default' value returned.
+ *
+ * CHECK_ERROR() jumps on_error label when *have_error flag is defined and set.
+ * It's supposed to be used for immediate exit from the function on error
+ * after call of another function with 'bool *have_error' argument.
+ */
+#define RETURN_ERROR(throw_error) \
+do { \
+	if (have_error) \
+	{ \
+		*have_error = true; \
+		goto on_error; \
+	} \
+	else \
+	{ \
+		throw_error; \
+	} \
+} while (0)
+
+#define CHECK_ERROR \
+do { \
+	if (have_error && *have_error) \
+		goto on_error; \
+} while (0)
+
+/* ----------
  * Routines type
  * ----------
  */
@@ -1008,7 +1045,7 @@ static void parse_format(FormatNode *node, const char *str, const KeyWord *kw,
 static void DCH_to_char(FormatNode *node, bool is_interval,
 						TmToChar *in, char *out, Oid collid);
 static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out,
-						  bool strict);
+						  bool strict, bool *have_error);
 
 #ifdef DEBUG_TO_FROM_CHAR
 static void dump_index(const KeyWord *k, const int *index);
@@ -1019,14 +1056,21 @@ static const char *get_th(char *num, int type);
 static char *str_numth(char *dest, char *num, int type);
 static int	adjust_partial_year_to_2020(int year);
 static int	strspace_len(char *str);
-static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
-static void from_char_set_int(int *dest, const int value, const FormatNode *node);
-static int	from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
-static int	from_char_parse_int(int *dest, char **src, FormatNode *node);
+static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
+							   bool *have_error);
+static void from_char_set_int(int *dest, const int value, const FormatNode *node,
+							  bool *have_error);
+static int	from_char_parse_int_len(int *dest, char **src, const int len,
+									FormatNode *node, bool *have_error);
+static int	from_char_parse_int(int *dest, char **src, FormatNode *node,
+								bool *have_error);
 static int	seq_search(char *name, const char *const *array, int type, int max, int *len);
-static int	from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max, FormatNode *node);
+static int	from_char_seq_search(int *dest, char **src,
+								 const char *const *array, int type, int max,
+								 FormatNode *node, bool *have_error);
 static void do_to_timestamp(text *date_txt, text *fmt, bool strict,
-							struct pg_tm *tm, fsec_t *fsec, int *fprec, int *flags);
+							struct pg_tm *tm, fsec_t *fsec, int *fprec,
+							int *flags, bool *have_error);
 static char *fill_str(char *str, int c, int max);
 static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree);
 static char *int_to_roman(int number);
@@ -2202,21 +2246,26 @@ strspace_len(char *str)
  *
  * Puke if the date mode has already been set, and the caller attempts to set
  * it to a conflicting mode.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
  */
 static void
-from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
+from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode, bool *have_error)
 {
 	if (mode != FROM_CHAR_DATE_NONE)
 	{
 		if (tmfc->mode == FROM_CHAR_DATE_NONE)
 			tmfc->mode = mode;
 		else if (tmfc->mode != mode)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("invalid combination of date conventions"),
-					 errhint("Do not mix Gregorian and ISO week date "
-							 "conventions in a formatting template.")));
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("invalid combination of date conventions"),
+								  errhint("Do not mix Gregorian and ISO week date "
+										  "conventions in a formatting template."))));
 	}
+
+on_error:
+	return;
 }
 
 /*
@@ -2224,18 +2273,25 @@ from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
  *
  * Puke if the destination integer has previously been set to some other
  * non-zero value.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
  */
 static void
-from_char_set_int(int *dest, const int value, const FormatNode *node)
+from_char_set_int(int *dest, const int value, const FormatNode *node,
+				  bool *have_error)
 {
 	if (*dest != 0 && *dest != value)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-				 errmsg("conflicting values for \"%s\" field in formatting string",
-						node->key->name),
-				 errdetail("This value contradicts a previous setting for "
-						   "the same field type.")));
+		RETURN_ERROR(ereport(ERROR,
+							 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+							  errmsg("conflicting values for \"%s\" field in "
+									 "formatting string",
+									 node->key->name),
+							  errdetail("This value contradicts a previous setting "
+										"for the same field type."))));
 	*dest = value;
+
+on_error:
+	return;
 }
 
 /*
@@ -2257,9 +2313,13 @@ from_char_set_int(int *dest, const int value, const FormatNode *node)
  * Note that from_char_parse_int() provides a more convenient wrapper where
  * the length of the field is the same as the length of the format keyword (as
  * with DD and MI).
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and -1 is returned.
  */
 static int
-from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
+from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node,
+						bool *have_error)
 {
 	long		result;
 	char		copy[DCH_MAX_ITEM_SIZ + 1];
@@ -2292,51 +2352,60 @@ from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
 		char	   *last;
 
 		if (used < len)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("source string too short for \"%s\" formatting field",
-							node->key->name),
-					 errdetail("Field requires %d characters, but only %d "
-							   "remain.",
-							   len, used),
-					 errhint("If your source string is not fixed-width, try "
-							 "using the \"FM\" modifier.")));
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("source string too short for \"%s\" "
+										 "formatting field",
+										 node->key->name),
+								  errdetail("Field requires %d characters, "
+											"but only %d remain.",
+											len, used),
+								  errhint("If your source string is not fixed-width, "
+										  "try using the \"FM\" modifier."))));
 
 		errno = 0;
 		result = strtol(copy, &last, 10);
 		used = last - copy;
 
 		if (used > 0 && used < len)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("invalid value \"%s\" for \"%s\"",
-							copy, node->key->name),
-					 errdetail("Field requires %d characters, but only %d "
-							   "could be parsed.", len, used),
-					 errhint("If your source string is not fixed-width, try "
-							 "using the \"FM\" modifier.")));
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("invalid value \"%s\" for \"%s\"",
+										 copy, node->key->name),
+								  errdetail("Field requires %d characters, "
+											"but only %d could be parsed.",
+											len, used),
+								  errhint("If your source string is not fixed-width, "
+										  "try using the \"FM\" modifier."))));
 
 		*src += used;
 	}
 
 	if (*src == init)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-				 errmsg("invalid value \"%s\" for \"%s\"",
-						copy, node->key->name),
-				 errdetail("Value must be an integer.")));
+		RETURN_ERROR(ereport(ERROR,
+							 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+							  errmsg("invalid value \"%s\" for \"%s\"",
+									 copy, node->key->name),
+							  errdetail("Value must be an integer."))));
 
 	if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-				 errmsg("value for \"%s\" in source string is out of range",
-						node->key->name),
-				 errdetail("Value must be in the range %d to %d.",
-						   INT_MIN, INT_MAX)));
+		RETURN_ERROR(ereport(ERROR,
+							 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+							  errmsg("value for \"%s\" in source string is out of range",
+									 node->key->name),
+							  errdetail("Value must be in the range %d to %d.",
+										INT_MIN, INT_MAX))));
 
 	if (dest != NULL)
-		from_char_set_int(dest, (int) result, node);
+	{
+		from_char_set_int(dest, (int) result, node, have_error);
+		CHECK_ERROR;
+	}
+
 	return *src - init;
+
+on_error:
+	return -1;
 }
 
 /*
@@ -2349,9 +2418,9 @@ from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
  * required length explicitly.
  */
 static int
-from_char_parse_int(int *dest, char **src, FormatNode *node)
+from_char_parse_int(int *dest, char **src, FormatNode *node, bool *have_error)
 {
-	return from_char_parse_int_len(dest, src, node->key->len, node);
+	return from_char_parse_int_len(dest, src, node->key->len, node, have_error);
 }
 
 /* ----------
@@ -2434,11 +2503,12 @@ seq_search(char *name, const char *const *array, int type, int max, int *len)
  * pointed to by 'dest', advance 'src' to the end of the part of the string
  * which matched, and return the number of characters consumed.
  *
- * If the string doesn't match, throw an error.
+ * If the string doesn't match, throw an error if 'have_error' is NULL,
+ * otherwise set '*have_error' and return -1.
  */
 static int
-from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max,
-					 FormatNode *node)
+from_char_seq_search(int *dest, char **src, const char *const *array, int type,
+					 int max, FormatNode *node, bool *have_error)
 {
 	int			len;
 
@@ -2450,15 +2520,18 @@ from_char_seq_search(int *dest, char **src, const char *const *array, int type,
 		Assert(max <= DCH_MAX_ITEM_SIZ);
 		strlcpy(copy, *src, max + 1);
 
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-				 errmsg("invalid value \"%s\" for \"%s\"",
-						copy, node->key->name),
-				 errdetail("The given value did not match any of the allowed "
-						   "values for this field.")));
+		RETURN_ERROR(ereport(ERROR,
+							 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+							  errmsg("invalid value \"%s\" for \"%s\"",
+									 copy, node->key->name),
+							  errdetail("The given value did not match any of "
+										"the allowed values for this field."))));
 	}
 	*src += len;
 	return len;
+
+on_error:
+	return -1;
 }
 
 /* ----------
@@ -3060,10 +3133,13 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
  *
  * Note: we currently don't have any to_interval() function, so there
  * is no need here for INVALID_FOR_INTERVAL checks.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
  * ----------
  */
 static void
-DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
+DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict,
+			  bool *have_error)
 {
 	FormatNode *n;
 	char	   *s;
@@ -3146,7 +3222,8 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 			continue;
 		}
 
-		from_char_set_mode(out, n->key->date_mode);
+		from_char_set_mode(out, n->key->date_mode, have_error);
+		CHECK_ERROR;
 
 		switch (n->key->id)
 		{
@@ -3158,8 +3235,10 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 			case DCH_a_m:
 			case DCH_p_m:
 				from_char_seq_search(&value, &s, ampm_strings_long,
-									 ALL_UPPER, n->key->len, n);
-				from_char_set_int(&out->pm, value % 2, n);
+									 ALL_UPPER, n->key->len, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->pm, value % 2, n, have_error);
+				CHECK_ERROR;
 				out->clock = CLOCK_12_HOUR;
 				break;
 			case DCH_AM:
@@ -3167,30 +3246,37 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 			case DCH_am:
 			case DCH_pm:
 				from_char_seq_search(&value, &s, ampm_strings,
-									 ALL_UPPER, n->key->len, n);
-				from_char_set_int(&out->pm, value % 2, n);
+									 ALL_UPPER, n->key->len, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->pm, value % 2, n, have_error);
+				CHECK_ERROR;
 				out->clock = CLOCK_12_HOUR;
 				break;
 			case DCH_HH:
 			case DCH_HH12:
-				from_char_parse_int_len(&out->hh, &s, 2, n);
+				from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
+				CHECK_ERROR;
 				out->clock = CLOCK_12_HOUR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_HH24:
-				from_char_parse_int_len(&out->hh, &s, 2, n);
+				from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_MI:
-				from_char_parse_int(&out->mi, &s, n);
+				from_char_parse_int(&out->mi, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_SS:
-				from_char_parse_int(&out->ss, &s, n);
+				from_char_parse_int(&out->ss, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_MS:		/* millisecond */
-				len = from_char_parse_int_len(&out->ms, &s, 3, n);
+				len = from_char_parse_int_len(&out->ms, &s, 3, n, have_error);
+				CHECK_ERROR;
 
 				/*
 				 * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
@@ -3211,7 +3297,8 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 			case DCH_US:		/* microsecond */
 				len = from_char_parse_int_len(&out->us, &s,
 											  n->key->id == DCH_US ? 6 :
-											  out->ff, n);
+											  out->ff, n, have_error);
+				CHECK_ERROR;
 
 				out->us *= len == 1 ? 100000 :
 					len == 2 ? 10000 :
@@ -3222,16 +3309,18 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_SSSS:
-				from_char_parse_int(&out->ssss, &s, n);
+				from_char_parse_int(&out->ssss, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_tz:
 			case DCH_TZ:
 			case DCH_OF:
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("formatting field \"%s\" is only supported in to_char",
-								n->key->name)));
+				RETURN_ERROR(ereport(ERROR,
+									 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+									  errmsg("formatting field \"%s\" is only supported in to_char",
+											 n->key->name))));
+				CHECK_ERROR;
 				break;
 			case DCH_TZH:
 
@@ -3255,82 +3344,102 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 						out->tzsign = +1;
 				}
 
-				from_char_parse_int_len(&out->tzh, &s, 2, n);
+				from_char_parse_int_len(&out->tzh, &s, 2, n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_TZM:
 				/* assign positive timezone sign if TZH was not seen before */
 				if (!out->tzsign)
 					out->tzsign = +1;
-				from_char_parse_int_len(&out->tzm, &s, 2, n);
+				from_char_parse_int_len(&out->tzm, &s, 2, n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_A_D:
 			case DCH_B_C:
 			case DCH_a_d:
 			case DCH_b_c:
 				from_char_seq_search(&value, &s, adbc_strings_long,
-									 ALL_UPPER, n->key->len, n);
-				from_char_set_int(&out->bc, value % 2, n);
+									 ALL_UPPER, n->key->len, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->bc, value % 2, n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_AD:
 			case DCH_BC:
 			case DCH_ad:
 			case DCH_bc:
 				from_char_seq_search(&value, &s, adbc_strings,
-									 ALL_UPPER, n->key->len, n);
-				from_char_set_int(&out->bc, value % 2, n);
+									 ALL_UPPER, n->key->len, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->bc, value % 2, n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_MONTH:
 			case DCH_Month:
 			case DCH_month:
 				from_char_seq_search(&value, &s, months_full, ONE_UPPER,
-									 MAX_MONTH_LEN, n);
-				from_char_set_int(&out->mm, value + 1, n);
+									 MAX_MONTH_LEN, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->mm, value + 1, n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_MON:
 			case DCH_Mon:
 			case DCH_mon:
 				from_char_seq_search(&value, &s, months, ONE_UPPER,
-									 MAX_MON_LEN, n);
-				from_char_set_int(&out->mm, value + 1, n);
+									 MAX_MON_LEN, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->mm, value + 1, n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_MM:
-				from_char_parse_int(&out->mm, &s, n);
+				from_char_parse_int(&out->mm, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_DAY:
 			case DCH_Day:
 			case DCH_day:
 				from_char_seq_search(&value, &s, days, ONE_UPPER,
-									 MAX_DAY_LEN, n);
-				from_char_set_int(&out->d, value, n);
+									 MAX_DAY_LEN, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->d, value, n, have_error);
+				CHECK_ERROR;
 				out->d++;
 				break;
 			case DCH_DY:
 			case DCH_Dy:
 			case DCH_dy:
 				from_char_seq_search(&value, &s, days, ONE_UPPER,
-									 MAX_DY_LEN, n);
-				from_char_set_int(&out->d, value, n);
+									 MAX_DY_LEN, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->d, value, n, have_error);
+				CHECK_ERROR;
 				out->d++;
 				break;
 			case DCH_DDD:
-				from_char_parse_int(&out->ddd, &s, n);
+				from_char_parse_int(&out->ddd, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_IDDD:
-				from_char_parse_int_len(&out->ddd, &s, 3, n);
+				from_char_parse_int_len(&out->ddd, &s, 3, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_DD:
-				from_char_parse_int(&out->dd, &s, n);
+				from_char_parse_int(&out->dd, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_D:
-				from_char_parse_int(&out->d, &s, n);
+				from_char_parse_int(&out->d, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_ID:
-				from_char_parse_int_len(&out->d, &s, 1, n);
+				from_char_parse_int_len(&out->d, &s, 1, n, have_error);
+				CHECK_ERROR;
 				/* Shift numbering to match Gregorian where Sunday = 1 */
 				if (++out->d > 7)
 					out->d = 1;
@@ -3338,7 +3447,8 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 				break;
 			case DCH_WW:
 			case DCH_IW:
-				from_char_parse_int(&out->ww, &s, n);
+				from_char_parse_int(&out->ww, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_Q:
@@ -3353,11 +3463,13 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 				 * We still parse the source string for an integer, but it
 				 * isn't stored anywhere in 'out'.
 				 */
-				from_char_parse_int((int *) NULL, &s, n);
+				from_char_parse_int((int *) NULL, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_CC:
-				from_char_parse_int(&out->cc, &s, n);
+				from_char_parse_int(&out->cc, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_Y_YYY:
@@ -3369,11 +3481,12 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 
 					matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch);
 					if (matched < 2)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-								 errmsg("invalid input string for \"Y,YYY\"")));
+						RETURN_ERROR(ereport(ERROR,
+											 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+											  errmsg("invalid input string for \"Y,YYY\""))));
 					years += (millennia * 1000);
-					from_char_set_int(&out->year, years, n);
+					from_char_set_int(&out->year, years, n, have_error);
+					CHECK_ERROR;
 					out->yysz = 4;
 					s += nch;
 					SKIP_THth(s, n->suffix);
@@ -3381,47 +3494,62 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 				break;
 			case DCH_YYYY:
 			case DCH_IYYY:
-				from_char_parse_int(&out->year, &s, n);
+				from_char_parse_int(&out->year, &s, n, have_error);
+				CHECK_ERROR;
 				out->yysz = 4;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_YYY:
 			case DCH_IYY:
-				if (from_char_parse_int(&out->year, &s, n) < 4)
+				len = from_char_parse_int(&out->year, &s, n, have_error);
+				CHECK_ERROR;
+				if (len < 4)
 					out->year = adjust_partial_year_to_2020(out->year);
 				out->yysz = 3;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_YY:
 			case DCH_IY:
-				if (from_char_parse_int(&out->year, &s, n) < 4)
+				len = from_char_parse_int(&out->year, &s, n, have_error);
+				CHECK_ERROR;
+				if (len < 4)
 					out->year = adjust_partial_year_to_2020(out->year);
 				out->yysz = 2;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_Y:
 			case DCH_I:
-				if (from_char_parse_int(&out->year, &s, n) < 4)
+				len = from_char_parse_int(&out->year, &s, n, have_error);
+				CHECK_ERROR;
+				if (len < 4)
 					out->year = adjust_partial_year_to_2020(out->year);
 				out->yysz = 1;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_RM:
 				from_char_seq_search(&value, &s, rm_months_upper,
-									 ALL_UPPER, MAX_RM_LEN, n);
-				from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
+									 ALL_UPPER, MAX_RM_LEN, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
+								  n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_rm:
 				from_char_seq_search(&value, &s, rm_months_lower,
-									 ALL_LOWER, MAX_RM_LEN, n);
-				from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
+									 ALL_LOWER, MAX_RM_LEN, n, have_error);
+				CHECK_ERROR;
+				from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
+								  n, have_error);
+				CHECK_ERROR;
 				break;
 			case DCH_W:
-				from_char_parse_int(&out->w, &s, n);
+				from_char_parse_int(&out->w, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 			case DCH_J:
-				from_char_parse_int(&out->j, &s, n);
+				from_char_parse_int(&out->j, &s, n, have_error);
+				CHECK_ERROR;
 				SKIP_THth(s, n->suffix);
 				break;
 		}
@@ -3441,19 +3569,22 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool strict)
 	if (strict)
 	{
 		if (n->type != NODE_TYPE_END)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("input string is too short for datetime format")));
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("input string is too short for datetime format"))));
 
 		while (*s != '\0' && isspace((unsigned char) *s))
 			s++;
 
 		if (*s != '\0')
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("trailing characters remain in input string after "
-							"datetime format")));
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("trailing characters remain in input string "
+										 "after datetime format"))));
 	}
+
+on_error:
+	return;
 }
 
 /*
@@ -3474,9 +3605,13 @@ DCH_prevent_counter_overflow(void)
 	}
 }
 
-/* Get mask of date/time/zone components present in format nodes. */
+/*
+ * Get mask of date/time/zone components present in format nodes.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
+ */
 static int
-DCH_datetime_type(FormatNode *node)
+DCH_datetime_type(FormatNode *node, bool *have_error)
 {
 	FormatNode *n;
 	int			flags = 0;
@@ -3517,10 +3652,10 @@ DCH_datetime_type(FormatNode *node)
 			case DCH_tz:
 			case DCH_TZ:
 			case DCH_OF:
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("formatting field \"%s\" is only supported in to_char",
-								n->key->name)));
+				RETURN_ERROR(ereport(ERROR,
+									 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+									  errmsg("formatting field \"%s\" is only supported in to_char",
+											 n->key->name))));
 				flags |= DCH_ZONED;
 				break;
 			case DCH_TZH:
@@ -3574,6 +3709,7 @@ DCH_datetime_type(FormatNode *node)
 		}
 	}
 
+on_error:
 	return flags;
 }
 
@@ -3867,7 +4003,7 @@ to_timestamp(PG_FUNCTION_ARGS)
 	fsec_t		fsec;
 	int			fprec;
 
-	do_to_timestamp(date_txt, fmt, false, &tm, &fsec, &fprec, NULL);
+	do_to_timestamp(date_txt, fmt, false, &tm, &fsec, &fprec, NULL, NULL);
 
 	/* Use the specified time zone, if any. */
 	if (tm.tm_zone)
@@ -3906,7 +4042,7 @@ to_date(PG_FUNCTION_ARGS)
 	struct pg_tm tm;
 	fsec_t		fsec;
 
-	do_to_timestamp(date_txt, fmt, false, &tm, &fsec, NULL, NULL);
+	do_to_timestamp(date_txt, fmt, false, &tm, &fsec, NULL, NULL, NULL);
 
 	/* Prevent overflow in Julian-day routines */
 	if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
@@ -3933,17 +4069,21 @@ to_date(PG_FUNCTION_ARGS)
  * the presence of date/time/zone components in the format string.
  *
  * When timezone component is present, the corresponding offset is set to '*tz'.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and zero value is returned.
  */
 Datum
 parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
-			   int32 *typmod, int *tz)
+			   int32 *typmod, int *tz, bool *have_error)
 {
 	struct pg_tm tm;
 	fsec_t		fsec;
 	int			fprec = 0;
 	int			flags;
 
-	do_to_timestamp(date_txt, fmt, strict, &tm, &fsec, &fprec, &flags);
+	do_to_timestamp(date_txt, fmt, strict, &tm, &fsec, &fprec, &flags, have_error);
+	CHECK_ERROR;
 
 	*typmod = fprec ? fprec : -1;	/* fractional part precision */
 
@@ -3964,16 +4104,22 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 				}
 				else
 				{
-					if (*tz == PG_INT32_MIN)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-								 errmsg("missing time zone in input string for type timestamptz")));
+					/*
+					 * Time zone is present in format string, but not in input
+					 * string.  Assuming do_to_timestamp() triggers no error
+					 * this should be possible only in non-strict case.
+					 */
+					Assert(!strict);
+
+					RETURN_ERROR(ereport(ERROR,
+										 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+										  errmsg("missing time zone in input string for type timestamptz"))));
 				}
 
 				if (tm2timestamp(&tm, fsec, tz, &result) != 0)
-					ereport(ERROR,
-							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-							 errmsg("timestamptz out of range")));
+					RETURN_ERROR(ereport(ERROR,
+										 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+										  errmsg("timestamptz out of range"))));
 
 				AdjustTimestampForTypmod(&result, *typmod);
 
@@ -3985,9 +4131,9 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 				Timestamp	result;
 
 				if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
-					ereport(ERROR,
-							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-							 errmsg("timestamp out of range")));
+					RETURN_ERROR(ereport(ERROR,
+										 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+										  errmsg("timestamp out of range"))));
 
 				AdjustTimestampForTypmod(&result, *typmod);
 
@@ -3999,9 +4145,9 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 		{
 			if (flags & DCH_ZONED)
 			{
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-						 errmsg("datetime format is zoned but not timed")));
+				RETURN_ERROR(ereport(ERROR,
+									 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+									  errmsg("datetime format is zoned but not timed"))));
 			}
 			else
 			{
@@ -4009,20 +4155,20 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 
 				/* Prevent overflow in Julian-day routines */
 				if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
-					ereport(ERROR,
-							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-							 errmsg("date out of range: \"%s\"",
-									text_to_cstring(date_txt))));
+					RETURN_ERROR(ereport(ERROR,
+										 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+										  errmsg("date out of range: \"%s\"",
+												 text_to_cstring(date_txt)))));
 
 				result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) -
 					POSTGRES_EPOCH_JDATE;
 
 				/* Now check for just-out-of-range dates */
 				if (!IS_VALID_DATE(result))
-					ereport(ERROR,
-							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-							 errmsg("date out of range: \"%s\"",
-									text_to_cstring(date_txt))));
+					RETURN_ERROR(ereport(ERROR,
+										 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+										  errmsg("date out of range: \"%s\"",
+												 text_to_cstring(date_txt)))));
 
 				*typid = DATEOID;
 				return DateADTGetDatum(result);
@@ -4040,20 +4186,26 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 				int			dterr = DecodeTimezone(unconstify(char *, tm.tm_zone), tz);
 
 				if (dterr)
-					DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz");
+					RETURN_ERROR(DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz"));
 			}
 			else
 			{
-				if (*tz == PG_INT32_MIN)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-							 errmsg("missing time zone in input string for type timetz")));
+				/*
+				 * Time zone is present in format string, but not in input
+				 * string.  Assuming do_to_timestamp() triggers no error this
+				 * should be possible only in non-strict case.
+				 */
+				Assert(!strict);
+
+				RETURN_ERROR(ereport(ERROR,
+									 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+									  errmsg("missing time zone in input string for type timetz"))));
 			}
 
 			if (tm2timetz(&tm, fsec, *tz, result) != 0)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-						 errmsg("timetz out of range")));
+				RETURN_ERROR(ereport(ERROR,
+									 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+									  errmsg("timetz out of range"))));
 
 			AdjustTimeForTypmod(&result->time, *typmod);
 
@@ -4065,9 +4217,9 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 			TimeADT		result;
 
 			if (tm2time(&tm, fsec, &result) != 0)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-						 errmsg("time out of range")));
+				RETURN_ERROR(ereport(ERROR,
+									 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+									  errmsg("time out of range"))));
 
 			AdjustTimeForTypmod(&result, *typmod);
 
@@ -4077,11 +4229,12 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
 	}
 	else
 	{
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-				 errmsg("datetime format is not dated and not timed")));
+		RETURN_ERROR(ereport(ERROR,
+							 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+							  errmsg("datetime format is not dated and not timed"))));
 	}
 
+on_error:
 	return (Datum) 0;
 }
 
@@ -4102,16 +4255,19 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
  *
  * 'strict' enables error reporting on unmatched trailing characters in input
  * or format string patterns.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
  */
 static void
-do_to_timestamp(text *date_txt, text *fmt, bool strict,
-				struct pg_tm *tm, fsec_t *fsec, int *fprec, int *flags)
+do_to_timestamp(text *date_txt, text *fmt, bool strict, struct pg_tm *tm,
+				fsec_t *fsec, int *fprec, int *flags, bool *have_error)
 {
-	FormatNode *format;
+	FormatNode *format = NULL;
 	TmFromChar	tmfc;
 	int			fmt_len;
 	char	   *date_str;
 	int			fmask;
+	bool		incache = false;
 
 	date_str = text_to_cstring(date_txt);
 
@@ -4125,7 +4281,6 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 	if (fmt_len)
 	{
 		char	   *fmt_str;
-		bool		incache;
 
 		fmt_str = text_to_cstring(fmt);
 
@@ -4135,8 +4290,6 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 			 * Allocate new memory if format picture is bigger than static
 			 * cache and do not use cache (call parser always)
 			 */
-			incache = false;
-
 			format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
 
 			parse_format(format, fmt_str, DCH_keywords,
@@ -4158,15 +4311,21 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 		/* dump_index(DCH_keywords, DCH_index); */
 #endif
 
-		DCH_from_char(format, date_str, &tmfc, strict);
+		DCH_from_char(format, date_str, &tmfc, strict, have_error);
+		CHECK_ERROR;
 
 		pfree(fmt_str);
 
 		if (flags)
-			*flags = DCH_datetime_type(format);
+			*flags = DCH_datetime_type(format, have_error);
 
 		if (!incache)
+		{
 			pfree(format);
+			format = NULL;
+		}
+
+		CHECK_ERROR;
 	}
 
 	DEBUG_TMFC(&tmfc);
@@ -4195,11 +4354,13 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 	if (tmfc.clock == CLOCK_12_HOUR)
 	{
 		if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("hour \"%d\" is invalid for the 12-hour clock",
-							tm->tm_hour),
-					 errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));
+		{
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("hour \"%d\" is invalid for the 12-hour clock",
+										 tm->tm_hour),
+								  errhint("Use the 24-hour clock, or give an hour between 1 and 12."))));
+		}
 
 		if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
 			tm->tm_hour += HOURS_PER_DAY / 2;
@@ -4303,9 +4464,11 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 		 */
 
 		if (!tm->tm_year && !tmfc.bc)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
-					 errmsg("cannot calculate day of year without year information")));
+		{
+			RETURN_ERROR(ereport(ERROR,
+								 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+								  errmsg("cannot calculate day of year without year information"))));
+		}
 
 		if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
 		{
@@ -4362,7 +4525,7 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 			 * said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
 			 * irrelevant hint about datestyle.
 			 */
-			DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp");
+			RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp"));
 		}
 	}
 
@@ -4371,7 +4534,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 		tm->tm_min < 0 || tm->tm_min >= MINS_PER_HOUR ||
 		tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
 		*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
-		DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp");
+	{
+		RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp"));
+	}
 
 	/* Save parsed time-zone into tm->tm_zone if it was specified */
 	if (tmfc.tzsign)
@@ -4380,7 +4545,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 
 		if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
 			tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
-			DateTimeParseError(DTERR_TZDISP_OVERFLOW, date_str, "timestamp");
+		{
+			RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, date_str, "timestamp"));
+		}
 
 		tz = psprintf("%c%02d:%02d",
 					  tmfc.tzsign > 0 ? '+' : '-', tmfc.tzh, tmfc.tzm);
@@ -4390,6 +4557,11 @@ do_to_timestamp(text *date_txt, text *fmt, bool strict,
 
 	DEBUG_TM(tm);
 
+on_error:
+
+	if (format && !incache)
+		pfree(format);
+
 	pfree(date_str);
 }
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 2931bd5..84bc97d 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -329,11 +329,11 @@ timestamp_scale(PG_FUNCTION_ARGS)
 }
 
 /*
- * AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
+ * AdjustTimestampForTypmodError --- round off a timestamp to suit given typmod
  * Works for either timestamp or timestamptz.
  */
-void
-AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
+bool
+AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
 {
 	static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
 		INT64CONST(1000000),
@@ -359,10 +359,18 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
 		&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
 	{
 		if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
+		{
+			if (error)
+			{
+				*error = true;
+				return false;
+			}
+
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("timestamp(%d) precision must be between %d and %d",
 							typmod, 0, MAX_TIMESTAMP_PRECISION)));
+		}
 
 		if (*time >= INT64CONST(0))
 		{
@@ -375,8 +383,15 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
 					  * TimestampScales[typmod]);
 		}
 	}
+
+	return true;
 }
 
+void
+AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
+{
+	(void) AdjustTimestampForTypmodError(time, typmod, NULL);
+}
 
 /* timestamptz_in()
  * Convert a string to internal form.
@@ -5172,8 +5187,15 @@ timestamp_timestamptz(PG_FUNCTION_ARGS)
 	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
 }
 
-static TimestampTz
-timestamp2timestamptz(Timestamp timestamp)
+/*
+ * Convert timestamp to timestamp with time zone.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and zero is returned.
+ */
+
+TimestampTz
+timestamp2timestamptz_opt_error(Timestamp timestamp, bool *have_error)
 {
 	TimestampTz result;
 	struct pg_tm tt,
@@ -5182,23 +5204,33 @@ timestamp2timestamptz(Timestamp timestamp)
 	int			tz;
 
 	if (TIMESTAMP_NOT_FINITE(timestamp))
-		result = timestamp;
-	else
-	{
-		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-					 errmsg("timestamp out of range")));
+		return timestamp;
 
+	if (!timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL))
+	{
 		tz = DetermineTimeZoneOffset(tm, session_timezone);
 
-		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-					 errmsg("timestamp out of range")));
+		if (!tm2timestamp(tm, fsec, &tz, &result))
+			return result;
 	}
 
-	return result;
+	if (have_error)
+		*have_error = true;
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range")));
+
+	return 0;
+}
+
+/*
+ * Single-argument version of timestamp2timestamptz_opt_error().
+ */
+static TimestampTz
+timestamp2timestamptz(Timestamp timestamp)
+{
+	return timestamp2timestamptz_opt_error(timestamp, NULL);
 }
 
 /* timestamptz_timestamp()
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index bd15bfa..c29f13a 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -70,6 +70,8 @@ typedef struct
 /* date.c */
 extern int32 anytime_typmod_check(bool istz, int32 typmod);
 extern double date2timestamp_no_overflow(DateADT dateVal);
+extern Timestamp date2timestamp_opt_error(DateADT dateVal, bool *have_error);
+extern TimestampTz date2timestamptz_opt_error(DateADT dateVal, bool *have_error);
 extern void EncodeSpecialDate(DateADT dt, char *str);
 extern DateADT GetSQLCurrentDate(void);
 extern TimeTzADT *GetSQLCurrentTime(int32 typmod);
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 0cafdd2..5ebf336 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -337,5 +337,7 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
 extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
 
 extern void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
+extern bool AdjustTimestampForTypmodError(Timestamp *time, int32 typmod,
+										  bool *error);
 
 #endif							/* DATETIME_H */
diff --git a/src/include/utils/formatting.h b/src/include/utils/formatting.h
index a390302..1a2e2a9 100644
--- a/src/include/utils/formatting.h
+++ b/src/include/utils/formatting.h
@@ -29,6 +29,7 @@ extern char *asc_toupper(const char *buff, size_t nbytes);
 extern char *asc_initcap(const char *buff, size_t nbytes);
 
 extern Datum parse_datetime(text *date_txt, text *fmt, bool strict,
-							Oid *typid, int32 *typmod, int *tz);
+							Oid *typid, int32 *typmod, int *tz,
+							bool *have_error);
 
 #endif
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index ea16190..e884d44 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -97,6 +97,9 @@ extern int	timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
 /* timestamp comparison works for timestamptz also */
 #define timestamptz_cmp_internal(dt1,dt2)	timestamp_cmp_internal(dt1, dt2)
 
+extern TimestampTz timestamp2timestamptz_opt_error(Timestamp timestamp,
+												   bool *have_error);
+
 extern int	isoweek2j(int year, int week);
 extern void isoweek2date(int woy, int *year, int *mon, int *mday);
 extern void isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday);
