You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JDBC存储1900年早期时间戳时出现错误的时区转换问题

JDBC存储1900年早期时间戳时出现错误的时区转换问题

我正在开发一个应用,需要存储一些非常早期(1900年左右)带时间戳的数据。我的JVM是IBM Semeru 17,运行在Europe/Paris时区,但我希望将时间戳以UTC(GMT+0)存储到PostgreSQL中。

以下是我的相关代码和配置:

依赖配置

<dependencies>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.7.1</version>
    </dependency>
</dependencies>

Java测试代码

public class Main {

    /*
    create table theentity (
        theid integer not null,
        thevalue timestamp(6),
        primary key (theid)
    )
     */
    public static void main(String[] args) {
        TimeZone.setDefault( TimeZone.getTimeZone( ZoneId.of( "Europe/Paris" ) ) );
        LocalDateTime d_1900_01_01_T_00_09_23 = LocalDateTime.of( 1900, 1, 1, 0, 9, 23, 0 );
        LocalDateTime d_1900_01_01_T_00_09_22 = LocalDateTime.of( 1900, 1, 1, 0, 9, 22, 0 );
        LocalDateTime d_1900_01_01_T_00_09_21 = LocalDateTime.of( 1900, 1, 1, 0, 9, 21, 0 );
        LocalDateTime d_1900_01_01_T_00_09_20 = LocalDateTime.of( 1900, 1, 1, 0, 9, 20, 0 );
        LocalDateTime d_1900_01_01_T_00_09_19 = LocalDateTime.of( 1900, 1, 1, 0, 9, 19, 0 );

        try(Connection c = DriverManager.getConnection( "jdbc:postgresql://localhost:5432/hibernate_orm_test?preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn",
                                                                                                        "postgres", "root")) {
            PreparedStatement p = c.prepareStatement( "insert into theentity values(?, ?)" );
            bindAndExecute( p, 1, d_1900_01_01_T_00_09_23 );
            bindAndExecute( p, 2, d_1900_01_01_T_00_09_22 );
            bindAndExecute( p, 3, d_1900_01_01_T_00_09_21 );
            bindAndExecute( p, 4, d_1900_01_01_T_00_09_20 );
            bindAndExecute( p, 5, d_1900_01_01_T_00_09_19 );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void bindAndExecute(PreparedStatement p, int id, LocalDateTime localDateTime)
            throws SQLException {
        p.setInt( 1, id );
        p.setTimestamp(2,
                Timestamp.valueOf( localDateTime ),
                Calendar.getInstance( TimeZone.getTimeZone( ZoneId.of( "GMT" ) ) )
        );
        p.executeUpdate();
    }

}

我设置了JVM默认时区为Europe/Paris,准备了这5个LocalDateTime测试时间:

  • 1900-01-01T00:09:23
  • 1900-01-01T00:09:22
  • 1900-01-01T00:09:21
  • 1900-01-01T00:09:20
  • 1900-01-01T00:09:19

原本期望通过这段代码把Paris时区的时间转换为UTC存储:

p.setTimestamp(2,
        Timestamp.valueOf( localDateTime ),
        Calendar.getInstance( TimeZone.getTimeZone( ZoneId.of( "GMT" ) ) )
);

但执行后,PostgreSQL中存储的结果却不符合预期:

theid |      thevalue       
-------+---------------------
     1 | 1900-01-01 00:00:02
     2 | 1900-01-01 00:00:01
     3 | 1900-01-01 00:00:00
     4 | 1899-12-31 23:09:20
     5 | 1899-12-31 23:09:19
(5 rows)

大家可能以为巴黎一直是GMT+1,但实际上1900年时巴黎的时区偏移是GMT+00:09:21(9分21秒)!所以前3条时间的转换是正确的,但第4、5条就出现了奇怪的问题:比如第4条我预期应该是1899-12-31 23:59:59,结果却存成了1899-12-31 23:09:20,看起来老的日期时间API在处理1899年的时区时错误地使用了GMT+1偏移,这是为什么呢?

备注:内容来源于stack exchange,提问作者SoT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 10:23:00