Use ISO 8601 format for dates converted to JSON, too.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Aug 2014 02:57:15 +0000 (22:57 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Aug 2014 02:57:46 +0000 (22:57 -0400)
Commit f30015b6d794c15d52abbb3df3a65081fbefb1ed made this happen for
timestamp and timestamptz, but it seems pretty inconsistent to not
do it for simple dates as well.

(In passing, I re-pgindent'd json.c.)

doc/src/sgml/release-9.4.sgml
src/backend/utils/adt/json.c

index 7ae67e0cec2e9848bbecf50da3fcf904f80349e4..5330c425d3d2c257bf23e44e7c9326ba28936ea3 100644 (file)
 
     <listitem>
      <para>
-      When converting values of type <type>timestamp</>
+      When converting values of type <type>date</>, <type>timestamp</>
       or <type>timestamptz</>
       to <link linkend="datatype-json"><type>JSON</type></link>, render the
       values in a format compliant with ISO 8601 (Andrew Dunstan)
      <para>
       Previously such values were rendered according to the current
       <xref linkend="guc-datestyle"> setting; but many JSON processors
-      require timestamps to be in ISO 8601 format.
+      require timestamps to be in ISO 8601 format.  If necessary, the
+      previous behavior can be obtained by explicitly casting the datetime
+      value to <type>text</> before passing it to the JSON conversion
+      function.
      </para>
     </listitem>
 
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]) &&