nzw921rx opened a new issue, #13024:
URL: https://github.com/apache/seatunnel/issues/13024
### Search before asking
Searched existing issues and pull requests. Related reports such as #12884
concern MySQL CDC TIMESTAMP values written to Doris; this report concerns MySQL
JDBC DATETIME values written to a MySQL JDBC sink.
### What happened
MySQL `DATETIME` maps to SeaTunnel `TIMESTAMP` / `LocalDateTime`. Its date
and time fields can shift silently when written through JDBC if the execution
JVM default time zone differs from the JDBC connection time zone.
The reported version comparison is:
| JVM time zone | JDBC serverTimezone | 2.3.13 | 3.1.0 |
|---|---|---|---|
| Asia/Shanghai | UTC | Exact | -8 hours, 5,000/5,000 rows |
| UTC | Asia/Shanghai | Exact | +8 hours, 5,000/5,000 rows |
| Asia/Shanghai | Asia/Shanghai | Exact | Exact |
| UTC | UTC | Exact | Exact |
This table is the reported job-level comparison. Independent connector-level
verification of the affected dev code is described below; the 2.3.13 job was
not independently rerun during that verification.
### SeaTunnel version and verification environment
- Reported comparison: 2.3.13 versus 3.1.0.
- Independently reproduced with production converter code from dev commit
`8f859a49234b4eccf4600e3bf385444e24d668db` (3.1.0-SNAPSHOT).
- MySQL Server: 8.0.43.
- MySQL Connector/J: 8.0.27, the version declared by the JDBC module.
- Java: OpenJDK 17.0.20.1.
- Both client-side and server-side prepared statements were exercised.
### Relevant configuration and reproduction
The full original engine job configuration is unavailable. The independent
reproduction calls the production `MysqlJdbcRowConverter.toInternal` and
`toExternal` methods against a real MySQL instance.
For the -8 hour case, use:
```text
Execution JVM: -Duser.timezone=Asia/Shanghai
Source JDBC:
jdbc:mysql://HOST:3306/ntz_test?serverTimezone=UTC&preserveInstants=true
Sink JDBC:
jdbc:mysql://HOST:3306/ntz_test?serverTimezone=UTC&preserveInstants=true
```
Prepare tables and an independently inserted local value:
```sql
CREATE TABLE ntz_src (id INT PRIMARY KEY, dt DATETIME(6));
CREATE TABLE ntz_dst (id INT PRIMARY KEY, dt DATETIME(6));
INSERT INTO ntz_src VALUES (1, '2026-01-01 12:34:56.123456');
```
Read `SELECT dt FROM ntz_src WHERE id = 1`, convert that field as SeaTunnel
`LocalTimeType.LOCAL_DATE_TIME_TYPE`, and bind it through the JDBC converter to
`INSERT INTO ntz_dst (id, dt) VALUES (1, ?)`:
```java
TableSchema schema =
TableSchema.builder()
.column(
PhysicalColumn.builder()
.name("dt")
.dataType(LocalTimeType.LOCAL_DATE_TIME_TYPE)
.build())
.build();
MysqlJdbcRowConverter converter = new MysqlJdbcRowConverter();
// sourceResultSet is positioned on the source row;
// insertStatement is the prepared INSERT above.
SeaTunnelRow row = converter.toInternal(sourceResultSet, schema);
converter.toExternal(schema, row, insertStatement);
insertStatement.executeUpdate();
```
Check the database value independently of Java timestamp conversion:
```sql
SELECT DATE_FORMAT(dt, '%Y-%m-%d %H:%i:%s.%f') FROM ntz_dst WHERE id = 1;
```
Expected: `2026-01-01 12:34:56.123456`.
Affected JVM Asia/Shanghai / JDBC UTC path: `2026-01-01 04:34:56.123456`.
Reverse the JVM and JDBC zones to reproduce the opposite direction.
### Independent verification
The production-converter reproduction used five JVM/connection combinations:
UTC/UTC, UTC/Asia-Shanghai, Asia-Shanghai/UTC, Asia-Shanghai/Asia-Shanghai, and
America-New-York/UTC, each with both prepared-statement modes.
```text
Cases: 10
Rows: 60
Source read mismatches: 0
Sink value mismatches: 30
```
Each case contained five non-null DATETIME values and one null.
Matching-zone cases passed; all five non-null values shifted in each
mismatching-zone case. The null remained null. These are connector-level
database checks, not a full SeaTunnel engine E2E run.
### Relevant code / suspected cause
Related change: #10724, merged as `872077f6480ade460efca5501a3847fa2703e681`.
The shared JDBC TIMESTAMP read branch changed from
`getTimestamp(...).toLocalDateTime()` to
`JdbcFieldTypeUtils.getLocalDateTime(...)`, whose preferred path is the typed
`LocalDateTime` getter. The ordinary TIMESTAMP writer retained:
```java
statement.setTimestamp(statementIndex, Timestamp.valueOf(localDateTime));
```
`Timestamp.valueOf` uses the JVM default time zone; the driver subsequently
serializes the timestamp using its effective connection time zone.
This is a suspected regression mechanism identified by code inspection. A
before/after-commit bisect has not been performed.
### Workaround
For affected DATETIME jobs, align the effective source and sink JDBC
connection time zones with the JVM default time zone on every execution node,
for example all UTC or all Asia/Shanghai. Both matching combinations passed the
reported comparison and the independent converter checks.
### Error exception
No exception is required: rows can be written successfully with shifted
values.
### Scope
This report is limited to the verified MySQL DATETIME / SeaTunnel TIMESTAMP
path. It does not establish the same regression for MySQL TIMESTAMP / SeaTunnel
TIMESTAMP_TZ, CDC sources, OceanBase, or other JDBC dialects.
Other drivers may support different temporal binding APIs and need separate
verification. The report does not propose changing the shared JDBC default for
every dialect or treating untested databases as confirmed affected.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]