Skip to content

Commit 2ab9c2d

Browse files
authored
Merge pull request #2032 from steve-community/1371-mysql-jdbc-driver-truncates-milliseconds-when-using-with-mariadb
1371 mysql jdbc driver truncates milliseconds when using with mariadb
2 parents c21f7a7 + afa0187 commit 2ab9c2d

4 files changed

Lines changed: 193 additions & 0 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
image: ${{ matrix.database.image }}
4141
command: ${{ matrix.database.command }}
4242
env:
43+
TZ: +00:00
4344
MYSQL_ROOT_PASSWORD: root
4445
MARIADB_ROOT_PASSWORD: root
4546
MYSQL_DATABASE: root

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ services:
1313
- 3306:3306
1414
environment:
1515
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
16+
TZ: +00:00
1617
MYSQL_DATABASE: stevedb
1718
MYSQL_USER: steve
1819
MYSQL_PASSWORD: changeme
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
3+
* Copyright (C) 2013-2026 SteVe Community Team
4+
* All Rights Reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package de.rwth.idsg.steve.issues;
20+
21+
import de.rwth.idsg.testconfig.JooqOnlyTestConfiguration;
22+
import jooq.steve.db.tables.records.ChargeBoxRecord;
23+
import org.joda.time.DateTime;
24+
import org.jooq.DSLContext;
25+
import org.jooq.Result;
26+
import org.junit.jupiter.api.AfterEach;
27+
import org.junit.jupiter.api.Assertions;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.params.ParameterizedTest;
30+
import org.junit.jupiter.params.provider.MethodSource;
31+
import org.springframework.beans.factory.annotation.Autowired;
32+
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
33+
import org.springframework.test.context.ActiveProfiles;
34+
import org.springframework.test.context.ContextConfiguration;
35+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
36+
37+
import java.util.stream.Stream;
38+
39+
import static jooq.steve.db.Tables.CHARGE_BOX;
40+
41+
/**
42+
* Tests about fractional seconds: https://github.com/steve-community/steve/issues/1371
43+
*
44+
* @author Sevket Goekay <sevketgokay@gmail.com>
45+
* @since 21.01.2024
46+
*/
47+
@ActiveProfiles(profiles = "test")
48+
@SpringJUnitConfig
49+
@ContextConfiguration(
50+
classes = JooqOnlyTestConfiguration.class,
51+
initializers = ConfigDataApplicationContextInitializer.class
52+
)
53+
public class Issue1371Test {
54+
55+
@Autowired
56+
private DSLContext dslContext;
57+
58+
private static final String TEST_CHARGE_BOX_ID = "CB-A5EK1234";
59+
60+
@BeforeEach
61+
public void setup() {
62+
dslContext.deleteFrom(CHARGE_BOX)
63+
.where(CHARGE_BOX.CHARGE_BOX_ID.eq(TEST_CHARGE_BOX_ID))
64+
.execute();
65+
}
66+
67+
@AfterEach
68+
public void teardown() {
69+
setup();
70+
}
71+
72+
public static Stream<String> provideInputDateTimes() {
73+
return Stream.of(
74+
"2024-01-21T05:06:07.000Z",
75+
"2024-01-21T05:06:07.123Z"
76+
);
77+
}
78+
79+
@ParameterizedTest
80+
@MethodSource("provideInputDateTimes")
81+
public void testPrecisionInJava(String dateTimeString) {
82+
DateTime dtIn = DateTime.parse(dateTimeString);
83+
Assertions.assertEquals(dateTimeString, dtIn.toString());
84+
85+
DateTime dtOut = DateTime.parse(dtIn.toString());
86+
Assertions.assertEquals(dateTimeString, dtOut.toString());
87+
88+
Assertions.assertEquals(dtIn, dtOut);
89+
}
90+
91+
@ParameterizedTest
92+
@MethodSource("provideInputDateTimes")
93+
public void testPrecisionWithDatabase(String dateTimeString) {
94+
DateTime dtIn = DateTime.parse(dateTimeString);
95+
DateTime dtOut = insertAndGetDateTime(dtIn);
96+
97+
Assertions.assertTrue(dtIn.isEqual(dtOut), () -> "Expected: " + dtIn + " vs actual: " + dtOut);
98+
}
99+
100+
/**
101+
* Persist some DateTime in some table in DB (just to have DB evaluate it) and
102+
* get the evaluated version back for further checks.
103+
*/
104+
private DateTime insertAndGetDateTime(DateTime dtIn) {
105+
// 1. insert
106+
dslContext.insertInto(CHARGE_BOX)
107+
.set(CHARGE_BOX.CHARGE_BOX_ID, TEST_CHARGE_BOX_ID)
108+
.set(CHARGE_BOX.LAST_HEARTBEAT_TIMESTAMP, dtIn)
109+
.execute();
110+
111+
// 2. read
112+
Result<ChargeBoxRecord> rows = dslContext.selectFrom(CHARGE_BOX)
113+
.where(CHARGE_BOX.CHARGE_BOX_ID.eq(TEST_CHARGE_BOX_ID))
114+
.fetch();
115+
Assertions.assertNotNull(rows);
116+
117+
ChargeBoxRecord chargeBoxRecord = rows.get(0);
118+
Assertions.assertNotNull(chargeBoxRecord);
119+
120+
return chargeBoxRecord.getLastHeartbeatTimestamp();
121+
}
122+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
3+
* Copyright (C) 2013-2026 SteVe Community Team
4+
* All Rights Reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package de.rwth.idsg.testconfig;
20+
21+
import com.zaxxer.hikari.HikariDataSource;
22+
import de.rwth.idsg.steve.config.BeanConfiguration;
23+
import de.rwth.idsg.steve.config.SteveProperties;
24+
import org.jooq.DSLContext;
25+
import org.jooq.SQLDialect;
26+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
27+
import org.springframework.boot.jdbc.autoconfigure.DataSourceProperties;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Configuration;
30+
31+
import javax.sql.DataSource;
32+
33+
/**
34+
* Test configuration for tests that need the real Spring Boot property binding and
35+
* production jOOQ/DataSource bean wiring, but do not need to start the full SteVe
36+
* application context.
37+
*
38+
* This class intentionally lives outside {@code de.rwth.idsg.steve}. The
39+
* production {@link BeanConfiguration} component-scans that package, and full
40+
* {@code @SpringBootTest} tests would otherwise discover this helper as an
41+
* additional configuration class. That would register duplicate beans such as
42+
* {@code dataSource} when the full application context is started.
43+
*
44+
* @author Sevket Goekay <sevketgokay@gmail.com>
45+
* @since 01.05.2026
46+
*/
47+
@Configuration(proxyBeanMethods = false)
48+
@EnableConfigurationProperties({DataSourceProperties.class, SteveProperties.class})
49+
public class JooqOnlyTestConfiguration {
50+
51+
private final BeanConfiguration beanConfiguration = new BeanConfiguration();
52+
53+
@Bean
54+
HikariDataSource dataSource(DataSourceProperties properties) {
55+
return beanConfiguration.dataSource(properties);
56+
}
57+
58+
@Bean
59+
SQLDialect jooqSqlDialect(DataSource dataSource) {
60+
return beanConfiguration.jooqSqlDialect(dataSource);
61+
}
62+
63+
@Bean
64+
DSLContext dslContext(DataSource dataSource,
65+
SteveProperties steveProperties,
66+
SQLDialect jooqSqlDialect) {
67+
return beanConfiguration.dslContext(dataSource, steveProperties, jooqSqlDialect);
68+
}
69+
}

0 commit comments

Comments
 (0)