Preserve tz when converting to jsonb timestamptz
authorAndrew Dunstan <[email protected]>
Tue, 30 Jul 2024 11:57:38 +0000 (07:57 -0400)
committerAndrew Dunstan <[email protected]>
Tue, 30 Jul 2024 11:57:38 +0000 (07:57 -0400)
This removes an inconsistency in the treatment of different datatypes by
the jsonpath timestamp_tz() function. Conversions from data types that
are not timestamp-aware, such as date and timestamp, are now treated
consistently with conversion from those that are such as timestamptz.

Author: David Wheeler
Reviewed-by: Junwang Zhao and Jeevan Chalke
Discussion: https://postgr.es/m/7DE080CE-6D8C-4794-9BD1-7D9699172FAB%40justatheory.com

Backpatch to release 17.

src/backend/utils/adt/jsonpath_exec.c
src/test/regress/expected/jsonb_jsonpath.out

index d79c9298227a064740764a2581322991d9aea3da..c47221b7ee77eb0d6e465f7bee9c32089109d82a 100644 (file)
@@ -2707,12 +2707,27 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
            break;
        case jpiTimestampTz:
            {
+               struct pg_tm tm;
+               fsec_t      fsec;
+
                /* Convert result type to timestamp with time zone */
                switch (typid)
                {
                    case DATEOID:
                        checkTimezoneIsUsedForCast(cxt->useTz,
                                                   "date", "timestamptz");
+
+                       /*
+                        * Get the timezone value explicitly since JsonbValue
+                        * keeps that separate.
+                        */
+                       j2date(DatumGetDateADT(value) + POSTGRES_EPOCH_JDATE,
+                              &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
+                       tm.tm_hour = 0;
+                       tm.tm_min = 0;
+                       tm.tm_sec = 0;
+                       tz = DetermineTimeZoneOffset(&tm, session_timezone);
+
                        value = DirectFunctionCall1(date_timestamptz,
                                                    value);
                        break;
@@ -2726,6 +2741,16 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
                    case TIMESTAMPOID:
                        checkTimezoneIsUsedForCast(cxt->useTz,
                                                   "timestamp", "timestamptz");
+
+                       /*
+                        * Get the timezone value explicitly since JsonbValue
+                        * keeps that separate.
+                        */
+                       if (timestamp2tm(DatumGetTimestamp(value), NULL, &tm,
+                                        &fsec, NULL, NULL) == 0)
+                           tz = DetermineTimeZoneOffset(&tm,
+                                                        session_timezone);
+
                        value = DirectFunctionCall1(timestamp_timestamptz,
                                                    value);
                        break;
index 7bb4eb1bc276ef0bff04836e115ba359bc5dbd60..02abaac68912c7105cc0fffa8b605b9b31ddae8d 100644 (file)
@@ -2964,7 +2964,7 @@ HINT:  Use *_tz() function for time zone support.
 select jsonb_path_query_tz('"2023-08-15"', '$.timestamp_tz()'); -- should work
      jsonb_path_query_tz     
 -----------------------------
- "2023-08-15T07:00:00+00:00"
+ "2023-08-15T00:00:00-07:00"
 (1 row)
 
 select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
@@ -3151,7 +3151,7 @@ HINT:  Use *_tz() function for time zone support.
 select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp_tz()'); -- should work
      jsonb_path_query_tz     
 -----------------------------
- "2023-08-15T02:34:56+00:00"
+ "2023-08-15T12:34:56+10:00"
 (1 row)
 
 select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');