]> granicus.if.org Git - postgresql/commitdiff
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]) &&