Java中Timestamp转LocalDateTime时区处理问题及解决方案
将java.sql.Timestamp转换为LocalDateTime的时区问题
我在将java.sql.Timestamp转换为LocalDateTime时遇到了问题。已知Timestamp(和Date一样)不包含时区信息,我手中的Timestamp实际代表UTC时间,需要转换为GMT+2时区(欧洲柏林时区)的本地时间。但调用Timestamp#toInstant()方法后得到的时间比预期少了2小时——因为Instant基于UTC时间轴,Java默认把这个Timestamp当成了本地时区(GMT+2)的时间,所以toInstant()做了减2小时的转换,但实际上这个Timestamp本身就是UTC时间,这一步转换是错误的。
我测试了这段代码:
Timestamp stamp = new Timestamp(123, 7, 1, 10, 0, 0, 0); // 代表UTC时间2023-08-01 10:00:00 stamp.toInstant() // 返回2023-08-01 08:00:00
我期望得到的结果是2023-08-01 12:00:00。之前我用新的java.time API实现过正确转换,但没有涉及Timestamp:
Instant nowUtc = Instant.now(); ZoneId europeZone = ZoneId.of("Europe/Berlin"); ZonedDateTime berlinTime = ZonedDateTime.ofInstant(nowUtc, europeZone);
补充说明:
- 生产环境中,
Timestamp并非通过已弃用的构造函数new Timestamp(123, 7, 1, 10, 0, 0, 0)创建,而是通过Spring Data CrudRepository结合jakarta.persistence @Entity从数据库获取,不清楚具体构造逻辑。 - 当前代码库暂时无法重构持久层,必须使用
Timestamp,尽管我知道推荐用新的java.time API替代旧日期API。 - 同事最终找到了适配该场景的解决方案:
@Test fun `convert LocalDateTime to Timestamp and back`() { val timestamp = Timestamp.valueOf(LocalDateTime.now(ZoneOffset.UTC)) println("BEFORE $timestamp") val localDateTime = ZonedDateTime.of(timestamp.toLocalDateTime(), ZoneOffset.UTC) .withZoneSameInstant(ZoneId.of("Europe/Berlin")).toLocalDateTime() println("AFTER $localDateTime") }
运行输出(依执行时间而定):
BEFORE 2023-08-22 14:53:34.085801 AFTER 2023-08-22T16:53:34.085801
内容的提问来源于stack exchange,提问作者MKorsch
相关产品推荐
相关产品推荐

