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 */