Which timestamp type should I choose in a PostgreSQL database?
我想定义一个在多时区项目上下文中在Postgres数据库中存储时间戳的最佳实践。
我可以
我对选项3(带有时区的时间戳)有一点偏好,但我想对这个问题有一个有根据的意见。
首先,PostgreSQL的时间处理和算法非常好,在一般情况下,选项3也很好。然而,这是一个不完整的时间和时区视图,可以补充:好的。
这个选项并不总是有效的,因为很难获得用户的时区,因此对于轻量级应用程序,使用
与选项3一样,
从程序和一致性的角度来看,确保所有计算都使用UTC作为时区。这不是PostgreSQL的要求,但在与其他编程语言或环境集成时,它会有所帮助。在列上设置一个
1 2 3 4 5 6 7 8 9 10 11 12 | CREATE TABLE my_tbl ( my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0') ); test=> SET timezone = 'America/Los_Angeles'; SET test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW()); ERROR: NEW ROW FOR relation"my_tbl" violates CHECK CONSTRAINT"my_tbl_my_timestamp_check" test=> SET timezone = 'UTC'; SET test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW()); INSERT 0 1 |
它不是100%完美的,但它提供了足够强大的反足迹措施,确保数据已经转换为UTC。关于如何做到这一点,有很多意见,但从我的经验来看,这似乎是最好的做法。好的。
对数据库时区处理的批评在很大程度上是合理的(有很多数据库处理的能力非常差),但是PostgreSQL对时间戳和时区的处理非常出色(尽管这里和那里有一些"特性")。例如,一个这样的特性:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -- Make sure we're all working off of the same local time zone test=> SET timezone = 'America/Los_Angeles'; SET test=> SELECT NOW(); now ------------------------------- 2011-05-27 15:47:58.138995-07 (1 ROW) test=> SELECT NOW() AT TIME ZONE 'UTC'; timezone ---------------------------- 2011-05-27 22:48:02.235541 (1 ROW) |
请注意,
当从不完整的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | test=> SET timezone = 'America/Los_Angeles'; SET test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW()); date_part ----------- -7 (1 ROW) test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541'); date_part ----------- -7 (1 ROW) -- Now change to UTC test=> SET timezone = 'UTC'; SET -- Create an absolute time with timezone offset: test=> SELECT NOW(); now ------------------------------- 2011-05-27 22:48:40.540119+00 (1 ROW) -- Creates a relative time in a given frame of reference (i.e. no offset) test=> SELECT NOW() AT TIME ZONE 'UTC'; timezone ---------------------------- 2011-05-27 22:48:49.444446 (1 ROW) test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW()); date_part ----------- 0 (1 ROW) test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541'); date_part ----------- 0 (1 ROW) |
底线是:好的。
- 将用户的时区存储为命名标签(如
America/Los_Angeles ,而不是与UTC的偏移量(如-0700 ) - 除非有令人信服的理由存储非零偏移量,否则对所有内容都使用UTC
- 将所有非零UTC时间视为输入错误
- 从不混合和匹配相对和绝对时间戳
- 如果可能的话,也使用
UTC 作为数据库中的timezone 。
随机编程语言注释:python的
编辑好的。
我再解释一下相对和绝对的区别。好的。
绝对时间用于记录事件。示例:"用户123登录"或"毕业典礼从2011-05-28太平洋标准时间下午2点开始"。无论您所在的时区是什么,如果您可以传送到事件发生的位置,您都可以见证事件的发生。数据库中的大多数时间数据都是绝对的(因此应该是
一个相对的事件是从一个尚未确定的时区的角度记录或安排某个事物的时间。例如:"我们公司的大门早上8点开门,晚上9点关门","让我们每周一早上7点开会,每周开早餐会",或"每个万圣节晚上8点",通常情况下,模板或工厂中的活动使用相对时间,而绝对时间几乎用于其他所有活动。有一个罕见的例外值得指出,它应该说明相对时间的价值。对于未来足够远的事件,如果不确定某些事情发生的绝对时间,则使用相对时间戳。下面是一个现实世界的例子:好的。
假设是2004年,你需要在2008年10月31日美国西海岸下午1点交货(即
另一种/最后一种相对时间是
最后一点混淆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | test=> SET timezone = 'America/Los_Angeles'; SET test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC'; timezone --------------------- 2011-05-11 07:00:00 (1 ROW) test=> SET timezone = 'UTC'; SET test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC'; timezone --------------------- 2011-05-11 00:00:00 (1 ROW) |
在数据库中放置日期和时区是一件好事,但很容易得到微妙的错误结果。正确和完整地存储时间信息需要最少的额外工作,但这并不意味着总是需要额外的工作。好的。好啊。
肖恩的回答过于复杂和误导。
事实上,"有时区"和"没有时区"都将值存储为类似于Unix的绝对UTC时间戳。区别在于时间戳的显示方式。当"有时区"时,显示的值是转换到用户区域的UTC存储值。当"无时区"时,不管用户设置了哪个时区,都会扭曲UTC存储值以显示相同的时钟面。
"无时区"唯一可用的情况是,无论实际时区如何,时钟面值都适用。例如,当时间戳指示投票站可能关闭的时间(即,无论某人的时区如何,投票站都会在20:00关闭)。
使用选择3。除非有非常具体的理由不这样做,否则请始终使用"带时区"。
我倾向于选择3,因为Postgres可以为你重新计算与时区相关的时间戳,而其他两个你必须自己计算。用时区存储时间戳的额外存储开销实际上可以忽略不计,除非您谈论的是数百万条记录,在这种情况下,您可能已经有了相当多的存储需求。