我应该在PostgreSQL数据库中选择哪个时间戳类型?

前端之家收集整理的这篇文章主要介绍了我应该在PostgreSQL数据库中选择哪个时间戳类型?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我想定义一个最佳实践,在多时区项目的上下文中的Postgres数据库中存储时间戳。

我可以

>选择TIMESTAMP WITHOUT TIME ZONE,并记住在此字段的插入时间使用了哪个时区
>选择TIMESTAMP WITHOUT TIME ZONE并添加另一个字段,其中包含插入时使用的时区名称
>选择TIMESTAMP WITH TIME ZONE并相应地插入时间戳

我有一个轻微的偏好选项3(时间戳与时区),但希望有一个教育的意见。

首先,Postgresql的时间处理和算术是太棒了,选项3在一般情况下很好。然而,它是一个不完整的时间和时区的观点,可以补充:

>将用户时区的名称存储为用户偏好设置(例如America / Los_Angeles,而不是-0700)。
>将用户事件/时间数据提交到他们的参考框架(很可能是与UTC的偏移量,如-0700)。
>在应用程序中,将时间转换为UTC并使用TIMESTAMP WITH TIME ZONE列存储。
>对用户时区的本地返回时间请求(即从UTC转换为America / Los_Angeles)。
>将数据库的时区设置为UTC。

此选项并不总是起作用,因为可能难以获取用户的时区,因此对于轻量级应用程序使用TIMESTAMP WITH TIME ZONE的对冲建议很难。也就是说,让我更详细地解释这个选项4的一些背景方面。

与选项3一样,WITH TIME ZONE的原因是因为发生事情的时间是绝对的时间。 WITHOUT TIME ZONE产生相对时区。永远不要混合绝对和相对TIMESTAMP。

从程序和一致性的角度来看,确保使用UTC作为时区进行所有计算。这不是Postgresql的要求,但它在与其他编程语言或环境集成时有所帮助。在列上设置CHECK以确保对时间戳列的写入具有0的时区偏移量是防御位置,其防止了几类错误(例如,脚本将数据转储到文件并且另外排序时间数据使用词法排序)。再次,Postgresql不需要这样做来正确地进行日期计算或在时区之间转换(即Postgresql非常擅长在任意两个任意时区之间转换时间)。为了确保数据进入数据库,存储的偏移量为零:

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%完美,但它提供了一个足够强的反footshooting措施,确保数据已经转换为UTC。有很多关于如何做到这一点的意见,但这似乎是在我的经验的实践中最好的。

批评数据库时区处理在很大程度上是合理的(有很多数据库处理这个与无能),但Postgresql的时间戳和时区的处理是相当可怕的(尽管一些“功能”在这里和那里)。例如,一个这样的特征:

-- 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)

注意,AT TIME ZONE’UTC’剥离时区信息,并使用目标的参考系(UTC)创建相对TIMESTAMP WITHOUT TIME ZONE。

当从不完全TIMESTAMP WITHOUT TIME ZONE转换到TIMESTAMP WITH TIME ZONE时,缺少的时区将继承自您的连接:

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作为数据库中的时区

随机编程语言注:Python的datetime数据类型非常好地保持绝对和相对时间之间的区别(虽然首先感到沮丧,直到你补充它像一个库像PyTZ)。

编辑

让我解释一下相对与绝对之间的差异。

绝对时间用于记录事件。示例:“用户123登录”或“毕业典礼开始于2011-05-28 2pm太平洋标准时间”。无论您当地的时区,如果您可以传送到事件发生的地点,您可以看到事件发生。数据库中的大多数时间数据是绝对的(因此应该是TIMESTAMP WITH TIME ZONE,理想情况下为0偏移和一个文本标签,表示管理特定时区的规则,而不是偏移量)。

相对事件将是从尚未确定的时区的角度记录或调度某事物的时间。例如:“我们的商店的门在上午8点开门,晚上9点关门”,“让我们每个星期一上午7点见面,每周早餐会议”或“每个万圣节晚上8点”。通常,相对时间在事件的模板或工厂中使用,绝对时间用于几乎所有其他事物。有一个罕见的例外值得指出,它应该说明相对时间的价值。对于未来足够远的未来事件,如果可能存在某些事情可能发生的绝对时间的不确定性,请使用相对时间戳。这里是一个真实世界的例子:

假设是2004年,您需要在2008年10月31日下午1点在美国西海岸(即美国/ Los_Angeles / PST8PDT)安排交货。如果你使用绝对时间使用’2008-10-31 21:00:00.000000 00′:: TIMESTAMP WITH TIME ZONE存储它,交付将在下午2点显示,因为美国政府通过了改变夏令时规则的Energy Policy Act of 2005时间。在2004年交付时间安排时,10-31-2008的日期将是太平洋标准时间(8000),但从2005年时区数据库开始,认识到10-31-2008将是太平洋夏令时(0700)。存储具有时区的相对时间戳将导致正确的递送调度,因为相对时间戳不受国会的不良通知篡改的影响。在调度事件的使用相对与绝对时间之间的界限是一个模糊线,但我的经验法则是,在未来超过3-6mo的任何事情的调度应利用相对时间戳(调度=绝对vs计划=相对)。

其他/最后一种类型的相对时间是INTERVAL。示例:“会话将在用户登录20分钟后超时”。可以使用绝对时间戳(TIMESTAMP WITH TIME ZONE)或相对时间戳(TIMESTAMP WITHOUT TIME ZONE)正确使用INTERVAL。同样正确的说,“用户会话在成功登录(login_utc session_duration)20分钟后到期”或“我们的早上早餐会议只能持续60分钟(recurring_start_time meeting_length)”。

最后的混乱位:DATE,TIME,TIME WITHOUT TIME ZONE和TIME WITH TIME ZONE都是相对数据类型。例如:’2011-05-28′:: DATE表示相对日期,因为您没有可用于识别午夜的时区信息。类似地,’23:23:59′:: TIME是相对的,因为你不知道时间或由时间表示的DATE。即使有’23:59:59-07′:: TIME WITH TIME ZONE,你也不知道DATE是什么。最后,DATE与一个时区不是事实上是一个DATE,它是一个TIMESTAMP WITH TIME ZONE:

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)

数据库中放置日期和时区是一件好事,但很容易得到不正确的结果。需要最小的额外努力来正确和完全地存储时间信息,然而这并不意味着总是需要额外的努力。

猜你在找的Postgre SQL相关文章