diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 95a9998..747ef49 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -41,8 +41,6 @@
 #endif
 
 
-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);
 static int	tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result);
 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
@@ -1249,7 +1247,7 @@ tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
  * If out of this range, leave as UTC (in practice that could only happen
  * if pg_time_t is just 32 bits) - thomas 97/05/27
  */
-static int
+int
 time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
 {
 	tm->tm_hour = time / USECS_PER_HOUR;
@@ -2073,7 +2071,7 @@ timetztypmodout(PG_FUNCTION_ARGS)
 /* timetz2tm()
  * Convert TIME WITH TIME ZONE data type to POSIX time structure.
  */
-static int
+int
 timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
 {
 	TimeOffset	trem = time->time;
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 151345a..97a5b85 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1504,11 +1504,69 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 			break;
 		case JSONTYPE_DATE:
 			{
+				char		buf[MAXDATELEN + 1];
+
+				JsonEncodeDateTime(buf, val, DATEOID);
+				appendStringInfo(result, "\"%s\"", buf);
+			}
+			break;
+		case JSONTYPE_TIMESTAMP:
+			{
+				char		buf[MAXDATELEN + 1];
+
+				JsonEncodeDateTime(buf, val, TIMESTAMPOID);
+				appendStringInfo(result, "\"%s\"", buf);
+			}
+			break;
+		case JSONTYPE_TIMESTAMPTZ:
+			{
+				char		buf[MAXDATELEN + 1];
+
+				JsonEncodeDateTime(buf, val, TIMESTAMPTZOID);
+				appendStringInfo(result, "\"%s\"", buf);
+			}
+			break;
+		case JSONTYPE_JSON:
+			/* JSON and JSONB output will already be escaped */
+			outputstr = OidOutputFunctionCall(outfuncoid, val);
+			appendStringInfoString(result, outputstr);
+			pfree(outputstr);
+			break;
+		case JSONTYPE_CAST:
+			/* outfuncoid refers to a cast function, not an output function */
+			jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
+			outputstr = text_to_cstring(jsontext);
+			appendStringInfoString(result, outputstr);
+			pfree(outputstr);
+			pfree(jsontext);
+			break;
+		default:
+			outputstr = OidOutputFunctionCall(outfuncoid, val);
+			escape_json(result, outputstr);
+			pfree(outputstr);
+			break;
+	}
+}
+
+/*
+ * Encode 'value' of datetime type 'typid' into JSON string in ISO format using
+ * optionally preallocated buffer 'buf'.
+ */
+char *
+JsonEncodeDateTime(char *buf, Datum value, Oid typid)
+{
+	if (!buf)
+		buf = palloc(MAXDATELEN + 1);
+
+	switch (typid)
+	{
+		case DATEOID:
+			{
 				DateADT		date;
 				struct pg_tm tm;
-				char		buf[MAXDATELEN + 1];
 
-				date = DatumGetDateADT(val);
+				date = DatumGetDateADT(value);
+
 				/* Same as date_out(), but forcing DateStyle */
 				if (DATE_NOT_FINITE(date))
 					EncodeSpecialDate(date, buf);
@@ -1518,17 +1576,40 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 						   &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
 					EncodeDateOnly(&tm, USE_XSD_DATES, buf);
 				}
-				appendStringInfo(result, "\"%s\"", buf);
 			}
 			break;
-		case JSONTYPE_TIMESTAMP:
+		case TIMEOID:
+			{
+				TimeADT		time = DatumGetTimeADT(value);
+				struct pg_tm tt,
+						   *tm = &tt;
+				fsec_t		fsec;
+
+				/* Same as time_out(), but forcing DateStyle */
+				time2tm(time, tm, &fsec);
+				EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
+			}
+			break;
+		case TIMETZOID:
+			{
+				TimeTzADT  *time = DatumGetTimeTzADTP(value);
+				struct pg_tm tt,
+						   *tm = &tt;
+				fsec_t		fsec;
+				int			tz;
+
+				/* Same as timetz_out(), but forcing DateStyle */
+				timetz2tm(time, tm, &fsec, &tz);
+				EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
+			}
+			break;
+		case TIMESTAMPOID:
 			{
 				Timestamp	timestamp;
 				struct pg_tm tm;
 				fsec_t		fsec;
-				char		buf[MAXDATELEN + 1];
 
-				timestamp = DatumGetTimestamp(val);
+				timestamp = DatumGetTimestamp(value);
 				/* Same as timestamp_out(), but forcing DateStyle */
 				if (TIMESTAMP_NOT_FINITE(timestamp))
 					EncodeSpecialTimestamp(timestamp, buf);
@@ -1538,19 +1619,17 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 					ereport(ERROR,
 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							 errmsg("timestamp out of range")));
-				appendStringInfo(result, "\"%s\"", buf);
 			}
 			break;
-		case JSONTYPE_TIMESTAMPTZ:
+		case TIMESTAMPTZOID:
 			{
 				TimestampTz timestamp;
 				struct pg_tm tm;
 				int			tz;
 				fsec_t		fsec;
 				const char *tzn = NULL;
-				char		buf[MAXDATELEN + 1];
 
-				timestamp = DatumGetTimestampTz(val);
+				timestamp = DatumGetTimestampTz(value);
 				/* Same as timestamptz_out(), but forcing DateStyle */
 				if (TIMESTAMP_NOT_FINITE(timestamp))
 					EncodeSpecialTimestamp(timestamp, buf);
@@ -1560,29 +1639,14 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 					ereport(ERROR,
 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							 errmsg("timestamp out of range")));
-				appendStringInfo(result, "\"%s\"", buf);
 			}
 			break;
-		case JSONTYPE_JSON:
-			/* JSON and JSONB output will already be escaped */
-			outputstr = OidOutputFunctionCall(outfuncoid, val);
-			appendStringInfoString(result, outputstr);
-			pfree(outputstr);
-			break;
-		case JSONTYPE_CAST:
-			/* outfuncoid refers to a cast function, not an output function */
-			jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
-			outputstr = text_to_cstring(jsontext);
-			appendStringInfoString(result, outputstr);
-			pfree(outputstr);
-			pfree(jsontext);
-			break;
 		default:
-			outputstr = OidOutputFunctionCall(outfuncoid, val);
-			escape_json(result, outputstr);
-			pfree(outputstr);
-			break;
+			elog(ERROR, "unknown jsonb value datetime type oid %d", typid);
+			return NULL;
 	}
+
+	return buf;
 }
 
 /*
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 014e7aa..0f70180 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -786,71 +786,19 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
 				}
 				break;
 			case JSONBTYPE_DATE:
-				{
-					DateADT		date;
-					struct pg_tm tm;
-					char		buf[MAXDATELEN + 1];
-
-					date = DatumGetDateADT(val);
-					/* Same as date_out(), but forcing DateStyle */
-					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, USE_XSD_DATES, buf);
-					}
-					jb.type = jbvString;
-					jb.val.string.len = strlen(buf);
-					jb.val.string.val = pstrdup(buf);
-				}
+				jb.type = jbvString;
+				jb.val.string.val = JsonEncodeDateTime(NULL, val, DATEOID);
+				jb.val.string.len = strlen(jb.val.string.val);
 				break;
 			case JSONBTYPE_TIMESTAMP:
-				{
-					Timestamp	timestamp;
-					struct pg_tm tm;
-					fsec_t		fsec;
-					char		buf[MAXDATELEN + 1];
-
-					timestamp = DatumGetTimestamp(val);
-					/* Same as timestamp_out(), but forcing DateStyle */
-					if (TIMESTAMP_NOT_FINITE(timestamp))
-						EncodeSpecialTimestamp(timestamp, buf);
-					else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
-						EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
-					else
-						ereport(ERROR,
-								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-								 errmsg("timestamp out of range")));
-					jb.type = jbvString;
-					jb.val.string.len = strlen(buf);
-					jb.val.string.val = pstrdup(buf);
-				}
+				jb.type = jbvString;
+				jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPOID);
+				jb.val.string.len = strlen(jb.val.string.val);
 				break;
 			case JSONBTYPE_TIMESTAMPTZ:
-				{
-					TimestampTz timestamp;
-					struct pg_tm tm;
-					int			tz;
-					fsec_t		fsec;
-					const char *tzn = NULL;
-					char		buf[MAXDATELEN + 1];
-
-					timestamp = DatumGetTimestampTz(val);
-					/* Same as timestamptz_out(), but forcing DateStyle */
-					if (TIMESTAMP_NOT_FINITE(timestamp))
-						EncodeSpecialTimestamp(timestamp, buf);
-					else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
-						EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
-					else
-						ereport(ERROR,
-								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
-								 errmsg("timestamp out of range")));
-					jb.type = jbvString;
-					jb.val.string.len = strlen(buf);
-					jb.val.string.val = pstrdup(buf);
-				}
+				jb.type = jbvString;
+				jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPTZOID);
+				jb.val.string.len = strlen(jb.val.string.val);
 				break;
 			case JSONBTYPE_JSONCAST:
 			case JSONBTYPE_JSON:
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 2749592..c9eb23a 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -73,5 +73,7 @@ extern void EncodeSpecialDate(DateADT dt, char *str);
 extern DateADT GetSQLCurrentDate(void);
 extern TimeTzADT *GetSQLCurrentTime(int32 typmod);
 extern TimeADT GetSQLLocalTime(int32 typmod);
+extern int	time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
+extern int	timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
 
 #endif							/* DATE_H */
diff --git a/src/include/utils/jsonapi.h b/src/include/utils/jsonapi.h
index d6baea5..e39572e 100644
--- a/src/include/utils/jsonapi.h
+++ b/src/include/utils/jsonapi.h
@@ -147,4 +147,6 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
 extern text *transform_json_string_values(text *json, void *action_state,
 							 JsonTransformStringValuesAction transform_action);
 
+extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid);
+
 #endif							/* JSONAPI_H */
