Java 的 LocalDateTime 和 PostgreSQL 的时间类型,“说的不是同一种时间”。


先搞清楚 PostgreSQL 的两种时间类型

TIMESTAMP           → 不带时区,就是个裸时间 "2026-06-25 10:00:00"
TIMESTAMPTZ         → 带时区,内部存 UTC,查询时按会话时区转换

Java 这边

LocalDateTime       → 没有时区概念,就是个裸时间
ZonedDateTime       → 带时区
OffsetDateTime      → 带偏移量(如 +08:00)
Instant             → UTC 时间戳

为什么会报错

PostgreSQL JDBC 驱动(特别是新版本 42.x+)对类型匹配非常严格

flowchart TD
    A[Java LocalDateTime] --> B[JDBC驱动]
    B --> C{PostgreSQL列类型}
    C -->|TIMESTAMP| D[✅ 可以匹配]
    C -->|TIMESTAMPTZ| E[❌ 类型不匹配报错]

你的列如果是 TIMESTAMPTZ(带时区),但 Java 传的是 LocalDateTime(无时区),驱动不知道该用哪个时区换算,就直接拒绝了。


常见的三种报错

Cannot convert LocalDateTime to TIMESTAMPTZ
Bad value for type timestamp/date
column is of type timestamp with time zone but expression is of type timestamp

解决方案

方案一:改 Java 类型(推荐)

// 把实体类里的字段改成
private OffsetDateTime createdAt;
// 或
private Instant createdAt;

OffsetDateTime 和 PostgreSQL 的 TIMESTAMPTZ 天然匹配。

方案二:改 PostgreSQL 列类型

-- 如果你不需要时区,把列改成不带时区
ALTER TABLE tb_archive 
ALTER COLUMN created_at TYPE TIMESTAMP;

然后 LocalDateTime 就能正常用了。

方案三:强制类型转换(临时方案,不推荐)

// MyBatis 层手动转
createdAt.atOffset(ZoneOffset.UTC)

最简建议

flowchart TD
    A[你的场景] --> B{需要时区吗}
    B -->|需要,比如多国用户| C[PostgreSQL用TIMESTAMPTZ\nJava用OffsetDateTime]
    B -->|不需要,单时区项目| D[PostgreSQL用TIMESTAMP\nJava用LocalDateTime]