]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/json.c
Use ISO 8601 format for dates converted to JSON, too.
[postgresql] / src / backend / utils / adt / json.c
index 68132aea4bfe59c2d6155e1d2e63b5ffb5cedbae..494a0285267a542582c8129a40fe551e2128cdb2 100644 (file)
@@ -25,6 +25,7 @@
 #include "parser/parse_coerce.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/date.h"
 #include "utils/datetime.h"
 #include "utils/lsyscache.h"
 #include "utils/json.h"
@@ -55,8 +56,9 @@ typedef enum                                  /* type categories for datum_to_json */
        JSONTYPE_NULL,                          /* null, so we didn't bother to identify */
        JSONTYPE_BOOL,                          /* boolean (built-in types only) */
        JSONTYPE_NUMERIC,                       /* numeric (ditto) */
-       JSONTYPE_TIMESTAMP,         /* we use special formatting for timestamp */
-       JSONTYPE_TIMESTAMPTZ,       /* ... and timestamptz */
+       JSONTYPE_DATE,                          /* we use special formatting for datetimes */
+       JSONTYPE_TIMESTAMP,
+       JSONTYPE_TIMESTAMPTZ,
        JSONTYPE_JSON,                          /* JSON itself (and JSONB) */
        JSONTYPE_ARRAY,                         /* array */
        JSONTYPE_COMPOSITE,                     /* composite */
@@ -1267,6 +1269,10 @@ json_categorize_type(Oid typoid,
                        *tcategory = JSONTYPE_NUMERIC;
                        break;
 
+               case DATEOID:
+                       *tcategory = JSONTYPE_DATE;
+                       break;
+
                case TIMESTAMPOID:
                        *tcategory = JSONTYPE_TIMESTAMP;
                        break;
@@ -1348,7 +1354,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                 tcategory == JSONTYPE_CAST))
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("key value must be scalar, not array, composite, or json")));
+                errmsg("key value must be scalar, not array, composite, or json")));
 
        switch (tcategory)
        {
@@ -1388,6 +1394,30 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                        }
                        pfree(outputstr);
                        break;
+               case JSONTYPE_DATE:
+                       {
+                               DateADT         date;
+                               struct pg_tm tm;
+                               char            buf[MAXDATELEN + 1];
+
+                               date = DatumGetDateADT(val);
+
+                               /* XSD doesn't support infinite values */
+                               if (DATE_NOT_FINITE(date))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                                        errmsg("date out of range"),
+                                                        errdetail("JSON does not support infinite date values.")));
+                               else
+                               {
+                                       j2date(date + POSTGRES_EPOCH_JDATE,
+                                                  &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
+                                       EncodeDateOnly(&tm, USE_XSD_DATES, buf);
+                               }
+
+                               appendStringInfo(result, "\"%s\"", buf);
+                       }
+                       break;
                case JSONTYPE_TIMESTAMP:
                        {
                                Timestamp       timestamp;
@@ -1410,7 +1440,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                                                        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                                                         errmsg("timestamp out of range")));
 
-                               appendStringInfo(result,"\"%s\"",buf);
+                               appendStringInfo(result, "\"%s\"", buf);
                        }
                        break;
                case JSONTYPE_TIMESTAMPTZ:
@@ -1437,7 +1467,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                                                        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                                                         errmsg("timestamp out of range")));
 
-                               appendStringInfo(result,"\"%s\"",buf);
+                               appendStringInfo(result, "\"%s\"", buf);
                        }
                        break;
                case JSONTYPE_JSON:
@@ -2305,20 +2335,21 @@ escape_json(StringInfo buf, const char *str)
                                appendStringInfoString(buf, "\\\"");
                                break;
                        case '\\':
+
                                /*
                                 * Unicode escapes are passed through as is. There is no
                                 * requirement that they denote a valid character in the
                                 * server encoding - indeed that is a big part of their
                                 * usefulness.
                                 *
-                                * All we require is that they consist of \uXXXX where
-                                * the Xs are hexadecimal digits. It is the responsibility
-                                * of the caller of, say, to_json() to make sure that the
-                                * unicode escape is valid.
+                                * All we require is that they consist of \uXXXX where the Xs
+                                * are hexadecimal digits. It is the responsibility of the
+                                * caller of, say, to_json() to make sure that the unicode
+                                * escape is valid.
                                 *
-                                * In the case of a jsonb string value being escaped, the
-                                * only unicode escape that should be present is \u0000,
-                                * all the other unicode escapes will have been resolved.
+                                * In the case of a jsonb string value being escaped, the only
+                                * unicode escape that should be present is \u0000, all the
+                                * other unicode escapes will have been resolved.
                                 */
                                if (p[1] == 'u' &&
                                        isxdigit((unsigned char) p[2]) &&