Postgres 时间戳列的默认值设置未正确使用

Posted

技术标签:

【中文标题】Postgres 时间戳列的默认值设置未正确使用【英文标题】:DEFAULT value setting of Postgres timestamp column not getting used correctly 【发布时间】:2017-02-22 17:44:15 【问题描述】:

我们面临一个问题,即从基于 Java 的 hibernate-spring REST API 将时间戳的 DEFAULT 值存储在 Postgres 表中。

列设置为

load_ts timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL,

我们不会从我们的代码中发送任何时间戳值。 这就是我们在实体类中指定时间戳字段的方式。

@Column(name = "LOAD_TS", insertable = false, updatable = false)
    private Timestamp loadTimeStamp; 

我们正在使用 Amazon RDS Postgres 数据库。数据库参数组值“时区”设置为“UTC”。

预期 - 当插入请求到达数据库而没有任何 load_ts 值发送到 DB 时,load_ts 应该根据默认设置获得 UTC 时间戳。

实际 - 当插入请求到达数据库而没有任何 load_ts 值发送到 DB 时,load_ts 将设置为东部时间 + 10 小时。 API 在东部时间运行。

当我们在数据库中手动插入记录时,时间戳会正确生成和存储。我们查看了 RDS postgres 中的 log_statements,但没有看到 API 在插入查询中发送任何时间戳。另一个有趣的观察是,当我们将 API 时区更改为 UTC 时,时间戳被正确存储。因此,API 以某种方式将时区传达给数据库。

应用程序堆栈 - 1) Hibernate 核心版本 - 5.0.7

2) 春季 - 4.2

3) Postgres - 9.4

4) Postgres 驱动程序 - 9.4.1212.jre7

5) RDS Postgres DB 时区参数组值 - UTC

6) 应用程序运行的时区 - 美国东部时间。

【问题讨论】:

【参考方案1】:

您的列定义没有意义。函数now() 返回类型timestamp with time zone。函数timezone() 有几个重载变体,但你最终调用的那个会返回timestamp without time zone。然后将其插入timestamp with time zone 类型的列中。

我不想完全了解这里实际发生的情况,但我想由于美国东部时间和 UTC 通常相隔 5 小时,而您的时差为 10 小时,您可能有时区偏移申请了两次。

根据您的描述,我认为您真正想要的是简单地定义您的列

load_ts timestamp with time zone DEFAULT now() NOT NULL,

一切都会好起来的。

【讨论】:

感谢彼得的建议。我们想过做同样的改变。我只是有点困惑为什么这个问题只有在我通过休眠应用程序插入数据时才会发生。当我使用普通的 sql 插入语句插入数据时,时间戳是正确的。在这种情况下,timezone() 可能会返回带有时区的时间戳。 因为从timestamp without time zone(函数结果)到timestamp with time zone(列类型)的转换考虑了会话时区。如果您的应用程序和您的 psql 会话配置了不同的时区,那么结果会有所不同。

以上是关于Postgres 时间戳列的默认值设置未正确使用的主要内容,如果未能解决你的问题,请参考以下文章