diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index 5a9cba69083..284e2c561fb 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -60,6 +60,7 @@ body:
- Solace
- Solr
- TiDB
+ - Timeplus
- ToxiProxy
- Trino
- Vault
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml
index c6fb9767f60..2ca7de6bc3c 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.yaml
+++ b/.github/ISSUE_TEMPLATE/enhancement.yaml
@@ -60,6 +60,7 @@ body:
- Solace
- Solr
- TiDB
+ - Timeplus
- ToxiProxy
- Trino
- Vault
diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml
index 485e1477a06..311ec8ca2c6 100644
--- a/.github/ISSUE_TEMPLATE/feature.yaml
+++ b/.github/ISSUE_TEMPLATE/feature.yaml
@@ -60,6 +60,7 @@ body:
- Solace
- Solr
- TiDB
+ - Timeplus
- ToxiProxy
- Trino
- Vault
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6bdca459fab..0239be10168 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -342,6 +342,11 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
+ - package-ecosystem: "gradle"
+ directory: "/modules/timeplus"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/toxiproxy"
schedule:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index f449a11685d..875e996c428 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -216,6 +216,10 @@
- changed-files:
- any-glob-to-any-file:
- modules/tidb/**/*
+"modules/timeplus":
+ - changed-files:
+ - any-glob-to-any-file:
+ - modules/timeplus/**/*
"modules/toxiproxy":
- changed-files:
- any-glob-to-any-file:
diff --git a/.github/settings.yml b/.github/settings.yml
index 2bd2289af79..06b1c06e72f 100644
--- a/.github/settings.yml
+++ b/.github/settings.yml
@@ -253,6 +253,9 @@ labels:
- name: modules/tidb
color: '#006b75'
+ - name: modules/timeplus
+ color: '#006b75'
+
- name: modules/toxiproxy
color: '#006b75'
diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md
index 3c92e8a99ad..90183228bd5 100644
--- a/docs/modules/databases/jdbc.md
+++ b/docs/modules/databases/jdbc.md
@@ -87,6 +87,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database
`jdbc:tc:tidb:v6.1.0:///databasename`
+#### Using Timeplus
+
+`jdbc:tc:timeplus:2.3.21:///databasename`
+
#### Using Trino
`jdbc:tc:trino:352://localhost/memory/default`
diff --git a/docs/modules/databases/timeplus.md b/docs/modules/databases/timeplus.md
new file mode 100644
index 00000000000..4f5cd4106a3
--- /dev/null
+++ b/docs/modules/databases/timeplus.md
@@ -0,0 +1,24 @@
+# Timeplus Module
+
+## Adding this module to your project dependencies
+
+Add the following dependency to your `pom.xml`/`build.gradle` file:
+
+=== "Gradle"
+ ```groovy
+ testImplementation "org.testcontainers:timeplus:{{latest_version}}"
+ ```
+
+=== "Maven"
+ ```xml
+
+ org.testcontainers
+ timeplus
+ {{latest_version}}
+ test
+
+ ```
+
+!!! hint
+ Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency.
+
diff --git a/mkdocs.yml b/mkdocs.yml
index 8aed24d20f6..2e1c60c7dcc 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -72,6 +72,7 @@ nav:
- modules/databases/presto.md
- modules/databases/questdb.md
- modules/databases/tidb.md
+ - modules/databases/timeplus.md
- modules/databases/trino.md
- modules/databases/yugabytedb.md
- modules/activemq.md
diff --git a/modules/timeplus/build.gradle b/modules/timeplus/build.gradle
new file mode 100644
index 00000000000..74570d821c2
--- /dev/null
+++ b/modules/timeplus/build.gradle
@@ -0,0 +1,10 @@
+description = "Testcontainers :: JDBC :: Timeplus"
+
+dependencies {
+ api project(':testcontainers')
+ api project(':jdbc')
+
+ testImplementation project(':jdbc-test')
+ testRuntimeOnly 'com.timeplus:timeplus-native-jdbc:2.0.4'
+ testImplementation 'org.assertj:assertj-core:3.26.3'
+}
diff --git a/modules/timeplus/src/main/java/org/testcontainers/timeplus/TimeplusContainer.java b/modules/timeplus/src/main/java/org/testcontainers/timeplus/TimeplusContainer.java
new file mode 100644
index 00000000000..0ba28c8f10a
--- /dev/null
+++ b/modules/timeplus/src/main/java/org/testcontainers/timeplus/TimeplusContainer.java
@@ -0,0 +1,125 @@
+package org.testcontainers.timeplus;
+
+import org.testcontainers.containers.JdbcDatabaseContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Testcontainers implementation for Timeplus.
+ *
+ * Supported image: {@code timeplus/timeplusd}
+ *
+ * Exposed ports:
+ *
+ * - Database: 8463
+ * - HTTP: 3218
+ *
+ */
+public class TimeplusContainer extends JdbcDatabaseContainer {
+
+ static final String NAME = "timeplus";
+
+ static final String DOCKER_IMAGE_NAME = "timeplus/timeplusd";
+
+ private static final DockerImageName TIMEPLUS_IMAGE_NAME = DockerImageName.parse(DOCKER_IMAGE_NAME);
+
+ private static final Integer HTTP_PORT = 3218;
+
+ private static final Integer NATIVE_PORT = 8463;
+
+ private static final String DRIVER_CLASS_NAME = "com.timeplus.jdbc.TimeplusDriver";
+
+ private static final String JDBC_URL_PREFIX = "jdbc:" + NAME + "://";
+
+ private static final String TEST_QUERY = "SELECT 1";
+
+ private String databaseName = "default";
+
+ private String username = "default";
+
+ private String password = "";
+
+ public TimeplusContainer(String dockerImageName) {
+ this(DockerImageName.parse(dockerImageName));
+ }
+
+ public TimeplusContainer(final DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(TIMEPLUS_IMAGE_NAME);
+
+ addExposedPorts(HTTP_PORT, NATIVE_PORT);
+ waitingFor(Wait.forHttp("/timeplusd/v1/ping").forStatusCode(200).withStartupTimeout(Duration.ofMinutes(1)));
+ }
+
+ @Override
+ protected void configure() {
+ withEnv("TIMEPLUS_DB", this.databaseName);
+ withEnv("TIMEPLUS_USER", this.username);
+ withEnv("TIMEPLUS_PASSWORD", this.password);
+ }
+
+ @Override
+ public Set getLivenessCheckPortNumbers() {
+ return new HashSet<>(getMappedPort(HTTP_PORT));
+ }
+
+ @Override
+ public String getDriverClassName() {
+ return DRIVER_CLASS_NAME;
+ }
+
+ @Override
+ public String getJdbcUrl() {
+ return (
+ JDBC_URL_PREFIX +
+ getHost() +
+ ":" +
+ getMappedPort(NATIVE_PORT) +
+ "/" +
+ this.databaseName +
+ constructUrlParameters("?", "&")
+ );
+ }
+
+ @Override
+ public String getUsername() {
+ return this.username;
+ }
+
+ @Override
+ public String getPassword() {
+ return this.password;
+ }
+
+ @Override
+ public String getDatabaseName() {
+ return this.databaseName;
+ }
+
+ @Override
+ public String getTestQueryString() {
+ return TEST_QUERY;
+ }
+
+ @Override
+ public TimeplusContainer withUsername(String username) {
+ this.username = username;
+ return this;
+ }
+
+ @Override
+ public TimeplusContainer withPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ @Override
+ public TimeplusContainer withDatabaseName(String databaseName) {
+ this.databaseName = databaseName;
+ return this;
+ }
+}
diff --git a/modules/timeplus/src/main/java/org/testcontainers/timeplus/TimeplusContainerProvider.java b/modules/timeplus/src/main/java/org/testcontainers/timeplus/TimeplusContainerProvider.java
new file mode 100644
index 00000000000..1d71e654a45
--- /dev/null
+++ b/modules/timeplus/src/main/java/org/testcontainers/timeplus/TimeplusContainerProvider.java
@@ -0,0 +1,32 @@
+package org.testcontainers.timeplus;
+
+import org.testcontainers.containers.JdbcDatabaseContainer;
+import org.testcontainers.containers.JdbcDatabaseContainerProvider;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * Factory for Timeplus containers.
+ */
+public class TimeplusContainerProvider extends JdbcDatabaseContainerProvider {
+
+ private static final String DEFAULT_TAG = "2.3.21";
+
+ @Override
+ public boolean supports(String databaseType) {
+ return databaseType.equals(TimeplusContainer.NAME);
+ }
+
+ @Override
+ public JdbcDatabaseContainer newInstance() {
+ return newInstance(DEFAULT_TAG);
+ }
+
+ @Override
+ public JdbcDatabaseContainer newInstance(String tag) {
+ if (tag != null) {
+ return new TimeplusContainer(DockerImageName.parse(TimeplusContainer.DOCKER_IMAGE_NAME).withTag(tag));
+ } else {
+ return newInstance();
+ }
+ }
+}
diff --git a/modules/timeplus/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/timeplus/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider
new file mode 100644
index 00000000000..f3122d3d73b
--- /dev/null
+++ b/modules/timeplus/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider
@@ -0,0 +1 @@
+org.testcontainers.timeplus.TimeplusContainerProvider
diff --git a/modules/timeplus/src/test/java/org/testcontainers/TimeplusImages.java b/modules/timeplus/src/test/java/org/testcontainers/TimeplusImages.java
new file mode 100644
index 00000000000..4079e0b4524
--- /dev/null
+++ b/modules/timeplus/src/test/java/org/testcontainers/TimeplusImages.java
@@ -0,0 +1,7 @@
+package org.testcontainers;
+
+import org.testcontainers.utility.DockerImageName;
+
+public interface TimeplusImages {
+ DockerImageName TIMEPLUS_IMAGE = DockerImageName.parse("timeplus/timeplusd:2.3.21");
+}
diff --git a/modules/timeplus/src/test/java/org/testcontainers/junit/timeplus/TimeplusJDBCDriverTest.java b/modules/timeplus/src/test/java/org/testcontainers/junit/timeplus/TimeplusJDBCDriverTest.java
new file mode 100644
index 00000000000..3f3a7ac8136
--- /dev/null
+++ b/modules/timeplus/src/test/java/org/testcontainers/junit/timeplus/TimeplusJDBCDriverTest.java
@@ -0,0 +1,19 @@
+package org.testcontainers.junit.timeplus;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.testcontainers.jdbc.AbstractJDBCDriverTest;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+
+@RunWith(Parameterized.class)
+public class TimeplusJDBCDriverTest extends AbstractJDBCDriverTest {
+
+ @Parameterized.Parameters(name = "{index} - {0}")
+ public static Iterable