pg数据库保存本地时间时区丢失问题

2024-09-03  本文已影响0人  flystarts

pg数据库有timestamptz字段类型可以用来保存带时区的时间
在实体定义里采用OffsetDateTime 来定义字段
dao层使用mybatis-plus框架

写入指定时区的时间:"2024-04-05T00:00:00+02:00"
但是数据库里变成了 2024-04-05 06:00:00.000 +0800
很显然,时间被转成了+8时区,这不是我们希望的效果

为了解决此问题,在网上找了很多解决办法,其中较多的是说要自定义typehandler,于是我自定义了
OffsetDateTimeTypeHandler:

@Override
    public void setNonNullParameter(PreparedStatement ps, int i, OffsetDateTime parameter, JdbcType jdbcType) throws SQLException {
        ps.setTimestamp(i, java.sql.Timestamp.from(parameter.toInstant()));
    }

测试发现没解决问题,查看Timestamp类的定义,发现他根本就不支持时区,所以必然导致时区丢失

后来又改为ps.setObject,

@Override
    public void setNonNullParameter(PreparedStatement ps, int i, OffsetDateTime parameter, JdbcType jdbcType) throws SQLException {
        //ps.setTimestamp(i, java.sql.Timestamp.from(parameter.toInstant()));
        ps.setObject(i, parameter);
    }

但是都不能解决问题。后来走读代码,发现mybatis自带的OffsetDateTimeTypeHandler也是这么写的,所以这里自定义typehandler不能解决问题

为了找到问题原因,我先用jdbc试一下

 // 时间和时区
        OffsetDateTime dateTimeWithZone = OffsetDateTime.parse("2024-04-04T00:00:00+02:00");
        System.out.println(dateTimeWithZone);// 默认时区
        Timestamp timestampWithZone = Timestamp.from(dateTimeWithZone.toInstant());

        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement("INSERT INTO newtable (time) VALUES (?)")) {

            // 设置时间戳
            pstmt.setTimestamp(1, timestampWithZone);  
            //pstmt.setObject(1, dateTimeWithZone);

            // 执行更新
            pstmt.executeUpdate();

            System.out.println("时间已保存到数据库");

        } catch (SQLException e) {
            e.printStackTrace();
        }
image.png

调试发现代码走到这里已经丢失了时区

看Timestamp的定义,发现这个类根本就不支持带时区,所以如果用pstmt.setTimestamp来写,肯定会丢失时区
接下来尝改为setObject,发现最后写到数据库里的时间还是改变了时区

通过代码调试,在pg数据库驱动代码里,SimpleParameterList: writeV3Value

image.png

这里把时间字符串 2024-04-04 00:00:00+02 转成bytes保存了然后调用pgStream.send

可见数据库客户端发过去的是这样的字符串2024-04-04 00:00:00+02

那为什么最后数据库保存的变成了+8呢? 这就需要看数据库服务端的实现了

综上,jdbc写时间的时候,客户端侧并没有改变时区,所以问题还在服务端。因此,在应用层,mybatis层面是无法解决问题的,当前只能先在应用层规避,即自己去做时区转换。

上一篇 下一篇

猜你喜欢

热点阅读