ClickHouse / ClickHouse/clickhouse-java

V2 driver ignores column timezones when reading via getTimestamp

Open
#2,787 16 comments 2 reactions 1 assignee Claimed by @chernser View on GitHub
bug question
Dominant language
Java
Stars
1.6k
Forks
636
Avg merge
2d 16h
Merged PRs (30d)
28

Description

## Description

### Steps to reproduce

When running the program below (see Code Example) I get the following output

```
Table test1 created.
Original Timestamp: 2026-03-12 22:58:08.000 (millis: 1773356288000)
Record inserted.
Read t_tokyo: 2026-03-13 07:58:08.0 (millis: 1773388688000)
Read t_ny: 2026-03-12 18:58:08.0 (millis: 1773341888000)
```

Note that one Timestamp value is inserted for both columns (as number of millis since epoch so it should be timezone independent) but when read back 2 different values are returned that are not equal to the original one.

I believe the issue is in https://github.com/ClickHouse/clickhouse-java/blob/268f1b7227ae16d144e81f09b4f7f71b2c2e59e4/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java#L1086 that completely ignores the column's timezone (`zdt.getTimeZone()`) and uses JVM default timezone to convert `LocalDateTime` value to `java.sql.Timestamp`.

### Expected Behaviour

I expect V2 driver to behave the same way as V1 driver and correctly produce 3 identical timestamps:

```
Table test1 created.
Original Timestamp: 2026-03-12 23:02:25.000 (millis: 1773356545000)
Record inserted.
Read t_tokyo: 2026-03-12 23:02:25.0 (millis: 1773356545000)
Read t_ny: 2026-03-12 23:02:25.0 (millis: 1773356545000)
```

### Code Example

```java

package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Properties;

public class App {
public static void main(String[] args) {
String url = "jdbc:clickhouse://localhost:8123/default";
Properties props = new Properties();
props.setProperty("user", "default");
props.setProperty("password", "");

try (Connection conn = DriverManager.getConnection(url, props)) {
try (Statement stmt = conn.createStatement()) {
// 1. Create the table
stmt.execute("DROP TABLE IF EXISTS test1");
stmt.execute("CREATE TABLE test1 (" +
" t_tokyo DateTime(3, 'Asia/Tokyo'), " +
" t_ny DateTime(3, 'America/New_York') " +
") ENGINE = MergeTree ORDER BY t_tokyo");
System.out.println("Table test1 created.");
}

// 2. Insert record
Timestamp originalTimestamp = new Timestamp(System.currentTimeMillis());
System.out.println("Original Timestamp: " + originalTimestamp + " (millis: " + originalTimestamp.getTime() + ")");

String insertSql = "INSERT INTO test1 (t_tokyo, t_ny) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
pstmt.setTimestamp(1, originalTimestamp);
pstmt.setTimestamp(2, originalTimestamp);
pstmt.executeUpdate();
System.out.println("Record inserted.");
}

// 3. Read back
String selectSql = "SELECT t_tokyo, t_ny FROM test1";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(selectSql)) {
if (rs.next()) {
Timestamp readTokyo = rs.getTimestamp("t_tokyo");
Timestamp readNy = rs.getTimestamp("t_ny");

System.out.println("Read t_tokyo: " + readTokyo + " (millis: " + readTokyo.getTime() + ")");
System.out.println("Read t_ny: " + readNy + " (millis: " + readNy.getTime() + ")");
} else {
System.out.println("No record found.");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

```

#### Environment
* Client version: 0.9.7
* Language version: 25
* OS: Linux

#### ClickHouse Server
* ClickHouse Server version: 26.2.3.2
* ClickHouse Server non-default settings, if any:

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.