From 44e9eac4acda9dd81269c874dcc3760c7acf4cfa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Prante?= <joergprante@gmail.com>
Date: Tue, 18 Mar 2025 17:28:43 +0100
Subject: [PATCH] add more date/time routines with java.time where possible

---
 gradle.properties                             |   2 +-
 .../java/org/xbib/jdbc/mariadb/MariaDB.java   |  18 +-
 .../xbib/jdbc/mariadb/test/MariaDBTest.java   |  14 +-
 .../java/org/xbib/jdbc/oracle/Oracle.java     |  39 ++--
 .../org/xbib/jdbc/oracle/test/OracleTest.java |   6 +-
 .../org/xbib/jdbc/postgresql/Postgresql.java  |  16 +-
 .../jdbc/postgresql/test/PostgresqlTest.java  |  13 +-
 .../org/xbib/jdbc/query/DatabaseImpl.java     |  42 ++--
 .../org/xbib/jdbc/query/DatabaseProvider.java |   2 +-
 .../main/java/org/xbib/jdbc/query/Flavor.java |   6 +-
 .../java/org/xbib/jdbc/query/Options.java     |   2 +-
 .../org/xbib/jdbc/query/OptionsDefault.java   |   2 +-
 .../org/xbib/jdbc/query/OptionsOverride.java  |   4 +-
 .../main/java/org/xbib/jdbc/query/Row.java    |  23 +-
 .../java/org/xbib/jdbc/query/RowsAdapter.java | 147 ++++++++----
 .../main/java/org/xbib/jdbc/query/Schema.java |  41 +++-
 .../java/org/xbib/jdbc/query/SqlInsert.java   |  17 +-
 .../org/xbib/jdbc/query/SqlInsertImpl.java    |  45 +++-
 .../java/org/xbib/jdbc/query/SqlSelect.java   |  32 ++-
 .../org/xbib/jdbc/query/SqlSelectImpl.java    | 125 ++++++++--
 .../org/xbib/jdbc/query/SqlUpdateImpl.java    |   4 +-
 .../org/xbib/jdbc/query/StatementAdapter.java |  27 ++-
 .../org/xbib/jdbc/query/flavor/Derby.java     |  14 +-
 .../java/org/xbib/jdbc/query/flavor/H2.java   |  14 +-
 .../java/org/xbib/jdbc/query/flavor/Hsql.java |  14 +-
 .../org/xbib/jdbc/query/test/HsqldbTest.java  |  20 +-
 .../org/xbib/jdbc/query/test/RowStub.java     | 126 ++++++++---
 jdbc-sqlserver/build.gradle                   |  10 -
 jdbc-sqlserver/src/main/java/module-info.java |  10 -
 .../org/xbib/jdbc/sqlserver/SqlServer.java    | 214 ------------------
 .../services/org.xbib.jdbc.query.Flavor       |   1 -
 .../jdbc/sqlserver/test/SqlServerTest.java    |  87 -------
 .../src/test/resources/logging.properties     |  10 -
 .../java/org/xbib/jdbc/test/CommonTest.java   | 162 ++++++++-----
 settings.gradle                               |  13 +-
 35 files changed, 703 insertions(+), 619 deletions(-)
 delete mode 100644 jdbc-sqlserver/build.gradle
 delete mode 100644 jdbc-sqlserver/src/main/java/module-info.java
 delete mode 100644 jdbc-sqlserver/src/main/java/org/xbib/jdbc/sqlserver/SqlServer.java
 delete mode 100644 jdbc-sqlserver/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor
 delete mode 100644 jdbc-sqlserver/src/test/java/org/xbib/jdbc/sqlserver/test/SqlServerTest.java
 delete mode 100644 jdbc-sqlserver/src/test/resources/logging.properties

diff --git a/gradle.properties b/gradle.properties
index 2593a86..4759c74 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,3 @@
 group = org.xbib
 name = database
-version = 2.3.3
+version = 2.4.0
diff --git a/jdbc-mariadb/src/main/java/org/xbib/jdbc/mariadb/MariaDB.java b/jdbc-mariadb/src/main/java/org/xbib/jdbc/mariadb/MariaDB.java
index 01b60c1..202c803 100644
--- a/jdbc-mariadb/src/main/java/org/xbib/jdbc/mariadb/MariaDB.java
+++ b/jdbc-mariadb/src/main/java/org/xbib/jdbc/mariadb/MariaDB.java
@@ -103,13 +103,23 @@ public class MariaDB implements Flavor {
     }
 
     @Override
-    public String typeLocalDateTime() {
-        return "datetime(3)";
+    public String typeInstant() {
+        return "timestamp(3)"; // 3 = millisecond resolution
     }
 
     @Override
-    public String columnTypeLocalDateTime() {
-        return "DATETIME";
+    public String typeLocalDateTime() {
+        return "timestamp(3)"; // 3 = millisecond resolution
+    }
+
+    @Override
+    public String typeOffsetDateTime() {
+        return "datetime(3)"; // 3 = millisecond resolution
+    }
+
+    @Override
+    public String typeZonedDateTime() {
+        return "datetime(3)"; // 3 = millisecond resolution
     }
 
     @Override
diff --git a/jdbc-mariadb/src/test/java/org/xbib/jdbc/mariadb/test/MariaDBTest.java b/jdbc-mariadb/src/test/java/org/xbib/jdbc/mariadb/test/MariaDBTest.java
index 068a720..f7183bc 100644
--- a/jdbc-mariadb/src/test/java/org/xbib/jdbc/mariadb/test/MariaDBTest.java
+++ b/jdbc-mariadb/src/test/java/org/xbib/jdbc/mariadb/test/MariaDBTest.java
@@ -44,7 +44,11 @@ public class MariaDBTest extends CommonTest {
     @Override
     protected DatabaseProvider createDatabaseProvider(OptionsOverride options) {
         return DatabaseProvider.builder(getClass().getClassLoader(),
-                mariaDBContainer.getJdbcUrl(), null, null, "testUser", "testPassword")
+                        mariaDBContainer.getJdbcUrl(),
+                        null,
+                        null,
+                        "testUser",
+                        "testPassword")
                 .withSqlParameterLogging()
                 .withSqlInExceptionMessages()
                 .withOptions(options)
@@ -154,7 +158,7 @@ public class MariaDBTest extends CommonTest {
                 .argClobString("hello again")
                 .argBlobBytes(new byte[]{'1', '2'})
                 .argBoolean(true)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow)
                 .insert(1);
 
@@ -170,7 +174,7 @@ public class MariaDBTest extends CommonTest {
                 .argClobString("bye again")
                 .argBlobBytes(new byte[]{'3', '4'})
                 .argBoolean(false)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow)
                 .insert(1);
 
@@ -227,7 +231,7 @@ public class MariaDBTest extends CommonTest {
                                 .argString("str_lob", "bye again")
                                 .argBlobBytes("bin_blob", new byte[]{'3', '4'})
                                 .argString("boolean_flag", "N")//.argBoolean("boolean_flag", false)
-                                .argLocalDateTime("date_millis", now)
+                                .argLocalDateTime("date_millis", localDateTimeNow)
                                 .argLocalDate("local_date", localDateNow),
                         new SqlArgs()
                                 .argInteger("nbr_integer", Integer.MAX_VALUE)
@@ -240,7 +244,7 @@ public class MariaDBTest extends CommonTest {
                                 .argString("str_lob", "hello again")
                                 .argBlobBytes("bin_blob", new byte[]{'1', '2'})
                                 .argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true)
-                                .argLocalDateTime("date_millis", now)
+                                .argLocalDateTime("date_millis", localDateTimeNow)
                                 .argLocalDate("local_date", localDateNow)),
                 db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal,"
                                 + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1")
diff --git a/jdbc-oracle/src/main/java/org/xbib/jdbc/oracle/Oracle.java b/jdbc-oracle/src/main/java/org/xbib/jdbc/oracle/Oracle.java
index 8210322..b84a28a 100644
--- a/jdbc-oracle/src/main/java/org/xbib/jdbc/oracle/Oracle.java
+++ b/jdbc-oracle/src/main/java/org/xbib/jdbc/oracle/Oracle.java
@@ -50,16 +50,6 @@ public class Oracle implements Flavor {
         }
     }
 
-    /*@Override
-    public void setFloat(PreparedStatement preparedStatement, int i, Float floatValue) throws SQLException {
-        if (preparedStatement instanceof ProxyPreparedStatement) {
-            ProxyPreparedStatement proxyPreparedStatement = (ProxyPreparedStatement) preparedStatement;
-            ((OraclePreparedStatement) proxyPreparedStatement.getDelegate()).setBinaryFloat(i, floatValue);
-        } else {
-            ((OraclePreparedStatement) preparedStatement).setBinaryFloat(i, floatValue);
-        }
-    }*/
-
     @Override
     public String typeDouble() {
         return "binary_double";
@@ -71,16 +61,6 @@ public class Oracle implements Flavor {
         }
     }
 
-    /*@Override
-    public void setDouble(PreparedStatement preparedStatement, int i, Double doubleValue) throws SQLException {
-        if (preparedStatement instanceof ProxyPreparedStatement) {
-            ProxyPreparedStatement proxyPreparedStatement = (ProxyPreparedStatement) preparedStatement;
-            ((OraclePreparedStatement) proxyPreparedStatement.getDelegate()).setBinaryDouble(i, doubleValue);
-        } else {
-            ((OraclePreparedStatement) preparedStatement).setBinaryDouble(i, doubleValue);
-        }
-    }*/
-
     @Override
     public String typeBigDecimal(int size, int precision) {
         return "numeric(" + size + "," + precision + ")";
@@ -102,17 +82,28 @@ public class Oracle implements Flavor {
     }
 
     @Override
-    public String typeLocalDateTime() {
-        return "timestamp(3)";
+    public String typeInstant() {
+        return "timestamp";
     }
 
     @Override
-    public String columnTypeLocalDateTime() {
-        return "TIMESTAMP";
+    public String typeLocalDateTime() {
+        return "timestamp";
+    }
+
+    @Override
+    public String typeOffsetDateTime() {
+        return "timestamp with time zone";
+    }
+
+    @Override
+    public String typeZonedDateTime() {
+        return "timestamp with time zone";
     }
 
     @Override
     public String typeLocalDate() {
+        // well, this is a full blown timestamp in Oracle
         return "date";
     }
 
diff --git a/jdbc-oracle/src/test/java/org/xbib/jdbc/oracle/test/OracleTest.java b/jdbc-oracle/src/test/java/org/xbib/jdbc/oracle/test/OracleTest.java
index 08f6ca9..85a7f52 100644
--- a/jdbc-oracle/src/test/java/org/xbib/jdbc/oracle/test/OracleTest.java
+++ b/jdbc-oracle/src/test/java/org/xbib/jdbc/oracle/test/OracleTest.java
@@ -45,7 +45,11 @@ public class OracleTest extends CommonTest {
     @Override
     protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
         return DatabaseProvider.builder(getClass().getClassLoader(),
-                oracleContainer.getJdbcUrl(), null, null, "testUser", "testPassword")
+                oracleContainer.getJdbcUrl(),
+                        null,
+                        null,
+                        "testUser",
+                        "testPassword")
                 .withSqlParameterLogging()
                 .withSqlInExceptionMessages()
                 .withOptions(options)
diff --git a/jdbc-postgresql/src/main/java/org/xbib/jdbc/postgresql/Postgresql.java b/jdbc-postgresql/src/main/java/org/xbib/jdbc/postgresql/Postgresql.java
index 2b0bb08..7b6eaa5 100644
--- a/jdbc-postgresql/src/main/java/org/xbib/jdbc/postgresql/Postgresql.java
+++ b/jdbc-postgresql/src/main/java/org/xbib/jdbc/postgresql/Postgresql.java
@@ -100,15 +100,25 @@ public class Postgresql implements Flavor {
     }
 
     @Override
-    public String typeLocalDateTime() {
-        return "timestamp(3)";
+    public String typeInstant() {
+        return "timestamp";
     }
 
     @Override
-    public String columnTypeLocalDateTime() {
+    public String typeLocalDateTime() {
         return "timestamp";
     }
 
+    @Override
+    public String typeOffsetDateTime() {
+        return "timestamp with time zone";
+    }
+
+    @Override
+    public String typeZonedDateTime() {
+        return "timestamp with time zone";
+    }
+
     @Override
     public String typeLocalDate() {
         return "date";
diff --git a/jdbc-postgresql/src/test/java/org/xbib/jdbc/postgresql/test/PostgresqlTest.java b/jdbc-postgresql/src/test/java/org/xbib/jdbc/postgresql/test/PostgresqlTest.java
index df0e2f1..327c924 100644
--- a/jdbc-postgresql/src/test/java/org/xbib/jdbc/postgresql/test/PostgresqlTest.java
+++ b/jdbc-postgresql/src/test/java/org/xbib/jdbc/postgresql/test/PostgresqlTest.java
@@ -20,7 +20,6 @@ public class PostgresqlTest extends CommonTest {
     static PostgreSQLContainer<?> postgreSQLContainer;
 
     static {
-        // postgresql 9.6.12
         postgreSQLContainer = new PostgreSQLContainer<>("postgres")
                 .withDatabaseName("testDB")
                 .withUsername("testUser")
@@ -39,12 +38,12 @@ public class PostgresqlTest extends CommonTest {
 
     @Override
     protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
-        Config config = ConfigSupplier.of()
-                .property("database.url", postgreSQLContainer.getJdbcUrl())
-                .property("database.user", "testUser")
-                .property("database.password", "testPassword")
-                .get();
-        return DatabaseProvider.builder(config)
+        return DatabaseProvider.builder(getClass().getClassLoader(),
+                        postgreSQLContainer.getJdbcUrl(),
+                        null,
+                        null,
+                        "testUser",
+                        "testPassword")
                 .withSqlParameterLogging()
                 .withSqlInExceptionMessages()
                 .withOptions(options)
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java
index 8c9f5fb..9be55fc 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseImpl.java
@@ -25,7 +25,7 @@ import java.util.logging.Logger;
  */
 public class DatabaseImpl implements Database {
     
-    private static final Logger logger = Logger.getLogger(Database.class.getName());
+    private static final Logger logger = Logger.getLogger(DatabaseImpl.class.getName());
     
     private final Connection connection;
 
@@ -354,15 +354,17 @@ public class DatabaseImpl implements Database {
             ResultSetMetaData md = rows.getMetadata();
             List<Object> columnLabels = new ArrayList<>();
             List<Object> classNames = new ArrayList<>();
+            List<Object> columnTypeNames = new ArrayList<>();
             for (int i = 1; i <= md.getColumnCount(); i++) {
                 columnLabels.add(md.getColumnLabel(i));
                 classNames.add(md.getColumnClassName(i));
+                columnTypeNames.add(md.getColumnTypeName(i));
             }
             table.add(columnLabels);
             table.add(classNames);
             int i = 0;
             while (rows.next() && (limit <= 0 || i++ < limit)) {
-                table.add(getRow(rows, classNames));
+                table.add(getRow(rows, classNames, columnTypeNames));
             }
             table.setTotal(rows.rowCount());
             return true;
@@ -382,15 +384,17 @@ public class DatabaseImpl implements Database {
             ResultSetMetaData md = rows.getMetadata();
             List<Object> columnLabels = new ArrayList<>();
             List<Object> classNames = new ArrayList<>();
+            List<Object> columnTypeNames = new ArrayList<>();
             for (int i = 1; i <= md.getColumnCount(); i++) {
                 columnLabels.add(md.getColumnLabel(i));
                 classNames.add(md.getColumnClassName(i));
+                columnTypeNames.add(md.getColumnTypeName(i));
             }
             consumer.accept(columnLabels);
             consumer.accept(classNames);
             int i = 0;
             while (rows.next() && (limit <= 0 || i++ < limit)) {
-                consumer.accept(getRow(rows, classNames));
+                consumer.accept(getRow(rows, classNames, columnTypeNames));
             }
             return true;
         });
@@ -517,20 +521,28 @@ public class DatabaseImpl implements Database {
         });
     }
 
-    private List<Object> getRow(Rows rows, List<Object> classNames) {
+    private List<Object> getRow(Rows rows, List<Object> classNames, List<Object> columnTypeNames) {
         List<Object> row = new ArrayList<>();
+        // we may have some column type names that need special treatment
+
         for (int i = 0; i < classNames.size(); i++) {
-            String className = classNames.get(i).toString();
-            switch (className) {
-                case "java.lang.Character", "java.lang.String" -> row.add(rows.getStringOrEmpty(i + 1));
-                case "java.lang.Integer" -> row.add(rows.getIntegerOrNull(i + 1));
-                case "java.lang.Long" -> row.add(rows.getLongOrNull(i + 1));
-                case "java.lang.Boolean" -> row.add(rows.getBooleanOrFalse(i + 1));
-                case "java.sql.Clob", "oracle.jdbc.OracleClob" -> row.add(rows.getClobStringOrEmpty(i + 1));
-                case "java.sql.Date" -> row.add(rows.getLocalDateOrNull(i + 1));
-                case "java.sql.Timestamp", "oracle.sql.TIMESTAMP" -> row.add(rows.getLocalDateTimeOrNull(i + 1));
-                case "java.math.BigDecimal" -> row.add(rows.getBigDecimalOrNull(i + 1));
-                default -> throw new DatabaseException("unexpected column class name: " + className);
+            String columnTypeName = columnTypeNames.get(i).toString();
+            if ("TIMESTAMPTZ".equals(columnTypeName)) {
+                row.add(rows.getOffsetDateTimeOrNull(i + 1));
+            } else {
+                String className = classNames.get(i).toString();
+                switch (className) {
+                    case "java.lang.Character", "java.lang.String" -> row.add(rows.getStringOrEmpty(i + 1));
+                    case "java.lang.Integer" -> row.add(rows.getIntegerOrNull(i + 1));
+                    case "java.lang.Long" -> row.add(rows.getLongOrNull(i + 1));
+                    case "java.lang.Boolean" -> row.add(rows.getBooleanOrFalse(i + 1));
+                    case "java.sql.Clob", "oracle.jdbc.OracleClob" -> row.add(rows.getClobStringOrEmpty(i + 1));
+                    case "java.sql.Date" -> row.add(rows.getLocalDateOrNull(i + 1));
+                    case "oracle.sql.TIMESTAMPTZ" -> row.add(rows.getOffsetDateTimeOrNull(i + 1));
+                    case "java.sql.Timestamp", "oracle.sql.TIMESTAMP" -> row.add(rows.getLocalDateTimeOrNull(i + 1));
+                    case "java.math.BigDecimal" -> row.add(rows.getBigDecimalOrNull(i + 1));
+                    default -> throw new DatabaseException("unexpected column class name: " + className);
+                }
             }
         }
         return row;
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java
index 4887719..33a30ba 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/DatabaseProvider.java
@@ -384,7 +384,7 @@ public final class DatabaseProvider implements Supplier<Database> {
         public DatabaseProviderBuilder withDatePerAppOnly() {
             return new DatabaseProviderBuilderImpl(dataSource, connectionProvider, new OptionsOverride() {
                 @Override
-                public boolean useLocalDateTimeOnly() {
+                public boolean useClientClock() {
                     return true;
                 }
             }.withParent(this.options));
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java
index df2b073..798c2bd 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Flavor.java
@@ -38,9 +38,13 @@ public interface Flavor {
 
     String typeBlob();
 
+    String typeInstant();
+
     String typeLocalDateTime();
 
-    String columnTypeLocalDateTime();
+    String typeOffsetDateTime();
+
+    String typeZonedDateTime();
 
     String typeLocalDate();
 
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java
index 353a6d5..9439cc3 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Options.java
@@ -100,7 +100,7 @@ public interface Options {
      * This is useful for testing purposes as you can use OptionsOverride to provide your
      * own clock that will be used.
      */
-    boolean useLocalDateTimeOnly();
+    boolean useClientClock();
 
     /**
      * The maximum number of characters to print in debug SQL for a given String type
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java
index 4e6f0fd..a918de5 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsDefault.java
@@ -69,7 +69,7 @@ public class OptionsDefault implements Options {
     }
 
     @Override
-    public boolean useLocalDateTimeOnly() {
+    public boolean useClientClock() {
         return false;
     }
 
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java
index 1f031a0..2b461dc 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/OptionsOverride.java
@@ -96,8 +96,8 @@ public class OptionsOverride implements Options {
     }
 
     @Override
-    public boolean useLocalDateTimeOnly() {
-        return parent.useLocalDateTimeOnly();
+    public boolean useClientClock() {
+        return parent.useClientClock();
     }
 
     @Override
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java
index 002e84a..e3ca351 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Row.java
@@ -4,9 +4,12 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.math.BigDecimal;
 import java.sql.ResultSetMetaData;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 
 /**
  * Interface for reading results from a database query.
@@ -355,15 +358,29 @@ public interface Row {
    
     InputStream getBlobInputStreamOrEmpty(String columnName);
 
+    Instant getInstantOrNull();
+
+    Instant getInstantOrNull(int columnOneBased);
+
+    Instant getInstantOrNull(String columnName);
+
     LocalDateTime getLocalDateTimeOrNull();
 
     LocalDateTime getLocalDateTimeOrNull(int columnOneBased);
 
-    LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId);
-
     LocalDateTime getLocalDateTimeOrNull(String columnName);
 
-    LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId);
+    OffsetDateTime getOffsetDateTimeOrNull();
+
+    OffsetDateTime getOffsetDateTimeOrNull(int columnOneBase);
+
+    OffsetDateTime getOffsetDateTimeOrNull(String columnName);
+
+    ZonedDateTime getZonedDateTimeOrNull();
+
+    ZonedDateTime getZonedDateTimeOrNull(int columnOneBase);
+
+    ZonedDateTime getZonedDateTimeOrNull(String columnName);
 
     /**
      * Retrieve column as LocalDate, .i.e, date with no time.
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdapter.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdapter.java
index b09428a..8851934 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdapter.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/RowsAdapter.java
@@ -5,13 +5,15 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.ZoneId;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 
 /**
  * Safely wrap a ResultSet and provide access to the data it contains.
@@ -20,13 +22,10 @@ class RowsAdapter implements Rows {
 
     private final ResultSet rs;
 
-    private final Options options;
-
     private int column = 1;
 
-    public RowsAdapter(ResultSet rs, Options options) {
+    public RowsAdapter(ResultSet rs) {
         this.rs = rs;
-        this.options = options;
     }
 
     @Override
@@ -39,6 +38,15 @@ class RowsAdapter implements Rows {
         }
     }
 
+    @Override
+    public ResultSetMetaData getMetadata() {
+        try {
+            return rs.getMetaData();
+        } catch (SQLException e) {
+            throw new DatabaseException("Unable to retrieve metadata from ResultSet", e);
+        }
+    }
+
     @Override
     public String[] getColumnLabels() {
         try {
@@ -52,16 +60,7 @@ class RowsAdapter implements Rows {
             throw new DatabaseException("Unable to retrieve metadata from ResultSet", e);
         }
     }
-    
-    @Override
-    public ResultSetMetaData getMetadata() {
-        try {
-            return rs.getMetaData();
-        } catch (SQLException e) {
-            throw new DatabaseException("Unable to retrieve metadata from ResultSet", e);
-        }
-    }
-    
+
     @Override
     public Boolean getBooleanOrNull() {
         return getBooleanOrNull(column++);
@@ -200,7 +199,7 @@ class RowsAdapter implements Rows {
         }
         return result;
     }
-    
+
     @Override
     public Long getLongOrNull() {
         return getLongOrNull(column++);
@@ -411,7 +410,7 @@ class RowsAdapter implements Rows {
         try {
             column = columnOneBased + 1;
             String result = rs.getString(columnOneBased);
-            if (result != null && result.length() == 0) {
+            if (result != null && result.isEmpty()) {
                 result = null;
             }
             return result;
@@ -425,7 +424,7 @@ class RowsAdapter implements Rows {
         try {
             column = rs.findColumn(columnName) + 1;
             String result = rs.getString(columnName);
-            if (result != null && result.length() == 0) {
+            if (result != null && result.isEmpty()) {
                 result = null;
             }
             return result;
@@ -467,7 +466,7 @@ class RowsAdapter implements Rows {
         try {
             column = columnOneBased + 1;
             String result = rs.getString(columnOneBased);
-            if (result != null && result.length() == 0) {
+            if (result != null && result.isEmpty()) {
                 result = null;
             }
             return result;
@@ -481,7 +480,7 @@ class RowsAdapter implements Rows {
         try {
             column = rs.findColumn(columnName) + 1;
             String result = rs.getString(columnName);
-            if (result != null && result.length() == 0) {
+            if (result != null && result.isEmpty()) {
                 result = null;
             }
             return result;
@@ -656,7 +655,32 @@ class RowsAdapter implements Rows {
         }
         return result;
     }
-    
+
+    @Override
+    public Instant getInstantOrNull() {
+        return getInstantOrNull(column++);
+    }
+
+    @Override
+    public Instant getInstantOrNull(int columnOneBased) {
+        try {
+            column = columnOneBased + 1;
+            return rs.getObject(columnOneBased, Instant.class);
+        } catch (SQLException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @Override
+    public Instant getInstantOrNull(String columnName) {
+        try {
+            column = rs.findColumn(columnName) + 1;
+            return rs.getObject(columnName, Instant.class);
+        } catch (SQLException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
     @Override
     public LocalDateTime getLocalDateTimeOrNull() {
         return getLocalDateTimeOrNull(column++);
@@ -672,22 +696,6 @@ class RowsAdapter implements Rows {
         }
     }
 
-    @Override
-    public LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId) {
-        try {
-            column = columnOneBased + 1;
-            LocalDateTime localDateTime = rs.getObject(columnOneBased, LocalDateTime.class);
-            Timestamp timestamp = rs.getTimestamp(columnOneBased);
-            if (zoneId != null) {
-                return localDateTime != null ? localDateTime.atZone(zoneId).toLocalDateTime() : null;
-            } else {
-                return localDateTime;
-            }
-        } catch (SQLException e) {
-            throw new DatabaseException(e);
-        }
-    }
-
     @Override
     public LocalDateTime getLocalDateTimeOrNull(String columnName) {
         try {
@@ -698,17 +706,6 @@ class RowsAdapter implements Rows {
         }
     }
 
-    @Override
-    public LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId) {
-        try {
-            column = rs.findColumn(columnName) + 1;
-            LocalDateTime localDateTime = rs.getObject(columnName, LocalDateTime.class);
-            return localDateTime != null ? localDateTime.atZone(zoneId).toLocalDateTime() : null;
-        } catch (SQLException e) {
-            throw new DatabaseException(e);
-        }
-    }
-
     @Override
     public LocalDate getLocalDateOrNull() {
         return getLocalDateOrNull(column++);
@@ -736,6 +733,56 @@ class RowsAdapter implements Rows {
         }
     }
 
+    @Override
+    public OffsetDateTime getOffsetDateTimeOrNull() {
+        return getOffsetDateTimeOrNull(column++);
+    }
+
+    @Override
+    public OffsetDateTime getOffsetDateTimeOrNull(int columnOneBased) {
+        try {
+            column = columnOneBased + 1;
+            return rs.getObject(columnOneBased, OffsetDateTime.class);
+        } catch (SQLException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @Override
+    public OffsetDateTime getOffsetDateTimeOrNull(String columnName) {
+        try {
+            column = rs.findColumn(columnName) + 1;
+            return rs.getObject(columnName, OffsetDateTime.class);
+        } catch (SQLException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @Override
+    public ZonedDateTime getZonedDateTimeOrNull() {
+        return getZonedDateTimeOrNull(column++);
+    }
+
+    @Override
+    public ZonedDateTime getZonedDateTimeOrNull(int columnOneBased) {
+        try {
+            column = columnOneBased + 1;
+            return rs.getObject(columnOneBased, ZonedDateTime.class);
+        } catch (SQLException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @Override
+    public ZonedDateTime getZonedDateTimeOrNull(String columnName) {
+        try {
+            column = rs.findColumn(columnName) + 1;
+            return rs.getObject(columnName, ZonedDateTime.class);
+        } catch (SQLException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
     @Override
     public Integer rowCount() {
         try {
@@ -750,7 +797,7 @@ class RowsAdapter implements Rows {
         if (val.scale() > 0) {
             val = val.stripTrailingZeros();
             if (val.scale() < 0) {
-                val = val.setScale(0);
+                val = val.setScale(0, RoundingMode.FLOOR);
             }
         }
         return val;
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java
index bafa851..1103d66 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/Schema.java
@@ -141,7 +141,6 @@ public class Schema {
                     // This is the type dates and times with time and time zone associated.
                     // Note that Oracle dates are always really Timestamps.
                     case Types.TIMESTAMP:
-                    case Types.TIMESTAMP_WITH_TIMEZONE:
                         // Check if we really have a LocalDate implemented by the DB as a timestamp
                         if (metadata.getScale(i + 1) == 0) {
                             // If the scale is 0, this is a LocalDate (no time/timezone).
@@ -151,6 +150,9 @@ public class Schema {
                             table.addColumn(names[i]).asLocalDateTime();
                         }
                         break;
+                    case Types.TIMESTAMP_WITH_TIMEZONE:
+                        table.addColumn(names[i]).asOffsetDateTime();
+                        break;
                     case Types.NVARCHAR:
                     case Types.VARCHAR:
                     case Types.LONGVARCHAR:
@@ -227,9 +229,18 @@ public class Schema {
                     case StringFixed:
                         sql.append(flavor.typeStringFixed(column.scale));
                         break;
+                    case Instant:
+                        sql.append(flavor.typeInstant());
+                        break;
                     case LocalDateTime:
                         sql.append(flavor.typeLocalDateTime());
                         break;
+                    case OffsetDateTime:
+                        sql.append(flavor.typeOffsetDateTime());
+                        break;
+                    case ZonedDateTime:
+                        sql.append(flavor.typeZonedDateTime());
+                        break;
                     case LocalDate:
                         sql.append(flavor.typeLocalDate());
                         break;
@@ -395,7 +406,21 @@ public class Schema {
     }
 
     public enum ColumnType {
-        Integer, Long, Float, Double, BigDecimal, StringVar, StringFixed, Clob, Blob, LocalDateTime, LocalDate, Boolean
+        Integer,
+        Long,
+        Float,
+        Double,
+        BigDecimal,
+        StringVar,
+        StringFixed,
+        Clob,
+        Blob,
+        Instant,
+        LocalDateTime,
+        OffsetDateTime,
+        ZonedDateTime,
+        LocalDate,
+        Boolean
     }
 
     public class Sequence {
@@ -902,10 +927,22 @@ public class Schema {
                 return asType(ColumnType.StringFixed);
             }
 
+            public Column asInstant() {
+                return asType(ColumnType.Instant);
+            }
+
             public Column asLocalDateTime() {
                 return asType(ColumnType.LocalDateTime);
             }
 
+            public Column asOffsetDateTime() {
+                return asType(ColumnType.OffsetDateTime);
+            }
+
+            public Column asZonedateTime() {
+                return asType(ColumnType.ZonedDateTime);
+            }
+
             public Column asLocalDate() {
                 return asType(ColumnType.LocalDate);
             }
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java
index 0d68f15..6299380 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsert.java
@@ -3,9 +3,12 @@ package org.xbib.jdbc.query;
 import java.io.InputStream;
 import java.io.Reader;
 import java.math.BigDecimal;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 
 /**
  * Interface for configuring (setting parameters) and executing a chunk of SQL.
@@ -40,12 +43,22 @@ public interface SqlInsert {
 
     SqlInsert argString( String argName, String arg);
 
+    SqlInsert argInstant(Instant arg);
+
+    SqlInsert argInstant(String argName, Instant arg);
+
     SqlInsert argLocalDateTime(LocalDateTime arg);
 
-    SqlInsert argLocalDateTime(LocalDateTime arg, ZoneId zoneId);
-
     SqlInsert argLocalDateTime(String argName, LocalDateTime arg);
 
+    SqlInsert argOffsetDateTime(OffsetDateTime arg);
+
+    SqlInsert argOffsetDateTime(String argName, OffsetDateTime arg);
+
+    SqlInsert argZonedDateTime(ZonedDateTime arg);
+
+    SqlInsert argZonedDateTime(String argName, ZonedDateTime arg);
+
     SqlInsert argLocalDate(LocalDate arg);
 
     SqlInsert argLocalDate( String argName, LocalDate arg);
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java
index 94f1b4a..2621820 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java
@@ -13,9 +13,11 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.Statement;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.ZoneId;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -142,13 +144,18 @@ public class SqlInsertImpl implements SqlInsert {
     }
 
     @Override
-    public SqlInsert argLocalDateTime(LocalDateTime arg) {
-        return positionalArg(adaptor.nullLocalDateTime(arg));
+    public SqlInsert argInstant(Instant arg) {
+        return positionalArg(adaptor.nullInstant(arg));
     }
 
     @Override
-    public SqlInsert argLocalDateTime(LocalDateTime arg, ZoneId zoneId) {
-        return positionalArg(adaptor.nullLocalDateTime(arg, zoneId));
+    public SqlInsert argInstant(String argName, Instant arg) {
+        return namedArg(argName, adaptor.nullInstant(arg));
+    }
+
+    @Override
+    public SqlInsert argLocalDateTime(LocalDateTime arg) {
+        return positionalArg(adaptor.nullLocalDateTime(arg));
     }
 
     @Override
@@ -165,10 +172,30 @@ public class SqlInsertImpl implements SqlInsert {
     public SqlInsert argLocalDate(LocalDate arg) {
         return positionalArg(adaptor.nullLocalDate(arg));
     }
-    
+
+    @Override
+    public SqlInsert argOffsetDateTime(OffsetDateTime arg) {
+        return positionalArg(adaptor.nullOffsetDateTime(arg));
+    }
+
+    @Override
+    public SqlInsert argOffsetDateTime(String argName, OffsetDateTime arg) {
+        return namedArg(argName, adaptor.nullOffsetDateTime(arg));
+    }
+
+    @Override
+    public SqlInsert argZonedDateTime(ZonedDateTime arg) {
+        return positionalArg(adaptor.nullZonedDateTime(arg));
+    }
+
+    @Override
+    public SqlInsert argZonedDateTime(String argName, ZonedDateTime arg) {
+        return namedArg(argName, adaptor.nullZonedDateTime(arg));
+    }
+
     @Override
     public SqlInsert argLocalDateTimeNowPerDb() {
-        if (options.useLocalDateTimeOnly()) {
+        if (options.useClientClock()) {
             return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now()));
         }
         return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
@@ -176,7 +203,7 @@ public class SqlInsertImpl implements SqlInsert {
 
     @Override
     public SqlInsert argLocalDateTimeNowPerDb(String argName) {
-        if (options.useLocalDateTimeOnly()) {
+        if (options.useClientClock()) {
             return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now()));
         }
         return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
@@ -602,7 +629,7 @@ public class SqlInsertImpl implements SqlInsert {
                 }
                 rs = ps.getGeneratedKeys();
                 final ResultSet finalRs = rs;
-                T result = handler.process(new RowsAdapter(finalRs, options));
+                T result = handler.process(new RowsAdapter(finalRs));
                 metric.checkpoint("read");
                 isSuccess = true;
                 return result;
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java
index e97c8a3..9fc950d 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelect.java
@@ -1,9 +1,11 @@
 package org.xbib.jdbc.query;
 
 import java.math.BigDecimal;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.ZoneId;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.List;
 
 /**
@@ -39,13 +41,29 @@ public interface SqlSelect {
 
     SqlSelect argString( String argName, String arg);
 
+    SqlSelect argInstant(Instant arg);
+
+    SqlSelect argInstant(String argName, Instant arg);
+
     SqlSelect argLocalDateTime(LocalDateTime arg);
 
     SqlSelect argLocalDateTime(String argName, LocalDateTime arg);
 
+    SqlSelect argOffsetDateTime(OffsetDateTime arg);
+
+    SqlSelect argOffsetDateTime(String argName, OffsetDateTime arg);
+
+    SqlSelect argZonedDateTime(ZonedDateTime arg);
+
+    SqlSelect argZonedDateTime(String argName, ZonedDateTime arg);
+
     SqlSelect argLocalDate(LocalDate arg);
 
-    SqlSelect argLocalDate( String argName, LocalDate arg);
+    SqlSelect argLocalDate(String argName, LocalDate arg);
+
+    SqlSelect argInstantNowPerDb();
+
+    SqlSelect argInstantNowPerDb(String argName);
 
     SqlSelect argLocalDateTimeNowPerDb();
 
@@ -113,13 +131,17 @@ public interface SqlSelect {
      */
     List<String> queryStrings();
 
-    LocalDateTime queryLocalDateTimeOrNull();
+    Instant queryInstantOrNull();
 
-    LocalDateTime queryLocalDateTimeOrNull(ZoneId zoneId);
+    List<Instant> queryInstants();
+
+    LocalDateTime queryLocalDateTimeOrNull();
 
     List<LocalDateTime> queryLocalDateTimes();
 
-    List<LocalDateTime> queryLocalDateTimes(ZoneId zoneId);
+    ZonedDateTime queryZonedDateTimeOrNull();
+
+    List<ZonedDateTime> queryZonedDateTimes();
 
     LocalDate queryLocalDateOrNull();
     
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java
index ea3267a..91d47ce 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlSelectImpl.java
@@ -9,9 +9,11 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.ZoneId;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -24,7 +26,7 @@ import java.util.logging.Logger;
  */
 public class SqlSelectImpl implements SqlSelect {
     
-    private static final Logger logger = Logger.getLogger(Database.class.getName());
+    private static final Logger logger = Logger.getLogger(SqlSelectImpl.class.getName());
     
     private final Connection connection;
     
@@ -134,7 +136,17 @@ public class SqlSelectImpl implements SqlSelect {
     public SqlSelect argString(String argName, String arg) {
         return namedArg(argName, adaptor.nullString(arg));
     }
-    
+
+    @Override
+    public SqlSelect argInstant(Instant arg) {
+        return positionalArg(adaptor.nullInstant(arg));
+    }
+
+    @Override
+    public SqlSelect argInstant(String argName, Instant arg) {
+        return namedArg(argName, adaptor.nullInstant(arg));
+    }
+
     @Override
     public SqlSelect argLocalDateTime(LocalDateTime arg) {
         return positionalArg(adaptor.nullLocalDateTime(arg));
@@ -144,7 +156,27 @@ public class SqlSelectImpl implements SqlSelect {
     public SqlSelect argLocalDateTime(String argName, LocalDateTime arg) {
         return namedArg(argName, adaptor.nullLocalDateTime(arg));
     }
-    
+
+    @Override
+    public SqlSelect argOffsetDateTime(OffsetDateTime arg) {
+        return positionalArg(adaptor.nullOffsetDateTime(arg));
+    }
+
+    @Override
+    public SqlSelect argOffsetDateTime(String argName, OffsetDateTime arg) {
+        return namedArg(argName, adaptor.nullOffsetDateTime(arg));
+    }
+
+    @Override
+    public SqlSelect argZonedDateTime(ZonedDateTime arg) {
+        return positionalArg(adaptor.nullZonedDateTime(arg));
+    }
+
+    @Override
+    public SqlSelect argZonedDateTime(String argName, ZonedDateTime arg) {
+        return namedArg(argName, adaptor.nullZonedDateTime(arg));
+    }
+
     @Override
     public SqlSelect argLocalDate(LocalDate arg) {
         return positionalArg(adaptor.nullLocalDate(arg));
@@ -155,9 +187,25 @@ public class SqlSelectImpl implements SqlSelect {
         return namedArg(argName, adaptor.nullLocalDate(arg));
     }
 
+    @Override
+    public SqlSelect argInstantNowPerDb() {
+        if (options.useClientClock()) {
+            return positionalArg(adaptor.nullInstant(Instant.now()));
+        }
+        return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
+    }
+
+    @Override
+    public SqlSelect argInstantNowPerDb(String argName) {
+        if (options.useClientClock()) {
+            return namedArg(argName, adaptor.nullInstant(Instant.now()));
+        }
+        return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
+    }
+
     @Override
     public SqlSelect argLocalDateTimeNowPerDb() {
-        if (options.useLocalDateTimeOnly()) {
+        if (options.useClientClock()) {
             return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now()));
         }
         return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
@@ -165,7 +213,7 @@ public class SqlSelectImpl implements SqlSelect {
     
     @Override
     public SqlSelect argLocalDateTimeNowPerDb(String argName) {
-        if (options.useLocalDateTimeOnly()) {
+        if (options.useClientClock()) {
             return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now()));
         }
         return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
@@ -429,17 +477,37 @@ public class SqlSelectImpl implements SqlSelect {
             return result;
         });
     }
-    
+
     @Override
-    public LocalDateTime queryLocalDateTimeOrNull() {
-        return queryLocalDateTimeOrNull(null);
+    public Instant queryInstantOrNull() {
+        return queryWithTimeout(rs -> {
+            if (rs.next()) {
+                return rs.getInstantOrNull(1);
+            }
+            return null;
+        });
     }
 
     @Override
-    public LocalDateTime queryLocalDateTimeOrNull(ZoneId zoneId) {
+    public List<Instant> queryInstants() {
+        return queryWithTimeout(rs -> {
+            List<Instant> result = new ArrayList<>();
+            while (rs.next()) {
+                Instant value = rs.getInstantOrNull(1);
+                if (value != null) {
+                    result.add(value);
+                }
+            }
+            return result;
+        });
+    }
+
+
+    @Override
+    public LocalDateTime queryLocalDateTimeOrNull() {
         return queryWithTimeout(rs -> {
             if (rs.next()) {
-                return rs.getLocalDateTimeOrNull(1, zoneId);
+                return rs.getLocalDateTimeOrNull(1);
             }
             return null;
         });
@@ -447,15 +515,34 @@ public class SqlSelectImpl implements SqlSelect {
 
     @Override
     public List<LocalDateTime> queryLocalDateTimes() {
-        return queryLocalDateTimes(null);
-    }
-
-    @Override
-    public List<LocalDateTime> queryLocalDateTimes(ZoneId zoneId) {
         return queryWithTimeout(rs -> {
             List<LocalDateTime> result = new ArrayList<>();
             while (rs.next()) {
-                LocalDateTime value = rs.getLocalDateTimeOrNull(1, zoneId);
+                LocalDateTime value = rs.getLocalDateTimeOrNull(1);
+                if (value != null) {
+                    result.add(value);
+                }
+            }
+            return result;
+        });
+    }
+
+    @Override
+    public ZonedDateTime queryZonedDateTimeOrNull() {
+        return queryWithTimeout(rs -> {
+            if (rs.next()) {
+                return rs.getZonedDateTimeOrNull(1);
+            }
+            return null;
+        });
+    }
+
+    @Override
+    public List<ZonedDateTime> queryZonedDateTimes() {
+        return queryWithTimeout(rs -> {
+            List<ZonedDateTime> result = new ArrayList<>();
+            while (rs.next()) {
+                ZonedDateTime value = rs.getZonedDateTimeOrNull(1);
                 if (value != null) {
                     result.add(value);
                 }
@@ -605,9 +692,9 @@ public class SqlSelectImpl implements SqlSelect {
                 adaptor.addParameters(ps, parameters);
                 metric.checkpoint("prep");
                 rs = ps.executeQuery();
+                final ResultSet resultSet = rs;
                 metric.checkpoint("exec");
-                final ResultSet finalRs = rs;
-                T result = handler.process(new RowsAdapter(finalRs, options));
+                T result = handler.process(new RowsAdapter(resultSet));
                 metric.checkpoint("read");
                 isSuccess = true;
                 return result;
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java
index 2512072..f1bfe12 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java
@@ -142,7 +142,7 @@ public class SqlUpdateImpl implements SqlUpdate {
 
     @Override
     public SqlUpdate argLocalDateTimeNowPerDb() {
-        if (options.useLocalDateTimeOnly()) {
+        if (options.useClientClock()) {
             return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now()));
         }
         return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
@@ -150,7 +150,7 @@ public class SqlUpdateImpl implements SqlUpdate {
 
     @Override
     public SqlUpdate argLocalDateTimeNowPerDb(String argName) {
-        if (options.useLocalDateTimeOnly()) {
+        if (options.useClientClock()) {
             return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now()));
         }
         return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdapter.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdapter.java
index f7109c2..40a8f98 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdapter.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/StatementAdapter.java
@@ -11,9 +11,13 @@ import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Timestamp;
 import java.sql.Types;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.List;
 import java.util.logging.Level;
@@ -25,8 +29,6 @@ import java.util.stream.Collectors;
  */
 public class StatementAdapter {
 
-    private static final Logger logger = Logger.getLogger(StatementAdapter.class.getName());
-
     private final Options options;
 
     public StatementAdapter(Options options) {
@@ -53,8 +55,7 @@ public class StatementAdapter {
                             + " or use SqlNull in place of null values to this query.", e);
                 }
                 ps.setNull(i + 1, parameterType);
-            } else if (parameter instanceof SqlNull) {
-                SqlNull sqlNull = (SqlNull) parameter;
+            } else if (parameter instanceof SqlNull sqlNull) {
                 if (options.useBytesForBlob() && sqlNull.getType() == Types.BLOB) {
                     // The setNull() seems more correct, but PostgreSQL chokes on it
                     ps.setBytes(i + 1, null);
@@ -63,13 +64,9 @@ public class StatementAdapter {
                 }
             } else if (parameter instanceof java.sql.Date) {
                 ps.setDate(i + 1, (java.sql.Date) parameter);
-            } else if (parameter instanceof Date) {
-                Date date = (Date) parameter;
+            } else if (parameter instanceof Date date) {
                 LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
                 ps.setTimestamp(i + 1, Timestamp.valueOf(localDateTime));
-                // this will correct the millis and nanos according to the JDBC spec
-                // if a correct Timestamp is passed in, this will detect that and leave it alone
-                //ps.setTimestamp(i + 1, toSqlTimestamp((Date) parameter), options.calendarForTimestamps());
             } else if (parameter instanceof Reader) {
                 if (options.useStringForClob()) {
                     try (BufferedReader reader = new BufferedReader((Reader) parameter)) {
@@ -100,12 +97,20 @@ public class StatementAdapter {
         }
     }
 
+    public Object nullInstant(Instant arg) {
+        return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.atOffset(ZoneOffset.UTC).toLocalDateTime());
+    }
+
     public Object nullLocalDateTime(LocalDateTime arg) {
         return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg);
     }
 
-    public Object nullLocalDateTime(LocalDateTime arg, ZoneId zoneId) {
-        return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.atZone(zoneId).toLocalDateTime());
+    public Object nullZonedDateTime(ZonedDateTime arg) {
+        return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.toLocalDateTime());
+    }
+
+    public Object nullOffsetDateTime(OffsetDateTime arg) {
+        return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.toLocalDateTime());
     }
 
     public Object nullLocalDate(LocalDate arg) {
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java
index 50f05ff..9628aa6 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java
@@ -95,14 +95,24 @@ public class Derby implements Flavor {
         return "blob";
     }
 
+    @Override
+    public String typeInstant() {
+        return "timestamp";
+    }
+
     @Override
     public String typeLocalDateTime() {
         return "timestamp";
     }
 
     @Override
-    public String columnTypeLocalDateTime() {
-        return "timestamp";
+    public String typeOffsetDateTime() {
+        return "timestamptz";
+    }
+
+    @Override
+    public String typeZonedDateTime() {
+        return "timestamptz";
     }
 
     @Override
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java
index 352e74f..9897b1b 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/H2.java
@@ -95,14 +95,24 @@ public class H2 implements Flavor {
         return "blob(2G)";
     }
 
+    @Override
+    public String typeInstant() {
+        return "timestamp";
+    }
+
     @Override
     public String typeLocalDateTime() {
         return "timestamp(3)";
     }
 
     @Override
-    public String columnTypeLocalDateTime() {
-        return "timestamp";
+    public String typeOffsetDateTime() {
+        return "timestamptz";
+    }
+
+    @Override
+    public String typeZonedDateTime() {
+        return "timestamptz";
     }
 
     @Override
diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java
index e05de29..84b0196 100644
--- a/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java
+++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java
@@ -95,14 +95,24 @@ public class Hsql implements Flavor {
         return "blob(2G)";
     }
 
+    @Override
+    public String typeInstant() {
+        return "timestamp";
+    }
+
     @Override
     public String typeLocalDateTime() {
+        return "timestamp";
+    }
+
+    @Override
+    public String typeOffsetDateTime() {
         return "timestamp with time zone";
     }
 
     @Override
-    public String columnTypeLocalDateTime() {
-        return "TIMESTAMP WITH TIME ZONE";
+    public String typeZonedDateTime() {
+        return "timestamp with time zone";
     }
 
     @Override
diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java
index 73b565e..075a03c 100644
--- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java
+++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/HsqldbTest.java
@@ -1,6 +1,5 @@
 package org.xbib.jdbc.query.test;
 
-import java.util.logging.Logger;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.xbib.jdbc.query.Config;
@@ -24,15 +23,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
  */
 public class HsqldbTest extends CommonTest {
 
-    private static final Logger logger = Logger.getLogger(HsqldbTest.class.getName());
-
     @Override
-    protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
+    protected DatabaseProvider createDatabaseProvider(OptionsOverride options) {
         String propertiesFile = "hsqldb.properties";
         Config config = ConfigSupplier.of()
                 .properties(getClass().getResourceAsStream(propertiesFile))
                 .get();
-        return DatabaseProvider.builder(config)
+        return DatabaseProvider.builder(getClass().getClassLoader(),
+                        config.getString("database.url"),
+                        null,
+                        null,
+                        config.getString("database.user"),
+                        config.getString("database.password"))
                 .withSqlParameterLogging()
                 .withSqlInExceptionMessages()
                 .withOptions(options)
@@ -79,7 +81,7 @@ public class HsqldbTest extends CommonTest {
                 .argClobString("hello again")
                 .argBlobBytes(new byte[]{'1', '2'})
                 .argBoolean(true)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow)
                 .insert(1);
 
@@ -95,7 +97,7 @@ public class HsqldbTest extends CommonTest {
                 .argClobString("bye again")
                 .argBlobBytes(new byte[]{'3', '4'})
                 .argBoolean(false)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow)
                 .insert(1);
 
@@ -150,7 +152,7 @@ public class HsqldbTest extends CommonTest {
                                 .argClobString("str_lob", "bye again")
                                 .argBlobBytes("bin_blob", new byte[]{'3', '4'})
                                 .argString("boolean_flag", "N")//.argBoolean("boolean_flag", false)
-                                .argLocalDateTime("date_millis", now)
+                                .argLocalDateTime("date_millis", localDateTimeNow)
                                 .argLocalDate("local_date", localDateNow),
                         new SqlArgs()
                                 .argInteger("nbr_integer", Integer.MAX_VALUE)
@@ -163,7 +165,7 @@ public class HsqldbTest extends CommonTest {
                                 .argClobString("str_lob", "hello again")
                                 .argBlobBytes("bin_blob", new byte[]{'1', '2'})
                                 .argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true)
-                                .argLocalDateTime("date_millis", now)
+                                .argLocalDateTime("date_millis", localDateTimeNow)
                                 .argLocalDate("local_date", localDateNow)),
                 db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal,"
                                 + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1")
diff --git a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java
index 0023d2b..09b7b32 100644
--- a/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java
+++ b/jdbc-query/src/test/java/org/xbib/jdbc/query/test/RowStub.java
@@ -10,9 +10,12 @@ import java.io.StringReader;
 import java.math.BigDecimal;
 import java.sql.ResultSetMetaData;
 import java.sql.Types;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
@@ -578,6 +581,23 @@ public class RowStub {
                 return new ByteArrayInputStream(getBlobBytesOrZeroLen(columnName));
             }
 
+            @Override
+            public Instant getInstantOrNull() {
+                return toInstant(rows.get(row)[++col]);
+            }
+
+            @Override
+            public Instant getInstantOrNull(int columnOneBased) {
+                col = columnOneBased;
+                return toInstant(rows.get(row)[columnOneBased - 1]);
+            }
+
+            @Override
+            public Instant getInstantOrNull(String columnName) {
+                col = columnIndexByName(columnName) + 1;
+                return toInstant(rows.get(row)[columnIndexByName(columnName)]);
+            }
+
             @Override
             public LocalDateTime getLocalDateTimeOrNull() {
                 return toLocalDateTime(rows.get(row)[++col]);
@@ -589,12 +609,6 @@ public class RowStub {
                 return toLocalDateTime(rows.get(row)[columnOneBased - 1]);
             }
 
-            @Override
-            public LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId) {
-                col = columnOneBased;
-                return toLocalDateTime(rows.get(row)[columnOneBased - 1], zoneId);
-            }
-
             @Override
             public LocalDateTime getLocalDateTimeOrNull(String columnName) {
                 col = columnIndexByName(columnName) + 1;
@@ -602,36 +616,50 @@ public class RowStub {
             }
 
             @Override
-            public LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId) {
-                return null;
+            public OffsetDateTime getOffsetDateTimeOrNull() {
+                return toOffsetDateTime(rows.get(row)[++col]);
             }
 
-            /**
-             * Returns a java.time.LocalDate.  It will have no timezone or other time data.
-             * If you require time, use the Date APIs instead.
-             */
+            @Override
+            public OffsetDateTime getOffsetDateTimeOrNull(int columnOneBased) {
+                col = columnOneBased;
+                return toOffsetDateTime(rows.get(row)[columnOneBased - 1]);
+            }
+
+            @Override
+            public OffsetDateTime getOffsetDateTimeOrNull(String columnName) {
+                col = columnIndexByName(columnName) + 1;
+                return toOffsetDateTime(rows.get(row)[columnIndexByName(columnName)]);
+            }
+
+            @Override
+            public ZonedDateTime getZonedDateTimeOrNull() {
+                return toZonedDateTime(rows.get(row)[++col]);
+            }
+
+            @Override
+            public ZonedDateTime getZonedDateTimeOrNull(int columnOneBased) {
+                col = columnOneBased;
+                return toZonedDateTime(rows.get(row)[columnOneBased - 1]);
+            }
+
+            @Override
+            public ZonedDateTime getZonedDateTimeOrNull(String columnName) {
+                col = columnIndexByName(columnName) + 1;
+                return toZonedDateTime(rows.get(row)[columnIndexByName(columnName)]);
+            }
 
             @Override
             public LocalDate getLocalDateOrNull() {
                 return toLocalDate(rows.get(row)[++col]);
             }
 
-            /**
-             * Returns a java.time.LocalDate.  It will have no timezone or other time data.
-             * If you require time, use the Date APIs instead.
-             */
-
             @Override
             public LocalDate getLocalDateOrNull(int columnOneBased) {
                 col = columnOneBased;
                 return toLocalDate(rows.get(row)[columnOneBased - 1]);
             }
 
-            /**
-             * Returns a java.time.LocalDate.  It will have no timezone or other time data.
-             * If you require time, use the Date APIs instead.
-             */
-
             @Override
             public LocalDate getLocalDateOrNull(String columnName) {
                 col = columnIndexByName(columnName) + 1;
@@ -707,32 +735,60 @@ public class RowStub {
                 return (BigDecimal) o;
             }
 
-            private LocalDateTime toLocalDateTime(Object o) {
-                return toLocalDateTime(o, ZoneId.systemDefault());
+            private Instant toInstant(Object o) {
+                if (o instanceof String s) {
+                    if (s.length() == "yyyy-MM-dd".length()) {
+                        return Instant.parse(s);
+                    }
+                    if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
+                        return Instant.parse(s);
+                    }
+                    throw new DatabaseException("Didn't understand date string: " + s);
+                }
+                return (Instant) o;
             }
 
-            private LocalDateTime toLocalDateTime(Object o, ZoneId zoneId) {
-                if (o instanceof String) {
-                    String s = (String) o;
+            private LocalDateTime toLocalDateTime(Object o) {
+                if (o instanceof String s) {
                     if (s.length() == "yyyy-MM-dd".length()) {
                         return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
-                                .atZone(zoneId).toLocalDateTime();
+                                .atZone(ZoneId.systemDefault()).toLocalDateTime();
                     }
                     if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
                         return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"))
-                                .atZone(zoneId).toLocalDateTime();
+                                .atZone(ZoneId.systemDefault()).toLocalDateTime();
                     }
                     throw new DatabaseException("Didn't understand date string: " + s);
                 }
                 return (LocalDateTime) o;
             }
 
-            /**
-             * Returns a LocalDate (no time).
-             * If the object is a String, it should be in ISO 8601 format.
-             *
-             * @return a LocalDate representation of the object
-             */
+            private OffsetDateTime toOffsetDateTime(Object o) {
+                if (o instanceof String s) {
+                    if (s.length() == "yyyy-MM-dd".length()) {
+                        return OffsetDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                    }
+                    if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
+                        return OffsetDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"));
+                    }
+                    throw new DatabaseException("Didn't understand date string: " + s);
+                }
+                return (OffsetDateTime) o;
+            }
+
+            private ZonedDateTime toZonedDateTime(Object o) {
+                if (o instanceof String s) {
+                    if (s.length() == "yyyy-MM-dd".length()) {
+                        return ZonedDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                    }
+                    if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
+                        return ZonedDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"));
+                    }
+                    throw new DatabaseException("Didn't understand date string: " + s);
+                }
+                return (ZonedDateTime) o;
+            }
+
             private LocalDate toLocalDate(Object o) {
                 if (o instanceof String) {
                     return LocalDate.parse((String) o);
diff --git a/jdbc-sqlserver/build.gradle b/jdbc-sqlserver/build.gradle
deleted file mode 100644
index 749572c..0000000
--- a/jdbc-sqlserver/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencies {
-    api project(':jdbc-query')
-    testImplementation project(':jdbc-test')
-    testImplementation testLibs.testcontainers
-    testImplementation testLibs.testcontainers.junit.jupiter
-}
-
-test {
-    systemProperty 'user.timezone', 'GMT'
-}
diff --git a/jdbc-sqlserver/src/main/java/module-info.java b/jdbc-sqlserver/src/main/java/module-info.java
deleted file mode 100644
index e3555d2..0000000
--- a/jdbc-sqlserver/src/main/java/module-info.java
+++ /dev/null
@@ -1,10 +0,0 @@
-import org.xbib.jdbc.query.Flavor;
-import org.xbib.jdbc.sqlserver.SqlServer;
-
-module org.xbib.jdbc.sqlserver {
-    requires org.xbib.jdbc.query;
-    requires java.sql;
-    uses Flavor;
-    exports org.xbib.jdbc.sqlserver;
-    provides Flavor with SqlServer;
-}
diff --git a/jdbc-sqlserver/src/main/java/org/xbib/jdbc/sqlserver/SqlServer.java b/jdbc-sqlserver/src/main/java/org/xbib/jdbc/sqlserver/SqlServer.java
deleted file mode 100644
index 2d47540..0000000
--- a/jdbc-sqlserver/src/main/java/org/xbib/jdbc/sqlserver/SqlServer.java
+++ /dev/null
@@ -1,214 +0,0 @@
-package org.xbib.jdbc.sqlserver;
-
-import org.xbib.jdbc.query.Flavor;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-public class SqlServer implements Flavor {
-
-    public SqlServer() {
-    }
-
-    @Override
-    public String getName() {
-        return "sqlServer";
-    }
-
-    @Override
-    public boolean supports(String url) {
-        return url.startsWith("jdbc:sqlserver:");
-    }
-
-    @Override
-    public String driverClass() {
-        return "com.microsoft.sqlserver.jdbc.SQLServerDriver";
-    }
-
-    @Override
-    public String normalizeTableName(String tableName) {
-        if (tableName == null) {
-            return tableName;
-        }
-        if (tableName.length() > 2) {
-            if (tableName.startsWith("\"") && tableName.endsWith("\"")) {
-                return tableName.substring(1, tableName.length() - 1);
-            }
-        }
-        return tableName;
-    }
-
-    @Override
-    public String typeFloat() {
-        return "float(24)";
-    }
-
-    @Override
-    public void setFloat(PreparedStatement preparedStatement, int i, Float floatValue) throws SQLException {
-        preparedStatement.setFloat(i, floatValue);
-    }
-
-    @Override
-    public String typeDouble() {
-        return "float(53)";
-    }
-
-    @Override
-    public void setDouble(PreparedStatement preparedStatement, int i, Double doubleValue) throws SQLException {
-        preparedStatement.setDouble(i, doubleValue);
-    }
-
-    @Override
-    public String typeBigDecimal(int size, int precision) {
-        return "numeric(" + size + "," + precision + ")";
-    }
-
-    @Override
-    public String typeInteger() {
-        return "numeric(10)";
-    }
-
-    @Override
-    public String typeBoolean() {
-        return "char(1)";
-    }
-
-    @Override
-    public String typeLong() {
-        return "numeric(19)";
-    }
-
-    @Override
-    public String typeLocalDateTime() {
-        return "datetime2(3)";
-    }
-
-    @Override
-    public String columnTypeLocalDateTime() {
-        return "datetime2";
-    }
-
-    @Override
-    public String typeLocalDate() {
-        return "date";
-    }
-
-    @Override
-    public boolean useStringForClob() {
-        return false;
-    }
-
-    @Override
-    public boolean useBytesForBlob() {
-        return false;
-    }
-
-    @Override
-    public String sequenceNextVal(String sequenceName) {
-        return "next value for " + sequenceName;
-    }
-
-    @Override
-    public String sequenceSelectNextVal(String sequenceName) {
-        return "select next value for " + sequenceName;
-    }
-
-    @Override
-    public String sequenceDrop(String dbtestSeq) {
-        return "drop sequence " + dbtestSeq;
-    }
-
-    @Override
-    public String tableDrop(String table) {
-        return "drop table " + table;
-    }
-
-    @Override
-    public String typeStringVar(int length) {
-        return "varchar(" + length + ")";
-    }
-
-    @Override
-    public String typeStringFixed(int length) {
-        return "char(" + length + ")";
-    }
-
-    @Override
-    public String typeClob() {
-        return "varchar(max)";
-    }
-
-    @Override
-    public String typeBlob() {
-        return "varbinary(max)";
-    }
-
-    @Override
-    public String sequenceOrderClause(boolean order) {
-        // Not supported
-        return "";
-    }
-
-    @Override
-    public String sequenceCycleClause(boolean cycle) {
-        return cycle ? " cycle" : " no cycle";
-    }
-
-    @Override
-    public boolean supportsInsertReturning() {
-        return true;
-    }
-
-    @Override
-    public String dbTimeMillis() {
-        return "current_timestamp";
-    }
-
-    @Override
-    public String sequenceCacheClause(int nbrValuesToCache) {
-        if (nbrValuesToCache < 2) {
-            return " no cache";
-        }
-        return " cache " + nbrValuesToCache;
-    }
-
-    @Override
-    public String fromAny() {
-        return "";
-    }
-
-    @Override
-    public String sequenceOptions() {
-        return "";
-    }
-
-    @Override
-    public boolean isAutoCommitOnly() {
-        return false;
-    }
-
-    @Override
-    public String queueLength(String table) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String writeNextIntoQueue(String table) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String readNextFromQueue(String table) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String succeedInQueue(String table) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String failInQueue(String table) {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/jdbc-sqlserver/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor b/jdbc-sqlserver/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor
deleted file mode 100644
index a1e1dec..0000000
--- a/jdbc-sqlserver/src/main/resources/META-INF/services/org.xbib.jdbc.query.Flavor
+++ /dev/null
@@ -1 +0,0 @@
-org.xbib.jdbc.sqlserver.SqlServer
\ No newline at end of file
diff --git a/jdbc-sqlserver/src/test/java/org/xbib/jdbc/sqlserver/test/SqlServerTest.java b/jdbc-sqlserver/src/test/java/org/xbib/jdbc/sqlserver/test/SqlServerTest.java
deleted file mode 100644
index a8e28d9..0000000
--- a/jdbc-sqlserver/src/test/java/org/xbib/jdbc/sqlserver/test/SqlServerTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package org.xbib.jdbc.sqlserver.test;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.xbib.jdbc.query.Config;
-import org.xbib.jdbc.query.ConfigSupplier;
-import org.xbib.jdbc.query.DatabaseProvider;
-import org.xbib.jdbc.query.OptionsOverride;
-import org.xbib.jdbc.query.Schema;
-import org.xbib.jdbc.test.CommonTest;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-
-/**
- * Exercise Database functionality with a real SQL server database.
- */
-@Disabled
-public class SqlServerTest extends CommonTest {
-    @Override
-    protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
-        String propertiesFile = System.getProperty("local.properties", "local.properties");
-        Config config = ConfigSupplier.of()
-                .systemProperties()
-                .properties(propertiesFile)
-                .excludePrefix("database.")
-                .removePrefix("sqlserver.")
-                .get();
-        return DatabaseProvider.builder(config)
-                .withSqlParameterLogging()
-                .withSqlInExceptionMessages()
-                .withOptions(options).build();
-    }
-
-    @Disabled("SQL Server prohibits NaN and Infinity")
-    @Test
-    public void argFloatNaN() {
-        super.argFloatNaN();
-    }
-
-    @Disabled("SQL Server prohibits NaN and Infinity")
-    @Test
-    public void argFloatInfinity() {
-        super.argFloatInfinity();
-    }
-
-    @Disabled("SQL Server prohibits NaN and Infinity")
-    @Test
-    public void argDoubleNaN() {
-        super.argDoubleNaN();
-    }
-
-    @Disabled("SQL Server prohibits NaN and Infinity")
-    @Test
-    public void argDoubleInfinity() {
-        super.argDoubleInfinity();
-    }
-
-    @Disabled("SQL Server seems to have incorrect min value for float (rounds to zero)")
-    @Test
-    public void argFloatMinMax() {
-        super.argFloatMinMax();
-    }
-
-    @Disabled("SQL Server doesn't support the interval syntax for date arithmetic")
-    @Test
-    public void intervals() {
-        super.intervals();
-    }
-
-    /**
-     * SQL Server seems to have different behavior in that is does not convert
-     * column names to uppercase (it preserves the case).
-     * I haven't figured out how to smooth over this difference, since all databases
-     * seem to respect the provided case when it is inside quotes, but don't provide
-     * a way to tell whether a particular parameter was quoted.
-     */
-    @Override
-    @Test
-    public void metadataColumnNames() {
-        db.dropTableQuietly("dbtest");
-        new Schema().addTable("dbtest").addColumn("pk").primaryKey().schema().execute(db);
-        db.toSelect("select Pk, Pk as Foo, Pk as \"Foo\" from dbtest").query(rs -> {
-            assertArrayEquals(new String[]{"Pk", "Foo", "Foo"}, rs.getColumnLabels());
-            return null;
-        });
-    }
-}
diff --git a/jdbc-sqlserver/src/test/resources/logging.properties b/jdbc-sqlserver/src/test/resources/logging.properties
deleted file mode 100644
index 5885e4d..0000000
--- a/jdbc-sqlserver/src/test/resources/logging.properties
+++ /dev/null
@@ -1,10 +0,0 @@
-handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
-.level=ALL
-java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] %5$s %6$s%n
-java.util.logging.ConsoleHandler.level=ALL
-java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
-java.util.logging.FileHandler.level=ALL
-java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
-java.util.logging.FileHandler.pattern=build/database.log
-jdk.event.security.level=INFO
-javax.management.level=INFO
diff --git a/jdbc-test/src/main/java/org/xbib/jdbc/test/CommonTest.java b/jdbc-test/src/main/java/org/xbib/jdbc/test/CommonTest.java
index 6c5d084..1195c53 100644
--- a/jdbc-test/src/main/java/org/xbib/jdbc/test/CommonTest.java
+++ b/jdbc-test/src/main/java/org/xbib/jdbc/test/CommonTest.java
@@ -29,8 +29,10 @@ import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.Month;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
@@ -58,29 +60,33 @@ public abstract class CommonTest {
 
     private static final Logger logger = Logger.getLogger(CommonTest.class.getName());
 
-    final static String TEST_TABLE_NAME = "dbtest";
-
     protected static DatabaseProvider dbp;
 
     protected Database db;
 
-    protected LocalDateTime now;
+    protected LocalDateTime localDateTimeNow;
 
     protected LocalDate localDateNow;
 
+    protected OffsetDateTime offsetDateTimeNow;
+
+    protected ZonedDateTime zonedDateTimeNow;
+
     protected abstract DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception;
 
     @BeforeEach
     public void setupJdbc() throws Exception {
-        now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
+        localDateTimeNow = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
         localDateNow = LocalDate.now();
+        offsetDateTimeNow = OffsetDateTime.now();
+        zonedDateTimeNow = ZonedDateTime.now();
         dbp = createDatabaseProvider(new OptionsOverride() {
         });
         db = dbp.get();
         if (!db.probe(15)) {
             fail();
         }
-        db.dropTableQuietly(TEST_TABLE_NAME);
+        db.dropTableQuietly("dbtest");
     }
 
     @AfterEach
@@ -99,12 +105,12 @@ public abstract class CommonTest {
 
     @Test
     public void tableExists() {
-        // Verify dbtest table does not exist
-        String lowercaseTable = TEST_TABLE_NAME.toLowerCase();
+        // Verify table does not exist
+        String lowercaseTable = "dbtest";
         testTableLookup(lowercaseTable);
         db.dropTableQuietly(lowercaseTable);
         // Let's try creating a table with an upper case name and verify it works
-        String uppercaseTable = TEST_TABLE_NAME.toUpperCase();
+        String uppercaseTable = "DBTEST";
         testTableLookup(uppercaseTable);
         db.dropTableQuietly(uppercaseTable);
         // Verify that null or empty name is handled gracefully
@@ -127,8 +133,8 @@ public abstract class CommonTest {
         String camelCaseTableName = "\"DbTest\"";
         assertEquals(camelCaseTableName.substring(1, camelCaseTableName.length() - 1),
                 db.flavor().normalizeTableName(camelCaseTableName));
-        assertEquals(TEST_TABLE_NAME.toUpperCase(Locale.ENGLISH),
-                db.flavor().normalizeTableName(TEST_TABLE_NAME).toUpperCase(Locale.ENGLISH));
+        assertEquals("DBTEST",
+                db.flavor().normalizeTableName("dbtest").toUpperCase(Locale.ENGLISH));
     }
 
     @Test
@@ -145,13 +151,29 @@ public abstract class CommonTest {
                 .addColumn("str_lob").asClob().table()
                 .addColumn("bin_blob").asBlob().table()
                 .addColumn("date_millis").asLocalDateTime().table()
-                .addColumn("local_date").asLocalDate().table().schema().execute(db);
+                .addColumn("local_date").asLocalDate().table()
+                .addColumn("offset_date_time").asOffsetDateTime().table()
+                .addColumn("tz_date_time").asZonedateTime().table()
+                .schema()
+                .execute(db);
         BigDecimal bigDecimal = new BigDecimal("5.3");
-        db.toInsert("insert into dbtest values (?,?,?,?,?,?,?,?,?,?,?)").argInteger(1).argLong(2L).argFloat(3.2f).argDouble(4.2)
-                .argBigDecimal(bigDecimal).argString("Hello").argString("T").argClobString("World")
-                .argBlobBytes("More".getBytes()).argLocalDateTime(now).argLocalDate(localDateNow).insert(1);
+        db.toInsert("insert into dbtest values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
+                .argInteger(1)
+                .argLong(2L)
+                .argFloat(3.2f)
+                .argDouble(4.2)
+                .argBigDecimal(bigDecimal)
+                .argString("Hello")
+                .argString("T")
+                .argClobString("World")
+                .argBlobBytes("More".getBytes())
+                .argLocalDateTime(localDateTimeNow)
+                .argLocalDate(localDateNow)
+                .argOffsetDateTime(offsetDateTimeNow)
+                .argZonedDateTime(zonedDateTimeNow)
+                .insert(1);
         db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
-                        + "bin_blob, date_millis, local_date from dbtest")
+                        + "bin_blob, date_millis, local_date, offset_date_time, tz_date_time from dbtest")
                 .query((RowsHandler<Void>) rs -> {
                             assertTrue(rs.next());
                             assertEquals(Integer.valueOf(1), rs.getIntegerOrNull(1));
@@ -190,15 +212,22 @@ public abstract class CommonTest {
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob"));
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen(9));
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen("bin_blob"));
-                            assertEquals(now, rs.getLocalDateTimeOrNull(10));
-                            assertEquals(now, rs.getLocalDateTimeOrNull("date_millis"));
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull(10));
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull("date_millis"));
                             assertEquals(localDateNow, rs.getLocalDateOrNull(11));
                             assertEquals(localDateNow, rs.getLocalDateOrNull("local_date"));
+                            assertEquals(offsetDateTimeNow.truncatedTo(ChronoUnit.SECONDS),
+                                    rs.getOffsetDateTimeOrNull(12).truncatedTo(ChronoUnit.SECONDS));
+                            assertEquals(offsetDateTimeNow.truncatedTo(ChronoUnit.SECONDS),
+                                    rs.getOffsetDateTimeOrNull("offset_date_time").truncatedTo(ChronoUnit.SECONDS));
+                            // org.postgresql.util.PSQLException: conversion to class java.time.ZonedDateTime from timestamptz not supported
+                            // assertEquals(zonedDateTimeNow, rs.getZonedDateTimeOrNull(13));
+                            // assertEquals(zonedDateTimeNow, rs.getZonedDateTimeOrNull("tz_date_time"));
                             return null;
                         });
         // Repeat the above query, using the various methods that automatically infer the column
         db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
-                        + "bin_blob, date_millis, local_date from dbtest")
+                        + "bin_blob, date_millis, local_date, offset_date_time, tz_date_time from dbtest")
                 .query((RowsHandler<Void>) rs -> {
                     assertTrue(rs.next());
                     assertEquals(Integer.valueOf(1), rs.getIntegerOrNull());
@@ -210,12 +239,15 @@ public abstract class CommonTest {
                     assertEquals("T", rs.getStringOrNull());
                     assertEquals("World", rs.getClobStringOrNull());
                     assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull());
-                    assertEquals(now, rs.getLocalDateTimeOrNull());
+                    assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull());
                     assertEquals(localDateNow, rs.getLocalDateOrNull());
+                    assertEquals(offsetDateTimeNow.truncatedTo(ChronoUnit.SECONDS), rs.getOffsetDateTimeOrNull().truncatedTo(ChronoUnit.SECONDS));
+                    // org.postgresql.util.PSQLException: conversion to class java.time.ZonedDateTime from timestamptz not supported
+                    //assertEquals(zonedDateTimeNow, rs.getZonedDateTimeOrNull());
                     return null;
                 });
         db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
-                        + "bin_blob, date_millis, local_date from dbtest")
+                        + "bin_blob, date_millis, local_date, offset_date_time, tz_date_time from dbtest")
                 .query((RowsHandler<Void>) rs -> {
                             assertTrue(rs.next());
                             assertEquals(1, rs.getIntegerOrZero());
@@ -227,6 +259,11 @@ public abstract class CommonTest {
                             assertEquals("T", rs.getStringOrEmpty());
                             assertEquals("World", rs.getClobStringOrEmpty());
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrZeroLen());
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull());
+                            assertEquals(localDateNow, rs.getLocalDateOrNull());
+                            assertEquals(offsetDateTimeNow.truncatedTo(ChronoUnit.SECONDS), rs.getOffsetDateTimeOrNull().truncatedTo(ChronoUnit.SECONDS));
+                            // org.postgresql.util.PSQLException: conversion to class java.time.ZonedDateTime from timestamptz not supported
+                            //assertEquals(zonedDateTimeNow, rs.getZonedDateTimeOrNull());
                             return null;
                         });
         db.toSelect("select str_lob, bin_blob from dbtest").query((RowsHandler<Void>) rs -> {
@@ -275,16 +312,16 @@ public abstract class CommonTest {
                 .argBigDecimal("bd", bigDecimal)
                 .argString("s", "Hello")
                 .argString("sf", "T")
-                .argLocalDateTime("date", now)
+                .argLocalDateTime("date", localDateTimeNow)
                 .argLocalDate("local_date", localDateNow)
                 .queryLongOrNull());
         List<Long> result = db.toSelect("select count(*) from dbtest where nbr_integer=:i and nbr_long=:l and "
                         + "abs(nbr_float-:f)<0.01 and abs(nbr_double-:d)<0.01 and nbr_big_decimal=:bd and str_varchar=:s "
                         + "and str_fixed=:sf and date_millis=:date and local_date=:local_date").argInteger("i", 1).argLong("l", 2L).argFloat("f", 3.2f)
                 .argDouble("d", 4.2).argBigDecimal("bd", bigDecimal).argString("s", "Hello").argString("sf", "T")
-                .argLocalDateTime("date", now).argLocalDate("local_date", localDateNow).queryLongs();
+                .argLocalDateTime("date", localDateTimeNow).argLocalDate("local_date", localDateNow).queryLongs();
         assertEquals(1, result.size());
-        assertEquals(Long.valueOf(1), result.get(0));
+        assertEquals(Long.valueOf(1), result.getFirst());
     }
 
     @Test
@@ -316,7 +353,7 @@ public abstract class CommonTest {
                 .argString("T")
                 .argClobString("World")
                 .argBlobBytes("More".getBytes())
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow).insert());
         db.toUpdate("update dbtest set nbr_integer=?, nbr_long=?, nbr_float=?, nbr_double=?, nbr_big_decimal=?, "
                         + "str_varchar=?, str_fixed=?, str_lob=?, bin_blob=?, date_millis=?, local_date=?").argInteger(null).argLong(null)
@@ -352,7 +389,7 @@ public abstract class CommonTest {
         assertEquals(1, db.toUpdate("update dbtest set nbr_integer=?, nbr_long=?, nbr_float=?, nbr_double=?, "
                         + "nbr_big_decimal=?, str_varchar=?, str_fixed=?, str_lob=?, bin_blob=?, date_millis=?, local_date=?").argInteger(1)
                 .argLong(2L).argFloat(3.2f).argDouble(4.2).argBigDecimal(bigDecimal).argString("Hello").argString("T")
-                .argClobString("World").argBlobBytes("More".getBytes()).argLocalDateTime(now).argLocalDate(localDateNow).update());
+                .argClobString("World").argBlobBytes("More".getBytes()).argLocalDateTime(localDateTimeNow).argLocalDate(localDateNow).update());
         db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
                         + "bin_blob, date_millis, local_date from dbtest").query((RowsHandler<Void>) rs -> {
                             assertTrue(rs.next());
@@ -374,8 +411,8 @@ public abstract class CommonTest {
                             assertEquals("World", rs.getClobStringOrNull("str_lob"));
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull(9));
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob"));
-                            assertEquals(now, rs.getLocalDateTimeOrNull(10));
-                            assertEquals(now, rs.getLocalDateTimeOrNull("date_millis"));
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull(10));
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull("date_millis"));
                             assertEquals(localDateNow, rs.getLocalDateOrNull(11));
                             assertEquals(localDateNow, rs.getLocalDateOrNull("local_date"));
                             return null;
@@ -422,7 +459,7 @@ public abstract class CommonTest {
                 .argLong(":b", 2L).argFloat(":c", 3.2f).argDouble(":d", 4.2).argBigDecimal(":e", bigDecimal)
                 .argString(":f", "Hello").argString(":sf", "T")
                 .argClobString(":g", "World").argBlobBytes(":h", "More".getBytes())
-                .argLocalDateTime(":i", now).argLocalDate(":j", localDateNow).insert(1);
+                .argLocalDateTime(":i", localDateTimeNow).argLocalDate(":j", localDateNow).insert(1);
         db.toUpdate("update dbtest set nbr_integer=:a, nbr_long=:b, nbr_float=:c, nbr_double=:d, nbr_big_decimal=:e, "
                         + "str_varchar=:f, str_fixed=:sf, str_lob=:g, bin_blob=:h, date_millis=:i, local_date=:j").argInteger(":a", null)
                 .argLong(":b", null).argFloat(":c", null).argDouble(":d", null).argBigDecimal(":e", null)
@@ -461,7 +498,7 @@ public abstract class CommonTest {
                 .argLong(":b", 2L).argFloat(":c", 3.2f).argDouble(":d", 4.2).argBigDecimal(":e", bigDecimal)
                 .argString(":f", "Hello").argString(":sf", "T")
                 .argClobString(":g", "World").argBlobBytes(":h", "More".getBytes())
-                .argLocalDateTime(":i", now).argLocalDate(":j", localDateNow).update(1);
+                .argLocalDateTime(":i", localDateTimeNow).argLocalDate(":j", localDateNow).update(1);
         db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
                         + "bin_blob, date_millis, local_date from dbtest")
                 .query((RowsHandler<Void>) rs -> {
@@ -484,8 +521,8 @@ public abstract class CommonTest {
                             assertEquals("World", rs.getClobStringOrNull("str_lob"));
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull(9));
                             assertArrayEquals("More".getBytes(), rs.getBlobBytesOrNull("bin_blob"));
-                            assertEquals(now, rs.getLocalDateTimeOrNull(10));
-                            assertEquals(now, rs.getLocalDateTimeOrNull("date_millis"));
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull(10));
+                            assertEquals(localDateTimeNow, rs.getLocalDateTimeOrNull("date_millis"));
                             assertEquals(localDateNow, rs.getLocalDateOrNull(11));
                             assertEquals(localDateNow, rs.getLocalDateOrNull("local_date"));
                             return null;
@@ -595,16 +632,17 @@ public abstract class CommonTest {
                 .addTable("dbtest")
                 .addColumn(timestampColumnName).asLocalDateTime().table()
                 .addColumn(dateColumnName).asLocalDate().table()
-                .schema().execute(db);
+                .schema()
+                .execute(db);
         db.toSelect("select * from dbtest").query((RowsHandler<Void>) rs -> {
             ResultSetMetaData metadata = rs.getMetadata();
             for (int i = 1; i <= metadata.getColumnCount(); i++) {
                 String columnName = metadata.getColumnName(i);
-                String columnType = metadata.getColumnTypeName(i);
+                String columnType = metadata.getColumnTypeName(i).toUpperCase(Locale.ROOT);
                 if (columnName.equalsIgnoreCase(timestampColumnName)) {
-                    assertEquals(db.flavor().columnTypeLocalDateTime(), columnType);
+                    assertEquals("TIMESTAMP", columnType);
                 } else if (columnName.equalsIgnoreCase(dateColumnName)) {
-                    assertEquals("DATE", columnType.toUpperCase());
+                    assertEquals("DATE", columnType);
                 } else {
                     fail("Unexpected column " + columnName + " of type " + columnType);
                 }
@@ -616,10 +654,10 @@ public abstract class CommonTest {
     @Test
     public void intervals() {
         new Schema().addTable("dbtest").addColumn("d").asLocalDateTime().schema().execute(db);
-        db.toInsert("insert into dbtest (d) values (?)").argLocalDateTime(now).insert(1);
+        db.toInsert("insert into dbtest (d) values (?)").argLocalDateTime(localDateTimeNow).insert(1);
         assertEquals(1, db.toSelect("select count(1) from dbtest where d - interval '1' hour * ? < ?")
                 .argInteger(2)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .queryIntegerOrZero());
     }
 
@@ -644,7 +682,7 @@ public abstract class CommonTest {
                 .argDouble(Double.MAX_VALUE).argBigDecimal(new BigDecimal("123.456"))
                 .argString("hello").argString("Z").argClobString("hello again")
                 .argBlobBytes(new byte[]{'1', '2'}).argBoolean(true)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow).insert(1);
         db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar,"
                         + " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)")
@@ -652,7 +690,7 @@ public abstract class CommonTest {
                 .argDouble(Double.MIN_VALUE).argBigDecimal(new BigDecimal("-123.456"))
                 .argString("goodbye").argString("A").argClobString("bye again")
                 .argBlobBytes(new byte[]{'3', '4'}).argBoolean(false)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow).insert(1);
         String expectedSchema = new Schema().addTable("dbtest2")
                 .addColumn("nbr_integer").asInteger().table()
@@ -707,7 +745,7 @@ public abstract class CommonTest {
                                 .argClobString("str_lob", "bye again")
                                 .argBlobBytes("bin_blob", new byte[]{'3', '4'})
                                 .argString("boolean_flag", "N")//.argBoolean("boolean_flag", false)
-                                .argLocalDateTime("date_millis", now)
+                                .argLocalDateTime("date_millis", localDateTimeNow)
                                 .argLocalDate("local_date", localDateNow),
                         new SqlArgs().argInteger("nbr_integer", Integer.MAX_VALUE)
                                 .argLong("nbr_long", Long.MAX_VALUE)
@@ -719,7 +757,7 @@ public abstract class CommonTest {
                                 .argClobString("str_lob", "hello again")
                                 .argBlobBytes("bin_blob", new byte[]{'1', '2'})
                                 .argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true)
-                                .argLocalDateTime("date_millis", now)
+                                .argLocalDateTime("date_millis", localDateTimeNow)
                                 .argLocalDate("local_date", localDateNow)),
                 db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal,"
                                 + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1")
@@ -1318,7 +1356,7 @@ public abstract class CommonTest {
         new Schema().addTable("dbtest").addColumn("i").asBigDecimal(38, 38).schema().execute(db);
         BigDecimal value = new BigDecimal("0.99999999999999999999999999999999999999"); // 38 digits
         db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
-        logger.info(db.toSelect("select i from dbtest").queryBigDecimalOrNull().toString());
+        String s = db.toSelect("select i from dbtest").queryBigDecimalOrNull().toString();
         assertEquals(value,
                 db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
     }
@@ -1367,15 +1405,15 @@ public abstract class CommonTest {
                 .execute(db);
         db.toInsert("insert into dbtest (pk, d) values (:seq, :d)")
                 .argPkSeq(":seq", "dbtest_seq")
-                .argLocalDateTime(":d", now)
+                .argLocalDateTime(":d", localDateTimeNow)
                 .insertReturning("dbtest", "pk", rs -> {
                     assertTrue(rs.next());
                     assertEquals(Long.valueOf(1L), rs.getLongOrNull(1));
-                    assertThat(rs.getLocalDateTimeOrNull(2, ZoneId.systemDefault()), equalTo(now));
+                    assertThat(rs.getLocalDateTimeOrNull(2), equalTo(localDateTimeNow));
                     assertFalse(rs.next());
                     return null;
                 }, "d");
-        assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argLocalDateTime(now).queryLongOrNull());
+        assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argLocalDateTime(localDateTimeNow).queryLongOrNull());
     }
 
     @Test
@@ -1393,7 +1431,7 @@ public abstract class CommonTest {
                 .execute(db);
         db.toInsert("insert into dbtest (pk, d, d3, s) values (?,?,?,?)")
                 .argLong(1L)
-                .argLocalDateTime(now)
+                .argLocalDateTime(localDateTimeNow)
                 .argLocalDate(localDateNow)
                 .argString("foo")
                 .insert(1);
@@ -1424,10 +1462,10 @@ public abstract class CommonTest {
         assertEquals("foo", db.toSelect("select s from dbtest").queryStrings().get(0));
         assertTrue(db.toSelect("select s from dbtest where 1=0").queryStrings().isEmpty());
         assertTrue(db.toSelect("select s2 from dbtest").queryStrings().isEmpty());
-        assertEquals(now, db.toSelect("select d from dbtest").queryLocalDateTimeOrNull());
+        assertEquals(localDateTimeNow, db.toSelect("select d from dbtest").queryLocalDateTimeOrNull());
         assertNull(db.toSelect("select d from dbtest where 1=0").queryLocalDateTimeOrNull());
         assertNull(db.toSelect("select d2 from dbtest").queryLocalDateTimeOrNull());
-        assertEquals(db.toSelect("select d from dbtest").queryLocalDateTimes().get(0), now);
+        assertEquals(db.toSelect("select d from dbtest").queryLocalDateTimes().get(0), localDateTimeNow);
         assertTrue(db.toSelect("select d from dbtest where 1=0").queryLocalDateTimes().isEmpty());
         assertTrue(db.toSelect("select d2 from dbtest").queryLocalDateTimes().isEmpty());
 
@@ -1570,27 +1608,32 @@ public abstract class CommonTest {
         for (int attempts = 1; attempts <= 10; attempts++) {
             new Schema()
                     .addTable("dbtest")
-                    .addColumn("d").asLocalDateTime().table().schema()
+                    .addColumn("d").asLocalDateTime()
+                    .table()
+                    .schema()
                     .execute(db);
             db.toInsert("insert into dbtest (d) values (?)")
                     .argLocalDateTimeNowPerDb()
                     .insert(1);
             LocalDateTime dbNow = db.toSelect("select d from dbtest").queryLocalDateTimeOrNull();
             if (dbNow != null && dbNow.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() % 10 != 0) {
+                // done
                 return;
             }
-            logger.info("Zero in least significant digit (attempt " + attempts + ")");
-            db.dropTableQuietly(TEST_TABLE_NAME);
+            logger.log(Level.INFO, "Zero in least significant digit (attempt " + attempts + ")");
+            db.dropTableQuietly("dbtest");
         }
-        fail("Timestamp had zero in the least significant digit");
+        fail("unable to match dateMillis");
     }
 
     @Test
     public void dateRoundTrip() {
         new Schema()
                 .addTable("dbtest")
-                .addColumn("d1").asLocalDateTime().table()
-                .addColumn("d2").asLocalDateTime().table()
+                .addColumn("d1").asLocalDateTime()
+                .table()
+                .addColumn("d2").asLocalDateTime()
+                .table()
                 .schema()
                 .execute(db);
         db.toInsert("insert into dbtest (d1) values (?)")
@@ -1600,15 +1643,14 @@ public abstract class CommonTest {
         db.toUpdate("update dbtest set d2 = ?")
                 .argLocalDateTime(dbNow)
                 .update(1);
-        assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d1=d2").queryLongOrNull());
+        assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d1 = d2").queryLongOrNull());
     }
 
     @Test
     public void dateRoundTripTimezones() {
         new Schema()
                 .addTable("dbtest")
-                .addColumn("d")
-                .asLocalDateTime()
+                .addColumn("d").asLocalDateTime()
                 .table()
                 .schema()
                 .execute(db);
@@ -1617,7 +1659,6 @@ public abstract class CommonTest {
         LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, dateMinusZone);
         ZoneId datePlusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4));
         LocalDateTime datePlus = LocalDateTime.ofInstant(instant,datePlusZone);
-        logger.log(Level.INFO, "dateMinus = " + dateMinus);
         db.toInsert("insert into dbtest (d) values (?)")
                 .argLocalDateTime(dateMinus)
                 .insert(1);
@@ -1631,7 +1672,7 @@ public abstract class CommonTest {
                 .argLocalDateTime(datePlus)
                 .insert(1);
         LocalDateTime localDateTimePlus = db.toSelect("select d from dbtest")
-                .queryLocalDateTimeOrNull(datePlusZone);
+                .queryLocalDateTimeOrNull();
         assertEquals(datePlus, localDateTimePlus);
         assertEquals("1970-01-03 02:17:36.789",
                 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimePlus));
@@ -1651,7 +1692,6 @@ public abstract class CommonTest {
         LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, dateMinusZone);
         ZoneId datePlusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4));
         LocalDateTime datePlus = LocalDateTime.ofInstant(instant, datePlusZone);
-        logger.info("LocalDateTime: dateMinus=" + dateMinus + " datePlus=" + datePlus);
         new Schema().addTable("dbtest").addColumn("d")
                 .asLocalDateTime()
                 .schema()
@@ -1670,7 +1710,7 @@ public abstract class CommonTest {
                 .argLocalDateTime(datePlus)
                 .insert(1);
         localDateTime = db.toSelect("select d from dbtest")
-                .queryLocalDateTimeOrNull(datePlusZone);
+                .queryLocalDateTimeOrNull();
         assertEquals("1970-01-03 02:17:36.789",
                 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTime.atZone(datePlusZone)));
         db.toDelete("delete from dbtest where d = ?")
diff --git a/settings.gradle b/settings.gradle
index 97b4ef6..4a79649 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,18 +2,18 @@ dependencyResolutionManagement {
     versionCatalogs {
         libs {
             version('gradle', '8.7')
-            library('mariadb', 'org.mariadb.jdbc', 'mariadb-java-client').version('3.4.1')
-            library('oracle', 'com.oracle.database.jdbc', 'ojdbc11').version('23.4.0.24.05')
-            library('postgresql', 'org.postgresql', 'postgresql').version('42.7.3')
+            library('mariadb', 'org.mariadb.jdbc', 'mariadb-java-client').version('3.5.2')
+            library('oracle', 'com.oracle.database.jdbc', 'ojdbc11').version('23.7.0.25.01')
+            library('postgresql', 'org.postgresql', 'postgresql').version('42.7.5')
         }
         testLibs {
-            version('junit', '5.10.2')
+            version('junit', '5.12.0')
             version('testcontainers', '1.20.0')
             library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
             library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit')
             library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit')
-            library('junit-jupiter-platform-launcher', 'org.junit.platform', 'junit-platform-launcher').version('1.10.1')
-            library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
+            library('junit-jupiter-platform-launcher', 'org.junit.platform', 'junit-platform-launcher').version('1.12.0')
+            library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('3.0')
             library('junit4', 'junit', 'junit').version('4.13.2')
             library('derby', 'org.apache.derby', 'derby').version('10.17.1.0')
             library('hsqldb', 'org.hsqldb', 'hsqldb').version('2.7.3')
@@ -35,4 +35,3 @@ include 'jdbc-test'
 include 'jdbc-mariadb'
 include 'jdbc-oracle'
 include 'jdbc-postgresql'
-include 'jdbc-sqlserver'