我认为IT的两大祸害之一是时间戳和时区(另一个是字符编码),人们一次又一次地偶然发现。。。
在这方面,我目前遇到了一个与Java应用程序中存储到PostgreSQL数据库中的不同时间戳有关的问题。
为了保持简单,假设有以下表格:
CREATE TABLE ts_test
(
id integer NOT NULL,
utc timestamp without time zone,
local timestamp with time zone,
CONSTRAINT pk PRIMARY KEY (id)
)
所以,我必须存储一个UTC时间戳和一个本地时间戳,在我的情况下是中欧夏令时间,所以目前是UTC+2。
进一步假设表中有2个条目,在psql控制台上输出如下(数据库以UTC运行):
# select id, utc, local, local-utc as diff from ts_test;
id | utc | local | diff
----+---------------------+------------------------+----------
1 | 2012-06-27 12:00:00 | 2012-06-27 12:00:00+00 | 00:00:00
2 | 2012-06-27 12:00:00 | 2012-06-27 14:00:00+00 | 02:00:00
(2 rows)
现在,出现了几个问题:
- 本地列中的输出究竟意味着什么
- 系统如何知道时区,我在其中插入了值
- 如何查看存储的真实原始值(例如毫秒)
我假设,第一行的本地"12:00:00+00"意味着,现在是UTC的12:00,也是CEST的14:00。但似乎(我们的数据库管理员告诉我),第二行的本地"14:00:00+00"是14:00 CEST的正确值,这得到了2小时差异的支持。
但是要通过sql插入生成第二行,我必须编写
insert into ts_test (id, utc, local) values (2, '2012-06-27 12:00:00', '2012-06-27 16:00:00+02');
这同样不支持预测。
所以,为了总结这个长问题,有人能告诉我这整件事是如何详细工作的,输出应该是什么意思,以及应该如何将本地时间戳正确地写入数据库吗?
根据local
列的输出,SQL会话的时区设置为UTC
或GMT
,而不是您居住的时区。这可能就是您的意思:数据库以UTC运行。这是问题的根源,但让我们试着详细说明一下。
数据库本身作为一个数据存储库,没有时区,但每个SQL会话都有自己的时区。
当SQL会话请求它们时,timestamp without time zone
的值被不旋转到会话的时区,并且没有显示时间偏移,而timestamp with time zone
的值被旋转到会话时区,并且显示该时区的时间偏移。这就是两者的区别。
时区永远不会存储在任何数据类型中,因为在读取值时,重要的是请求该值的SQL会话的时区。
将SQL时区设置为UTC
不是一个好主意,因为它与问题的另一部分相矛盾:
所以,我必须存储一个UTC时间戳和一个本地时间戳,在我的情况下是中欧夏季时间,所以目前UTC+2
让SQL会话知道您的实时时区,它就会开始按预期工作。如果你不这样做,timestamp with time zone
基本上是无用的。
还要注意,在utc timestamp without time zone
和local timestamp with time zone
中存储相同的时间是没有意义的,因为您总是可以使用获得utc
SELECT local AT TIME ZONE 'UTC' FROM ts_test WHERE...
编辑:评论中问题的答案:
Q: 你是说,如果我的时区设置为会话,那么我应该看到例如。。。当地时间14:00:00+02…的utc值。。。下午12:00:00
是的。
Q: 当从我的应用程序向本地字段写入内容时,设置哪个时区很重要?
没错。
Q: 如何在JDBS会话中设置此项?
我不知道JDBC,但在SQL级别,例如:
SET timezone='Europe/Berlin';
通常情况下,它是从环境中自动设置的,但可以在包括postgresql.conf
在内的各种级别强制设置。在会话中显式设置它将覆盖其他任何内容。
Q: 我能以某种方式查看时间戳的原始值吗,以确保它不仅仅是显示时的表示问题
我不知道如何做到这一点,除了在较低级别运行的pageinspect。