From f4cbc986e3732bb30c4c990e5c453baf90a4dcbc Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 28 Aug 2026 19:10:24 -0400 Subject: [PATCH] Always use `iso` format and test `timezone` impact I noticed that timestamptz values weren't exported with time zone information, which is necessary to ensure proper round-tripping. Add the `date_time_output_format='iso'` setting to each chDB query to always format `timestamp` and `timestamptz` values in plain text formats with the ISO-8601 format in UTC. Remove setting the `datestyle` and `timezone` GUCs to hard-coded constants, instead allowing them to be respected. Or at least, we respect the `timezone` GUC for converting `timestamp` values, but always emit UTC for both timestamp and timestamptz, since that's all that `date_time_output_format='iso'` supports. Add a new test file, `test/sql/timestamp.sql` to test the impact of the new setting on plain-text output, as well as the impact of the `timezone` setting on `timestamp` values. Interestingly, setting a time zone in the structure, e.g. `DateTime64(3, 'Japan')`, has no impact on exported values. Add the "Timestamp Conversion" section to the chdb_hook docs to document these behaviors. In passing, change the `real` columns in `test/qa/taxi.sql` to `double precision`, since the source defines the values as `Float64`. --- CHANGELOG.md | 27 ++++++- doc/chdb_hook.md | 38 ++++++++- src/helper/chdb_helper.c | 1 + src/hook/copy.c | 7 -- test/expected/settings.out | 15 ++-- test/expected/settings_1.out | 15 ++-- test/expected/timestamp.out | 149 +++++++++++++++++++++++++++++++++++ test/qa/taxi.sql | 6 +- test/sql/settings.sql | 3 +- test/sql/timestamp.sql | 80 +++++++++++++++++++ 10 files changed, 317 insertions(+), 24 deletions(-) create mode 100644 test/expected/timestamp.out create mode 100644 test/sql/timestamp.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 338dfdf..7842acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,31 @@ All notable changes to this project will be documented in this file. It uses the [Semantic Versioning]: https://semver.org/spec/v2.0.0.html "Semantic Versioning 2.0.0" +## [v0.1.1] — Unreleased + +### 🐞 Bug Fixes + +* Added the `date_time_output_format='iso'` setting to each chDB query to + always format `timestamp` and `timestamptz` values in plain text formats + with the ISO-8601 format in UTC, converting `timestamp` values from the + current `timezone` setting. + +### 💅🏻 Quality + +* Added comprehensive tests for timestamp and timestamptz `COPY` output and + round-tripping, including the impact of the `timezone` setting on + `timestamp` values. + +### 📚 Documentation + +* Added the "Timestamp Conversion" section to the [chdb_hook docs] to + document the ISO-8601 format of plain text exports of `timestamp` and + `timestamptz` values, as well as the impact of the `timezone` setting on + exported and imported values. + + [v0.1.1]: https://github.com/clickhouse/pg_chdb/compare/v0.1.0...v0.1.1 + [chdb_hook docs](./doc/chdb_hook.md) + ## [v0.1.0] — 2026-08-25 The theme of this release is *Shakedown.* @@ -48,7 +73,7 @@ The theme of this release is *Shakedown.* * chdb extension reference documentation in [doc/chdb.md](doc/chdb.md) * chdb_hook module reference documentation in [doc/chdb_hook.md](doc/chdb_hook.md) - [v0.1.0]: https://github.com/clickhouse/pg_chdb/compare/a1487bd...v0.1.0 + [v0.1.0]: https://github.com/clickhouse/pg_chdb/compare/fca4dc1...v0.1.0 [chDB]: https://clickhouse.com/chdb "chDB - fast, reliable, and scalable in-process database" [formats]: https://github.com/chdb-io/chdb/blob/main/refs/clickhouse-formats-settings.md#complete-format-names-table "chDB Docs: Complete Format Names Table" diff --git a/doc/chdb_hook.md b/doc/chdb_hook.md index 7f63eab..b142eb3 100644 --- a/doc/chdb_hook.md +++ b/doc/chdb_hook.md @@ -477,8 +477,8 @@ those you need. | date | Date32 | | | time | Time64(6) | Override with `String` for formats that don't support times. | | timetz | String | | -| timestamp | DateTime64(6) | Declared with the `UTC` time zone; values cross as UTC instants. | -| timestamptz | DateTime64(6) | Declared with the `UTC` time zone; values cross as UTC instants. | +| timestamp | DateTime64(6) | Declared with the `UTC` time zone, converted from session time zone. | +| timestamptz | DateTime64(6) | Declared with the `UTC` time zone. | | numeric | Decimal | | | uuid | UUID | | | point | `Point` | Same two coordinates as Postgres. | @@ -497,6 +497,40 @@ No Postgres type maps to `Map` or `Tuple`, but [structure](#structure) may name one. A `Map` can convert to an array of key value pairs, and a `Tuple` converts to an array. Use `text[]` for heterogeneous support. +### Timestamp Conversion + +In plain text formats (TSV, CSV, etc.), the `COPY` hook emits DateTime and +DateTime64 values in ISO-8601 format, `YYYY-MM-DDThh:mm:ssZ`, without regard +to the current `datestyle` setting. This ensures that timestamptz values +remain consistent, even if a source importing the values uses a different time +zone. Using a different type in the `structure` output, such as `Datetime64(3, +'America/Los_Angeles')`, has no impact on the offset of the output, but does +change the precision. + +Timestamp TZ Examples: + +| timestamptz | `DateTime64(6, 'UTC')` | `DateTime64(3 'Japan')` | +| ----------------------------------------- | ----------------------------- | -------------------------- | +| `2026-08-28T12:00:00Z` | `2026-08-28T12:00:00.000000Z` | `2026-08-28T12:00:00.000Z` | +| `2026-08-28T11:00:00 America/Los_Angeles` | `2026-08-28T18:00:00.000000Z` | `2026-08-28T18:00:00.000Z` | +| `2026-08-28T10:00:00.723923 Asia/Tokyo` | `2026-08-28T01:00:00.723923Z` | `2026-08-28T01:00:00.723Z` | + +The `COPY` hook also converts timestamp values from the session time zone to +UTC, thus ensuring that they're output relative to that time zone. When loaded +into a new system, it should convert them to its local time zone. Thus the +values will differ if the time zone differs, but will be the same relative to +the time zone difference. + +Example of the effect of the `timezone` setting on the timestamp +`2026-08-28T12:00:00`: + +| timezone setting | `DateTime64(6, 'UTC')` | `DateTime64(3 'Japan')` | +| --------------------- | ----------------------------- | -------------------------- | +| `UTC` | `2026-08-28T12:00:00.000000Z` | `2026-08-28T12:00:00.000Z` | +| `America/Los_Angeles` | `2026-08-28T19:00:00.000000Z` | `2026-08-28T19:00:00.000Z` | +| `America/New_York` | `2026-08-28T16:00:00.000000Z` | `2026-08-28T16:00:00.000Z` | +| `Japan` | `2026-08-28T03:00:00.000000Z` | `2026-08-28T03:00:00.000Z` | + ### chDB to Postgres chdb_hook maps the ClickHouse types reported by [`DESCRIBE`] to these Postgres diff --git a/src/helper/chdb_helper.c b/src/helper/chdb_helper.c index 43424c1..10fb9ac 100644 --- a/src/helper/chdb_helper.c +++ b/src/helper/chdb_helper.c @@ -319,6 +319,7 @@ setup_session( "output_format_json_quote_denormals," "output_format_native_write_json_as_string," "output_format_native_encode_types_in_binary_format=0," + "date_time_output_format='iso'," "max_threads=%" PRIu16 ",max_parsing_threads=%" PRIu16 ",max_memory_usage=%" PRIu64, max_threads, diff --git a/src/hook/copy.c b/src/hook/copy.c index aa77fde..ae31b40 100644 --- a/src/hook/copy.c +++ b/src/hook/copy.c @@ -138,13 +138,6 @@ chdb_copy(chdbCopyContext* ctx) { */ int nestlevel = NewGUCNestLevel(); - set_config_option( - "datestyle", "ISO", PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false - ); - set_config_option( - "timezone", "UTC", PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false - ); - /* We always need a structure. */ if (ctx->structure[0] == '\0') { TupleDesc desc = RelationGetDescr(ctx->rel); diff --git a/test/expected/settings.out b/test/expected/settings.out index 2f5c0a6..e62e597 100644 --- a/test/expected/settings.out +++ b/test/expected/settings.out @@ -20,7 +20,8 @@ PREPARE show_chdb(bool) AS 'max_memory_usage', 'max_threads', 'max_parsing_threads', 'allow_experimental_nullable_tuple_type', 'output_format_native_encode_types_in_binary_format', - 'output_format_native_write_json_as_string' + 'output_format_native_write_json_as_string', + 'date_time_output_format' ) ORDER BY name $$, @@ -44,12 +45,13 @@ EXECUTE show_chdb(false); name | value ----------------------------------------------------+------- allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 0 max_parsing_threads | auto max_threads | auto output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- Set integer values. SET chdb.max_memory = 5000; @@ -69,12 +71,13 @@ EXECUTE show_chdb(false); name | value ----------------------------------------------------+------------ allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 5242880000 max_parsing_threads | 12 max_threads | 42 output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- And again using size syntax for the memory SET chdb.max_memory = '100MB'; @@ -94,12 +97,13 @@ EXECUTE show_chdb(false); name | value ----------------------------------------------------+----------- allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 104857600 max_parsing_threads | 10 max_threads | 88 output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- Set max values. SET chdb.max_memory = 65535; @@ -119,12 +123,13 @@ EXECUTE show_chdb(true); name | value ----------------------------------------------------+------------- allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 68718428160 max_parsing_threads | 65535 max_threads | MAX output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- Set invalid values. SET chdb.max_memory = -1; diff --git a/test/expected/settings_1.out b/test/expected/settings_1.out index e027f47..d29cd28 100644 --- a/test/expected/settings_1.out +++ b/test/expected/settings_1.out @@ -20,7 +20,8 @@ PREPARE show_chdb(bool) AS 'max_memory_usage', 'max_threads', 'max_parsing_threads', 'allow_experimental_nullable_tuple_type', 'output_format_native_encode_types_in_binary_format', - 'output_format_native_write_json_as_string' + 'output_format_native_write_json_as_string', + 'date_time_output_format' ) ORDER BY name $$, @@ -44,12 +45,13 @@ EXECUTE show_chdb(false); name | value ----------------------------------------------------+------- allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 0 max_parsing_threads | auto max_threads | auto output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- Set integer values. SET chdb.max_memory = 5000; @@ -69,12 +71,13 @@ EXECUTE show_chdb(false); name | value ----------------------------------------------------+------------ allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 5242880000 max_parsing_threads | 12 max_threads | 42 output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- And again using size syntax for the memory SET chdb.max_memory = '100MB'; @@ -94,12 +97,13 @@ EXECUTE show_chdb(false); name | value ----------------------------------------------------+----------- allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 104857600 max_parsing_threads | 10 max_threads | 88 output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- Set max values. SET chdb.max_memory = 65535; @@ -119,12 +123,13 @@ EXECUTE show_chdb(true); name | value ----------------------------------------------------+------------- allow_experimental_nullable_tuple_type | 1 + date_time_output_format | iso max_memory_usage | 68718428160 max_parsing_threads | 65535 max_threads | MAX output_format_native_encode_types_in_binary_format | 0 output_format_native_write_json_as_string | 1 -(6 rows) +(7 rows) -- Set invalid values. SET chdb.max_memory = -1; diff --git a/test/expected/timestamp.out b/test/expected/timestamp.out new file mode 100644 index 0000000..3fe1984 --- /dev/null +++ b/test/expected/timestamp.out @@ -0,0 +1,149 @@ +LOAD 'chdb_hook'; +SET datestyle TO ISO; +CREATE TABLE date_times ( + id INT, + ts TIMESTAMP, + tstz TIMESTAMPTZ +); +INSERT INTO date_times +VALUES (1, '2026-08-28T12:00:00', '2026-08-28T12:00:00Z') + , (2, '2026-08-28T11:00:00', '2026-08-28T11:00:00 America/Los_Angeles') + , (3, '2026-08-28T10:00:00', '2026-08-28T10:00:00.723923 Asia/Tokyo') +; +SELECT * FROM date_times ORDER BY id; + id | ts | tstz +----+---------------------+------------------------------- + 1 | 2026-08-28 12:00:00 | 2026-08-28 05:00:00-07 + 2 | 2026-08-28 11:00:00 | 2026-08-28 11:00:00-07 + 3 | 2026-08-28 10:00:00 | 2026-08-27 18:00:00.723923-07 +(3 rows) + +\set temp_dir /tmp/chdb-timestamp.tmp +\set tsv_path :temp_dir /date_times.tsv +\set tsv_url file:// :tsv_path +-- timestamp values converted to UTC relative to Postgres timezone setting. +-- timestamptz values should always emit the same UTC output, regardless of +-- timezone setting. All values should be output using chDB's +-- `date_time_output_format='iso'` setting (`YYYY-MM-DDThh:mm:ssZ`). +/************************ America/Los_Angeles ************************/ +-- timestamp values should be offset 7 hours. +SET timezone TO 'America/Los_Angeles'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + pg_read_file +--------------------------------------------------------------------- + 1 2026-08-28T19:00:00.000000Z 2026-08-28T12:00:00.000000Z+ + 2 2026-08-28T18:00:00.000000Z 2026-08-28T18:00:00.000000Z+ + 3 2026-08-28T17:00:00.000000Z 2026-08-28T01:00:00.723923Z+ + +(1 row) + +-- Should load identical values. +CREATE TABLE dt2 (LIKE date_times INCLUDING ALL); +COPY dt2 FROM :'tsv_url'; +SELECT * FROM dt2 ORDER BY id; + id | ts | tstz +----+---------------------+------------------------------- + 1 | 2026-08-28 12:00:00 | 2026-08-28 05:00:00-07 + 2 | 2026-08-28 11:00:00 | 2026-08-28 11:00:00-07 + 3 | 2026-08-28 10:00:00 | 2026-08-27 18:00:00.723923-07 +(3 rows) + +-- Load with different TZ, ts values differ but tstz the same. +SET timezone TO 'America/New_York'; +TRUNCATE dt2; +COPY dt2 FROM :'tsv_url'; +SET timezone TO 'America/Los_Angeles'; +SELECT * FROM dt2 ORDER BY id; + id | ts | tstz +----+---------------------+------------------------------- + 1 | 2026-08-28 15:00:00 | 2026-08-28 05:00:00-07 + 2 | 2026-08-28 14:00:00 | 2026-08-28 11:00:00-07 + 3 | 2026-08-28 13:00:00 | 2026-08-27 18:00:00.723923-07 +(3 rows) + +-- Different time zone defined in structure does not impact output, but +-- precision does. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(6, ''UTC'')'); +SELECT pg_read_file(:'tsv_path'); + pg_read_file +--------------------------------------------------------------------- + 1 2026-08-28T19:00:00.000Z 2026-08-28T12:00:00.000000Z+ + 2 2026-08-28T18:00:00.000Z 2026-08-28T18:00:00.000000Z+ + 3 2026-08-28T17:00:00.000Z 2026-08-28T01:00:00.723923Z+ + +(1 row) + +/************************ America/New_York ************************/ +-- timestamp values should be offset 4 hours, timestamptz values unchanged. +SET timezone TO 'America/New_York'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + pg_read_file +--------------------------------------------------------------------- + 1 2026-08-28T16:00:00.000000Z 2026-08-28T12:00:00.000000Z+ + 2 2026-08-28T15:00:00.000000Z 2026-08-28T18:00:00.000000Z+ + 3 2026-08-28T14:00:00.000000Z 2026-08-28T01:00:00.723923Z+ + +(1 row) + +-- Time zone ignored in structure, precision respected. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(6, ''UTC'')'); +SELECT pg_read_file(:'tsv_path'); + pg_read_file +--------------------------------------------------------------------- + 1 2026-08-28T16:00:00.000Z 2026-08-28T12:00:00.000000Z+ + 2 2026-08-28T15:00:00.000Z 2026-08-28T18:00:00.000000Z+ + 3 2026-08-28T14:00:00.000Z 2026-08-28T01:00:00.723923Z+ + +(1 row) + +/************************ Asia/Tokyo ************************/ +-- timestamp values should be offset 9 hours, timestamptz values unchanged. +SET timezone TO 'Asia/Tokyo'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + pg_read_file +--------------------------------------------------------------------- + 1 2026-08-28T03:00:00.000000Z 2026-08-28T12:00:00.000000Z+ + 2 2026-08-28T02:00:00.000000Z 2026-08-28T18:00:00.000000Z+ + 3 2026-08-28T01:00:00.000000Z 2026-08-28T01:00:00.723923Z+ + +(1 row) + +-- Time zone ignored in structure, precision respected. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(3, ''America/New_York'')'); +SELECT pg_read_file(:'tsv_path'); + pg_read_file +------------------------------------------------------------------ + 1 2026-08-28T03:00:00.000Z 2026-08-28T12:00:00.000Z+ + 2 2026-08-28T02:00:00.000Z 2026-08-28T18:00:00.000Z+ + 3 2026-08-28T01:00:00.000Z 2026-08-28T01:00:00.723Z+ + +(1 row) + +/************************ UTC ************************/ +-- timestamp values should not be offset, timestamptz values unchanged. +SET timezone TO 'UTC'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + pg_read_file +--------------------------------------------------------------------- + 1 2026-08-28T12:00:00.000000Z 2026-08-28T12:00:00.000000Z+ + 2 2026-08-28T11:00:00.000000Z 2026-08-28T18:00:00.000000Z+ + 3 2026-08-28T10:00:00.000000Z 2026-08-28T01:00:00.723923Z+ + +(1 row) + +-- Time zone ignored in structure, precision respected. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(3, ''America/New_York'')'); +SELECT pg_read_file(:'tsv_path'); + pg_read_file +------------------------------------------------------------------ + 1 2026-08-28T12:00:00.000Z 2026-08-28T12:00:00.000Z+ + 2 2026-08-28T11:00:00.000Z 2026-08-28T18:00:00.000Z+ + 3 2026-08-28T10:00:00.000Z 2026-08-28T01:00:00.723Z+ + +(1 row) + +\! rm -rf /tmp/chdb-timestamp.tmp 2> /dev/null || true diff --git a/test/qa/taxi.sql b/test/qa/taxi.sql index bbdff7e..79b4bba 100644 --- a/test/qa/taxi.sql +++ b/test/qa/taxi.sql @@ -2,7 +2,7 @@ DROP TABLE IF EXISTS trips; CREATE TABLE trips ( trip_id bigint NOT NULL, - vendor_id text NOT NULL, + vendor_id bigint NOT NULL, pickup_date date NOT NULL, pickup_datetime timestamp with time zone NOT NULL, dropoff_date date NOT NULL, @@ -29,7 +29,7 @@ CREATE TABLE trips ( dropoff character varying(25) NOT NULL, cab_type text NOT NULL, pickup_nyct2010_gid smallint NOT NULL, - pickup_ctlabel real NOT NULL, + pickup_ctlabel double precision NOT NULL, pickup_borocode smallint NOT NULL, pickup_ct2010 text NOT NULL, pickup_boroct2010 text NOT NULL, @@ -38,7 +38,7 @@ CREATE TABLE trips ( pickup_ntaname text NOT NULL, pickup_puma integer NOT NULL, dropoff_nyct2010_gid smallint NOT NULL, - dropoff_ctlabel real NOT NULL, + dropoff_ctlabel double precision NOT NULL, dropoff_borocode smallint NOT NULL, dropoff_ct2010 text NOT NULL, dropoff_boroct2010 text NOT NULL, diff --git a/test/sql/settings.sql b/test/sql/settings.sql index f24e50d..1091271 100644 --- a/test/sql/settings.sql +++ b/test/sql/settings.sql @@ -22,7 +22,8 @@ PREPARE show_chdb(bool) AS 'max_memory_usage', 'max_threads', 'max_parsing_threads', 'allow_experimental_nullable_tuple_type', 'output_format_native_encode_types_in_binary_format', - 'output_format_native_write_json_as_string' + 'output_format_native_write_json_as_string', + 'date_time_output_format' ) ORDER BY name $$, diff --git a/test/sql/timestamp.sql b/test/sql/timestamp.sql new file mode 100644 index 0000000..a41fa84 --- /dev/null +++ b/test/sql/timestamp.sql @@ -0,0 +1,80 @@ +LOAD 'chdb_hook'; +SET datestyle TO ISO; + +CREATE TABLE date_times ( + id INT, + ts TIMESTAMP, + tstz TIMESTAMPTZ +); + +INSERT INTO date_times +VALUES (1, '2026-08-28T12:00:00', '2026-08-28T12:00:00Z') + , (2, '2026-08-28T11:00:00', '2026-08-28T11:00:00 America/Los_Angeles') + , (3, '2026-08-28T10:00:00', '2026-08-28T10:00:00.723923 Asia/Tokyo') +; +SELECT * FROM date_times ORDER BY id; + +\set temp_dir /tmp/chdb-timestamp.tmp +\set tsv_path :temp_dir /date_times.tsv +\set tsv_url file:// :tsv_path + +-- timestamp values converted to UTC relative to Postgres timezone setting. +-- timestamptz values should always emit the same UTC output, regardless of +-- timezone setting. All values should be output using chDB's +-- `date_time_output_format='iso'` setting (`YYYY-MM-DDThh:mm:ssZ`). + +/************************ America/Los_Angeles ************************/ +-- timestamp values should be offset 7 hours. +SET timezone TO 'America/Los_Angeles'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + +-- Should load identical values. +CREATE TABLE dt2 (LIKE date_times INCLUDING ALL); +COPY dt2 FROM :'tsv_url'; +SELECT * FROM dt2 ORDER BY id; + +-- Load with different TZ, ts values differ but tstz the same. +SET timezone TO 'America/New_York'; +TRUNCATE dt2; +COPY dt2 FROM :'tsv_url'; +SET timezone TO 'America/Los_Angeles'; +SELECT * FROM dt2 ORDER BY id; + +-- Different time zone defined in structure does not impact output, but +-- precision does. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(6, ''UTC'')'); +SELECT pg_read_file(:'tsv_path'); + +/************************ America/New_York ************************/ +-- timestamp values should be offset 4 hours, timestamptz values unchanged. +SET timezone TO 'America/New_York'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + +-- Time zone ignored in structure, precision respected. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(6, ''UTC'')'); +SELECT pg_read_file(:'tsv_path'); + +/************************ Asia/Tokyo ************************/ +-- timestamp values should be offset 9 hours, timestamptz values unchanged. +SET timezone TO 'Asia/Tokyo'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + +-- Time zone ignored in structure, precision respected. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(3, ''America/New_York'')'); +SELECT pg_read_file(:'tsv_path'); + +/************************ UTC ************************/ +-- timestamp values should not be offset, timestamptz values unchanged. +SET timezone TO 'UTC'; +COPY date_times TO :'tsv_url'; +SELECT pg_read_file(:'tsv_path'); + +-- Time zone ignored in structure, precision respected. +COPY date_times TO :'tsv_url' (structure 'id Int8, ts DateTime64(3, ''Japan''), tstz DateTime64(3, ''America/New_York'')'); +SELECT pg_read_file(:'tsv_path'); + + +\! rm -rf /tmp/chdb-timestamp.tmp 2> /dev/null || true