diff --git a/ebean-test/src/test/java/org/tests/timezone/LocalTimeTest.java b/ebean-test/src/test/java/org/tests/timezone/LocalTimeTest.java
deleted file mode 100644
index 6f5b7e0f91..0000000000
--- a/ebean-test/src/test/java/org/tests/timezone/LocalTimeTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.tests.timezone;
-
-import io.ebean.Database;
-import io.ebean.DatabaseFactory;
-import io.ebean.config.DatabaseConfig;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-
-import java.time.LocalTime;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class LocalTimeTest {
-
- protected String platform="h2";
- protected Database db;
-
- @BeforeAll
- public void startTest() {
- db = createServer("GMT"); // test uses GMT database
- }
-
- @AfterAll
- public void shutdown() {
- if (db != null) {
- db.find(MLocalTime.class).delete();
- db.shutdown();
- }
- }
-
- /**
- * The test checks the write and read of LocalTime values. The database is in GMT time zone.
- * In order to verify the test in different java time zones (where the application runs),
- * use the -Duser.timezone as JVM argument,
- * e.g. -Duser.timezone="America/New_York" or -Duser.timezone="PST">
- * or any other timezone: https://garygregory.wordpress.com/2013/06/18/what-are-the-java-timezone-ids/.
- */
- @Test
- public void testLocalTime() {
- LocalTime lt = LocalTime.of(5, 15, 15);
- assertThat(db.find(MLocalTime.class).findCount()).isEqualTo(0);
- db.sqlUpdate("insert into mlocal_time (id, local_time) values (1, '05:15:15')").execute();
-
- int count = db.find(MLocalTime.class).where().eq("local_time", lt).findCount();
- assertThat(count).isEqualTo(1);
-
- MLocalTime dbModel = db.find(MLocalTime.class).where().eq("local_time", lt).findOne();
- assertThat(dbModel.getLocalTime().toString()).isEqualTo(lt.toString());
- }
-
- private Database createServer(String dbTimeZone) {
- DatabaseConfig config = new DatabaseConfig();
- config.setName(platform);
- config.loadFromProperties();
- config.setDdlExtra(false);
- config.setDefaultServer(false);
- config.setRegister(false);
- config.setChangeLogAsync(false);
- config.addClass(MLocalTime.class);
-
- config.setDumpMetricsOnShutdown(false);
- config.setDataTimeZone(dbTimeZone);
-
- return DatabaseFactory.create(config);
- }
-}
diff --git a/ebean-test/src/test/java/org/tests/timezone/MLocalTime.java b/ebean-test/src/test/java/org/tests/timezone/MLocalTime.java
deleted file mode 100644
index 4a2d303137..0000000000
--- a/ebean-test/src/test/java/org/tests/timezone/MLocalTime.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.tests.timezone;
-
-import javax.annotation.Nullable;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import java.time.LocalTime;
-
-@Entity
-public class MLocalTime {
-
- @Id
- private Integer id;
-
- @Nullable
- private LocalTime localTime;
-
- @Nullable
- public LocalTime getLocalTime() {
- return localTime;
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
-
-}
diff --git a/ebean-test/src/test/java/org/tests/timezone/MTimeTest.java b/ebean-test/src/test/java/org/tests/timezone/MTimeTest.java
new file mode 100644
index 0000000000..238806a250
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/timezone/MTimeTest.java
@@ -0,0 +1,83 @@
+package org.tests.timezone;
+
+import javax.annotation.Nullable;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+/**
+ * Test model with various time types.
+ */
+@Entity
+public class MTimeTest {
+
+ @Id
+ private Integer id;
+ @Nullable
+ private Instant instant;
+ @Nullable
+ private LocalDate localDate;
+ @Nullable
+ private LocalTime localTime;
+ @Nullable
+ private LocalDateTime localDateTime;
+ @Nullable
+ private Timestamp timestamp;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Nullable
+ public Instant getInstant() {
+ return instant;
+ }
+
+ public void setInstant(@Nullable Instant instant) {
+ this.instant = instant;
+ }
+
+ @Nullable
+ public LocalDate getLocalDate() {
+ return localDate;
+ }
+
+ public void setLocalDate(@Nullable LocalDate localDate) {
+ this.localDate = localDate;
+ }
+
+ @Nullable
+ public LocalTime getLocalTime() {
+ return localTime;
+ }
+
+ public void setLocalTime(@Nullable LocalTime localTime) {
+ this.localTime = localTime;
+ }
+
+ @Nullable
+ public LocalDateTime getLocalDateTime() {
+ return localDateTime;
+ }
+
+ public void setLocalDateTime(@Nullable LocalDateTime localDateTime) {
+ this.localDateTime = localDateTime;
+ }
+
+ @Nullable
+ public Timestamp getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(@Nullable Timestamp timestamp) {
+ this.timestamp = timestamp;
+ }
+}
diff --git a/ebean-test/src/test/java/org/tests/timezone/TimeTypesTest.java b/ebean-test/src/test/java/org/tests/timezone/TimeTypesTest.java
new file mode 100644
index 0000000000..ab3dbba8ed
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/timezone/TimeTypesTest.java
@@ -0,0 +1,184 @@
+package org.tests.timezone;
+
+import io.ebean.Database;
+import io.ebean.DatabaseFactory;
+import io.ebean.config.DatabaseConfig;
+import io.ebean.xtest.BaseTestCase;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.*;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class TimeTypesTest extends BaseTestCase {
+
+ protected Database dbGmt;
+ protected Database dbEurope;
+ protected Database dbPST;
+
+ @BeforeAll
+ public void startTest() {
+ // both DBs are connected to the same datasource
+ dbGmt = createServer("GMT");
+ dbEurope = createServer("Europe/Berlin");
+ dbPST = createServer("PST");
+ }
+
+ @AfterAll
+ public void shutdown() {
+ if (dbGmt != null) {
+ dbGmt.shutdown();
+ }
+ if (dbEurope != null) {
+ dbEurope.shutdown();
+ }
+ if (dbPST != null) {
+ dbPST.shutdown();
+ }
+ }
+
+ @BeforeEach
+ public void clearDb() {
+ dbGmt.find(MTimeTest.class).delete();
+ }
+
+ /**
+ * Test for instant.
+ */
+ @Test
+ public void testInstant() {
+
+ dbGmt.sqlUpdate("insert into mtime_test (id, instant) values (1, '2021-11-21 05:15:15')").execute();
+
+ Instant inst = Instant.parse("2021-11-21T05:15:15Z");
+ testTimeType(MTimeTest.class, "instant", MTimeTest::getInstant, inst);
+
+ assertThat(dbEurope.find(MTimeTest.class).findOne().getInstant()) // we get 04:15:15/GMT here, as this is equal to 05:15:15/Europe
+ .isEqualTo(inst.minusSeconds(3600));
+
+ assertThat(dbPST.find(MTimeTest.class).findOne().getInstant())
+ .isEqualTo(inst.plusSeconds(8 * 3600));
+ }
+
+ @Test
+ public void testLocalDate() {
+ dbGmt.sqlUpdate("insert into mtime_test (id, local_date) values (1, '2021-11-21')").execute();
+
+ LocalDate ld = LocalDate.parse("2021-11-21");
+ testTimeType(MTimeTest.class, "local_date", MTimeTest::getLocalDate, ld);
+
+ assertThat(dbEurope.find(MTimeTest.class).findOne().getLocalDate()).isEqualTo(ld);
+
+ assertThat(dbPST.find(MTimeTest.class).findOne().getLocalDate()).isEqualTo(ld);
+ }
+
+ @Test
+ public void testLocalDateTime() {
+ dbGmt.sqlUpdate("insert into mtime_test (id, local_date_time) values (1, '2021-11-21 05:15:15')").execute();
+
+ LocalDateTime ldt = LocalDateTime.parse("2021-11-21T05:15:15");
+
+ // Expected:
+ // testTimeType(MTimeTest.class, "local_date_time", MTimeTest::getLocalDateTime, ldt);
+ // assertThat(dbEurope.find(MTimeTest.class).findOne().getLocalDateTime()).isEqualTo(ldt);
+ // assertThat(dbPST.find(MTimeTest.class).findOne().getLocalDateTime()).isEqualTo(ldt);
+
+ // Actual (if machine runs in Europe/Berlin):
+ // This is something I would not expect, or at least has to be discussed
+ // A LocalDateTime mapped to a DB column should IMHO be never adjusted
+ // Arguments: A LocalDateTime = LocalDate + LocalTime. Having these types in separate columns, they are also not adjusted
+ assertThat(dbGmt.find(MTimeTest.class).findOne().getLocalDateTime()).isEqualTo(ldt.plusSeconds(3600));
+ assertThat(dbEurope.find(MTimeTest.class).findOne().getLocalDateTime()).isEqualTo(ldt);
+ assertThat(dbPST.find(MTimeTest.class).findOne().getLocalDateTime()).isEqualTo(ldt.plusSeconds(9 * 3600));
+
+ }
+
+ @Test
+ public void testLocalTime() {
+ dbGmt.sqlUpdate("insert into mtime_test (id, local_time) values (1, '05:15:15')").execute();
+
+ LocalTime lt = LocalTime.of(5, 15, 15);
+
+ testTimeType(MTimeTest.class, "local_time", MTimeTest::getLocalTime, lt);
+
+ assertThat(dbEurope.find(MTimeTest.class).findOne().getLocalTime()).isEqualTo(lt);
+
+ assertThat(dbPST.find(MTimeTest.class).findOne().getLocalTime()).isEqualTo(lt);
+
+ }
+
+ @Test
+ public void testTimestamp() {
+ dbGmt.sqlUpdate("insert into mtime_test (id, timestamp) values (1, '2021-11-21 05:15:15')").execute();
+
+ Timestamp ts = Timestamp.from(Instant.parse("2021-11-21T05:15:15Z"));
+
+ testTimeType(MTimeTest.class, "timestamp", MTimeTest::getTimestamp, ts);
+
+
+ assertThat(dbEurope.find(MTimeTest.class).findOne().getTimestamp()).isEqualTo(new Timestamp(ts.getTime() - 3600_000));
+
+ assertThat(dbPST.find(MTimeTest.class).findOne().getTimestamp()).isEqualTo(new Timestamp(ts.getTime() + 8 * 3600_000));
+ }
+
+ /**
+ * This checks for each datatype, if it can be read properly from DB.
+ *
refeq(field, ref)eq(field, ref)in(field, ref)