数据类型timestamp
是的简称timestamp without time zone
。
另一种选择timestamptz
是timestamp with time zone
。
timestamptz
从字面上看,是日期/时间系列中的首选类型。它已typispreferred
设置为pg_type
,可能与以下内容有关:
内部存储和时代
在内部,时间戳占用磁盘和RAM上8个字节的存储空间。它是一个整数值,表示距Postgres纪元2000-01-01 00:00:00 UTC的微秒数。
Postgres还具有从UNIX时代1970-01-01 00:00:00 UTC开始的常用UNIX计时秒数的内置知识,并将其用于函数to_timestamp(double precision)
或中EXTRACT(EPOCH FROM timestamptz)
。
源代码:
*时间戳以及间隔的h / m / s字段存储为
* int64值,以微秒为单位。(曾几何时
*以秒为单位的双精度值。)
和:
/ *在Unix和Postgres估算中,第0天的儒略日期等效* /
#定义UNIX_EPOCH_JDATE 2440588 / * == date2j(1970,1,1)* /
#定义POSTGRES_EPOCH_JDATE 2451545 / * == date2j(2000,1,1)* /
微秒的分辨率最多可转换为6个小数位,持续几秒。
timestamp
键入为的值告诉Postgres没有明确提供时区。假设当前时区。Postgres会忽略错误添加的任何时区修饰符!timestamp
[without time zone]
没有时间转移显示。使用相同的时区设置,一切都很好。对于不同的时区设置,含义会发生变化,但值和显示保持不变。
timestamptz
的处理timestamp with time zone
略有不同。我在这里引用手册:
对于timestamp with time zone
,内部存储的值始终以UTC(全球标准时间...)表示
大胆强调我的。该时区本身永远不会保存。它是一个输入修饰符,用于计算相应的UTC时间戳,该时间戳被存储-或与输出修饰符一起用于计算要显示的本地时间-带有附加的时区偏移量。如果您没有timestamptz
在输入上附加偏移量,则假定会话的当前时区设置。所有计算都是使用UTC时间戳值完成的。如果您必须(或可能必须)处理多个时区,请使用timestamptz
。
客户端(如psql或pgAdmin或通过libpq进行通信的任何应用程序(如带有pg gem的Ruby)会显示当前时区或根据请求的时区的时间戳加上偏移量(请参见下文)。它始终是同一时间点,只有显示格式有所不同。或者,如手册所述:
所有可识别时区的日期和时间都内部存储在UTC中。在将
它们显示给客户端之前,它们会在TimeZone配置参数指定的区域中转换为本地时间。
考虑以下简单示例(在psql中):
db =#SELECT timestamptz'2012-03-05 20:00 +03 ';
时间戳
------------------------
2012-03-05 18:00:00 +01
大胆强调我的。这里发生了什么?
我+3
为输入文字选择了任意时区偏移量。对于Postgres来说,这只是输入UTC时间戳的多种方式之一2012-03-05 17:00:00
。在我的测试中,查询的结果针对当前时区设置Vienna / Austria 显示,该位置在冬季和夏季时间具有偏移量:,因为它属于冬季时间。+1
+2
2012-03-05 18:00:00+01
Postgres已经忘记了如何输入该值。它只记住值和数据类型。就像十进制数字一样。numeric '003.4'
,numeric '3.40'
或numeric '+3.4'
-所有结果都具有完全相同的内部值。
AT TIME ZONE
一旦掌握了这种逻辑,就可以做任何您想做的事情。现在缺少的只是根据特定时区解释或表示时间戳文字的工具。这就是AT TIME ZONE
构造的来源。有两种不同的用例。timestamptz
转换为timestamp
,反之亦然。
输入UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
...相当于:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
要显示与EST timestamp
(东部标准时间)相同的时间点:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
是的,AT TIME ZONE 'UTC'
两次。第一个将timestamp
值解释为返回类型的(给定)UTC时间戳timestamptz
。第二个转换timestamptz
到timestamp
在给定的时间区“EST” -什么在这个时间点独特的时区EST显示时钟。
例子
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
返回8(或9)个相同的行,其中的timestamptz列具有相同的UTC timestamp 2012-03-05 17:00:00
。第9行碰巧在我所在的时区起作用,但这是一个陷阱。见下文。
①具有时区名称和时区缩写(夏威夷时间)的第6-8行受DST(夏令时)的影响,并且可能会有所不同,尽管当前没有变化。像这样的时区名称'US/Hawaii'
会自动识别DST规则和所有历史性班次,而像的缩写HST
则只是一个固定偏移量的哑码。您可能需要为夏季/标准时间附加一个不同的缩写。该名称可以正确解释给定时区的任何时间戳。缩写很便宜,但是对于给定的时间戳,它必须是正确的缩写:
夏令时并不是人类提出过的最聪明的想法。
②第9行,标记为加载footgun的作品对我来说,只是巧合。如果将文字显式转换为timestamp [without time zone]
,则任何时区偏移都将被忽略!仅使用裸时间戳。然后,该值将timestamptz
在示例中自动强制为与列类型匹配的值。对于此步骤,timezone
假定当前会话的设置,+1
在我的情况下(欧洲/维也纳)碰巧是同一时区。但对于您而言,可能并非如此-这将带来不同的价值。简而言之:不要将timestamptz
文字转换为,timestamp
否则会丢失时区偏移。
你的问题
用户存储时间,例如2012年3月17日晚上7点。我不希望时区转换或时区被存储。
时区本身从不存储。使用上述方法之一输入UTC时间戳。
我仅使用用户指定的时区来获取用户本地时区中当前时间“之前”或“之后”的记录。
您可以对不同时区的所有客户端使用一个查询。
对于绝对的全球时间:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
对于根据本地时钟的时间:
SELECT * FROM tbl WHERE time_col > now()::time
还不厌倦背景信息吗?手册中还有更多内容。