From e2077509106abb70f6fc9526185d39ac975475bd Mon Sep 17 00:00:00 2001 From: Alex Kasko Date: Fri, 23 Jan 2026 11:09:47 +0000 Subject: [PATCH] Allow to get timestamps from DATE column (1.4) This is a backport of the PR #539 to `v1.4-andium` stable branch. This change fixes the NPE when a timestamp is being requested from the `DATE` result column. With it both `rs.getTimestamp(idx)` and `rs.getObject(idx, LocalDateTime.class)` should work. Testing: new tests added to cover `java.sql.Timestamp` and `java.time.LocalDateTime` Fixes: #537 --- src/main/java/org/duckdb/DuckDBResultSet.java | 2 +- src/main/java/org/duckdb/DuckDBVector.java | 22 +++++++++++-- src/test/java/org/duckdb/TestTimestamp.java | 32 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/duckdb/DuckDBResultSet.java b/src/main/java/org/duckdb/DuckDBResultSet.java index 0d14712dc..70e7d19e5 100644 --- a/src/main/java/org/duckdb/DuckDBResultSet.java +++ b/src/main/java/org/duckdb/DuckDBResultSet.java @@ -1356,7 +1356,7 @@ public T getObject(int columnIndex, Class type) throws SQLException { ", SQL type: " + sqlType); } } else if (type == LocalDateTime.class) { - if (isTimestamp(sqlType)) { + if (isTimestamp(sqlType) || sqlType == DuckDBColumnType.DATE) { return type.cast(getLocalDateTime(columnIndex)); } else { throw new SQLException("Can't convert value to LocalDateTime, Java type: " + type + diff --git a/src/main/java/org/duckdb/DuckDBVector.java b/src/main/java/org/duckdb/DuckDBVector.java index 7e4b923b5..8c1b958f5 100644 --- a/src/main/java/org/duckdb/DuckDBVector.java +++ b/src/main/java/org/duckdb/DuckDBVector.java @@ -632,11 +632,24 @@ private boolean isType(DuckDBColumnType columnType) { return duckdb_type == columnType; } + private LocalDateTime getLocalDateTimeFromDate(int idx) throws SQLException { + LocalDate ld = getLocalDate(idx); + if (ld == null) { + return null; + } + return ld.atStartOfDay(); + } + Timestamp getTimestamp(int idx, Calendar calNullable) throws SQLException { if (check_and_null(idx)) { return null; } - LocalDateTime ldt = getLocalDateTimeFromTimestamp(idx, calNullable); + final LocalDateTime ldt; + if (duckdb_type == DuckDBColumnType.DATE) { + ldt = getLocalDateTimeFromDate(idx); + } else { + ldt = getLocalDateTimeFromTimestamp(idx, calNullable); + } if (ldt != null) { return Timestamp.valueOf(ldt); } @@ -648,7 +661,12 @@ Timestamp getTimestamp(int idx, Calendar calNullable) throws SQLException { } LocalDateTime getLocalDateTime(int idx) throws SQLException { - LocalDateTime ldt = getLocalDateTimeFromTimestamp(idx, null); + final LocalDateTime ldt; + if (duckdb_type == DuckDBColumnType.DATE) { + ldt = getLocalDateTimeFromDate(idx); + } else { + ldt = getLocalDateTimeFromTimestamp(idx, null); + } if (ldt != null) { return ldt; } diff --git a/src/test/java/org/duckdb/TestTimestamp.java b/src/test/java/org/duckdb/TestTimestamp.java index a433ab0b7..59e798c23 100644 --- a/src/test/java/org/duckdb/TestTimestamp.java +++ b/src/test/java/org/duckdb/TestTimestamp.java @@ -581,4 +581,36 @@ public static void test_timestamp_before_epoch() throws Exception { TimeZone.setDefault(defaultTimeZone); } } + + public static void test_timestamp_read_ts_from_date() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT '2020-01-02'::DATE")) { + assertTrue(rs.next()); + assertEquals(rs.getTimestamp(1).toLocalDateTime().toLocalDate(), LocalDate.of(2020, 1, 2)); + assertFalse(rs.next()); + } + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT NULL::DATE")) { + assertTrue(rs.next()); + assertNull(rs.getTimestamp(1)); + assertTrue(rs.wasNull()); + assertFalse(rs.next()); + } + } + + public static void test_timestamp_read_ldt_from_date() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT '2020-01-02'::DATE")) { + assertTrue(rs.next()); + assertEquals(rs.getObject(1, LocalDateTime.class).toLocalDate(), LocalDate.of(2020, 1, 2)); + assertFalse(rs.next()); + } + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT NULL::DATE")) { + assertTrue(rs.next()); + assertNull(rs.getObject(1, LocalDateTime.class)); + assertTrue(rs.wasNull()); + assertFalse(rs.next()); + } + } }