diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 95a999857c7..747ef497897 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 151345ab2ff..97a5b85516f 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -1504,62 +1504,25 @@ datum_to_json(Datum val, bool is_null, StringInfo result, break; case JSONTYPE_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); - } + JsonEncodeDateTime(buf, val, DATEOID); appendStringInfo(result, "\"%s\"", buf); } break; case JSONTYPE_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"))); + JsonEncodeDateTime(buf, val, TIMESTAMPOID); appendStringInfo(result, "\"%s\"", buf); } break; case JSONTYPE_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"))); + JsonEncodeDateTime(buf, val, TIMESTAMPTZOID); appendStringInfo(result, "\"%s\"", buf); } break; @@ -1585,6 +1548,107 @@ datum_to_json(Datum val, bool is_null, StringInfo result, } } +/* + * 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; + + date = DatumGetDateADT(value); + + /* 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); + } + } + break; + 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; + + timestamp = DatumGetTimestamp(value); + /* 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"))); + } + break; + case TIMESTAMPTZOID: + { + TimestampTz timestamp; + struct pg_tm tm; + int tz; + fsec_t fsec; + const char *tzn = NULL; + + timestamp = DatumGetTimestampTz(value); + /* 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"))); + } + break; + default: + elog(ERROR, "unknown jsonb value datetime type oid %d", typid); + return NULL; + } + + return buf; +} + /* * Process a single dimension of an array. * If it's the innermost dimension, output the values, otherwise call diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c index 014e7aa6e38..0f701801641 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 274959231b4..e17cd49602e 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -17,7 +17,7 @@ #include #include "fmgr.h" - +#include "datatype/timestamp.h" typedef int32 DateADT; @@ -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 d6baea5368c..e39572e00f2 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 */