【问题标题】:How to get all the same date & time independently of the timezone如何独立于时区获取所有相同的日期和时间
【发布时间】:2012-02-04 16:17:15
【问题描述】:

我应该如何将我的“日期+时间”存储在 PostgreSQL 数据库中?

这就是我想要实现的目标:

  1. 我怎样才能获得所有发生在(例如)世界任何地方当地时间 2012 年 1 月 1 日 00:00:00 的条目?
  2. 显示根据UTC时间按日期排序的所有条目。 (2012 年纽约的新年前夜比伦敦的新年更近)。

我应该如何存储我的数据?我读过 PostgreSQL 在内部以 UTC 格式存储所有时间 (PostgreSQL documentation),所以我的用户时区实际上丢失了。

我认为我应该使用类型为“timestamp without timezone”的一列:

  • 第 1 点很简单。
  • 使用“String”类型的另一列,我将存储时区字符串(例如:America/New_York) 但是,第 2 点似乎仍然很难做到......

我希望我很清楚。

编辑新想法:我认为存储两个时间戳:一个没有时区(1. ok),一个有时区(2. ok)

【问题讨论】:

    标签: php sql postgresql


    【解决方案1】:

    是的,PostgreSQL 在内部将所有时间戳存储为 UTC。对于timestamp with time zone,时区偏移仅用于将时间调整为 UTC,但不会显式存储。

    我不会存储 时区字符串,甚至不会存储时区缩写(这些都不精确)。这在以后可能需要昂贵的计算,因为您必须考虑夏令时和国际时间制度的其他奇怪情况。

    您可以将时区偏移存储为间隔(需要 12 字节)或数字量的秒数(需要 4 字节 作为整数),就像我在这个related answer.

    或者,就像您已经建议的那样:除了 UTC 时间戳(占用 8 个字节)之外,还存储本地时间戳。这将使您的任务变得容易。考虑以下演示::

    -- DROP TABLE tbl;
    CREATE TEMP TABLE tbl (id int, ts_tz timestamp with time zone, ts timestamp);
    INSERT INTO tbl VALUES
     (1,'2012-1-1 00:00+01','2012-1-1 00:00+01')
    ,(2,'2012-1-1 00:00+02','2012-1-1 00:00+02')
    ,(3,'2012-1-1 00:01+03','2012-1-1 00:01+03')
    ,(4,'2012-1-1 00:02+04','2012-1-1 00:02+04');
    

    查询问题 1:

    SELECT *
    FROM   tbl
    WHERE  ts = '2012-1-1 00:00'::timestamp;
    
     id |         ts_tz          |         ts
    ----+------------------------+---------------------
      1 | 2012-01-01 00:00:00+01 | 2012-01-01 00:00:00
      2 | 2011-12-31 23:00:00+01 | 2012-01-01 00:00:00
    

    查询问题 2:

    SELECT *
    FROM   tbl
    ORDER  BY ts_tz;
    
     id |         ts_tz          |         ts
    ----+------------------------+---------------------
      4 | 2011-12-31 21:02:00+01 | 2012-01-01 00:02:00
      3 | 2011-12-31 22:01:00+01 | 2012-01-01 00:01:00
      2 | 2011-12-31 23:00:00+01 | 2012-01-01 00:00:00
      1 | 2012-01-01 00:00:00+01 | 2012-01-01 00:00:00
    

    此解决方案的棘手部分可能是输入本地时间戳。只要所有数据都在本地输入,这很容易。但是,如果您输入数据,例如洛杉矶的纽约,则需要考虑。为此使用AT TIME ZONE construct

    SELECT ('2012-1-1 00:00+00' AT TIME ZONE 'America/New_York')::timestamp
         , ('2012-1-1 00:00+00' AT TIME ZONE 'America/Los_Angeles')::timestamp
    
          timezone       |      timezone
    ---------------------+---------------------
     2011-12-31 19:00:00 | 2011-12-31 16:00:00
    

    注意我如何使用时间戳带时区作为输入。 AT TIME ZONE 为带或不带时区的时间戳提供不同的结果。

    【讨论】:

      【解决方案2】:

      我想您可以将日期存储为字符串并使用 ISO 8601 或 RFC 2822,因此您还可以存储时区。 或者实际上,对于更多比较选项,将时间戳和时区存储在 2 个不同的列中。

      【讨论】:

      • 将日期/时间存储为字符串是一种非常肮脏的做事方式,应该避免
      • 数据库中的日期/时间字段不总是一个字符串吗?我同意您在查询中进行比较,但如果不需要将其存储为 ISO 8601 字符串应该不是问题
      • 除非 postgresql 做的事情与其他数据库引擎有很大不同,否则,日期时间永远不会存储为字符串(通常它是一个整数值,表示从特定日期/时间定义的时间量过去)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多