Skip to content

Commit ab14a73

Browse files
committed
Use EncodeDateTime instead of to_char to render JSON timestamps.
Per gripe from Peter Eisentraut and Tom Lane. The output is slightly different, but still ISO 8601 compliant: to_char doesn't output the minutes when time zone offset is an integer number of hours, while EncodeDateTime outputs ":00". The code is slightly adapted from code in xml.c
1 parent 0ad1a81 commit ab14a73

File tree

3 files changed

+56
-33
lines changed

3 files changed

+56
-33
lines changed

src/backend/utils/adt/json.c

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
#include "lib/stringinfo.h"
2222
#include "libpq/pqformat.h"
2323
#include "mb/pg_wchar.h"
24+
#include "miscadmin.h"
2425
#include "parser/parse_coerce.h"
2526
#include "utils/array.h"
2627
#include "utils/builtins.h"
27-
#include "utils/formatting.h"
28+
#include "utils/datetime.h"
2829
#include "utils/lsyscache.h"
2930
#include "utils/json.h"
3031
#include "utils/jsonapi.h"
@@ -63,13 +64,6 @@ typedef enum /* type categories for datum_to_json */
6364
JSONTYPE_OTHER /* all else */
6465
} JsonTypeCategory;
6566

66-
/*
67-
* to_char formats to turn timestamps and timpstamptzs into json strings
68-
* that are ISO 8601 compliant
69-
*/
70-
#define TS_ISO8601_FMT "\\\"YYYY-MM-DD\"T\"HH24:MI:SS.US\\\""
71-
#define TSTZ_ISO8601_FMT "\\\"YYYY-MM-DD\"T\"HH24:MI:SS.USOF\\\""
72-
7367
static inline void json_lex(JsonLexContext *lex);
7468
static inline void json_lex_string(JsonLexContext *lex);
7569
static inline void json_lex_number(JsonLexContext *lex, char *s, bool *num_err);
@@ -1394,27 +1388,56 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
13941388
pfree(outputstr);
13951389
break;
13961390
case JSONTYPE_TIMESTAMP:
1397-
/*
1398-
* The timestamp format used here provides for quoting the string,
1399-
* so no escaping is required.
1400-
*/
1401-
jsontext = DatumGetTextP(
1402-
DirectFunctionCall2(timestamp_to_char, val,
1403-
CStringGetTextDatum(TS_ISO8601_FMT)));
1404-
outputstr = text_to_cstring(jsontext);
1405-
appendStringInfoString(result, outputstr);
1406-
pfree(outputstr);
1407-
pfree(jsontext);
1391+
{
1392+
Timestamp timestamp;
1393+
struct pg_tm tm;
1394+
fsec_t fsec;
1395+
char buf[MAXDATELEN + 1];
1396+
1397+
timestamp = DatumGetTimestamp(val);
1398+
1399+
/* XSD doesn't support infinite values */
1400+
if (TIMESTAMP_NOT_FINITE(timestamp))
1401+
ereport(ERROR,
1402+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1403+
errmsg("timestamp out of range"),
1404+
errdetail("JSON does not support infinite timestamp values.")));
1405+
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
1406+
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
1407+
else
1408+
ereport(ERROR,
1409+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1410+
errmsg("timestamp out of range")));
1411+
1412+
appendStringInfo(result,"\"%s\"",buf);
1413+
}
14081414
break;
14091415
case JSONTYPE_TIMESTAMPTZ:
1410-
/* same comment as for timestamp above */
1411-
jsontext = DatumGetTextP(
1412-
DirectFunctionCall2(timestamptz_to_char, val,
1413-
CStringGetTextDatum(TSTZ_ISO8601_FMT)));
1414-
outputstr = text_to_cstring(jsontext);
1415-
appendStringInfoString(result, outputstr);
1416-
pfree(outputstr);
1417-
pfree(jsontext);
1416+
{
1417+
TimestampTz timestamp;
1418+
struct pg_tm tm;
1419+
int tz;
1420+
fsec_t fsec;
1421+
const char *tzn = NULL;
1422+
char buf[MAXDATELEN + 1];
1423+
1424+
timestamp = DatumGetTimestamp(val);
1425+
1426+
/* XSD doesn't support infinite values */
1427+
if (TIMESTAMP_NOT_FINITE(timestamp))
1428+
ereport(ERROR,
1429+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1430+
errmsg("timestamp out of range"),
1431+
errdetail("JSON does not support infinite timestamp values.")));
1432+
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
1433+
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
1434+
else
1435+
ereport(ERROR,
1436+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1437+
errmsg("timestamp out of range")));
1438+
1439+
appendStringInfo(result,"\"%s\"",buf);
1440+
}
14181441
break;
14191442
case JSONTYPE_JSON:
14201443
/* JSON and JSONB output will already be escaped */

src/test/regress/expected/json.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,9 @@ select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
420420

421421
SET LOCAL TIME ZONE -8;
422422
select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
423-
to_json
424-
---------------------------------
425-
"2014-05-28T08:22:35.614298-08"
423+
to_json
424+
------------------------------------
425+
"2014-05-28T08:22:35.614298-08:00"
426426
(1 row)
427427

428428
COMMIT;

src/test/regress/expected/json_1.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,9 @@ select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
420420

421421
SET LOCAL TIME ZONE -8;
422422
select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
423-
to_json
424-
---------------------------------
425-
"2014-05-28T08:22:35.614298-08"
423+
to_json
424+
------------------------------------
425+
"2014-05-28T08:22:35.614298-08:00"
426426
(1 row)
427427

428428
COMMIT;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy