TIMESTAMP与TIMESTAMPTZ计算时间差的结果哪个正确?
为什么TIMESTAMP和TIMESTAMPTZ的时间差不一样?哪个才正确?
这问题问得特别好,刚好能帮你把PostgreSQL里这两个时间类型的核心差异掰明白!
先看你的例子:
你执行了这段查询:
SELECT '2018-03-01'::TIMESTAMP - '2018-09-01'::TIMESTAMP, '2018-03-01'::TIMESTAMPTZ - '2018-09-01'::TIMESTAMPTZ;
得到的结果是:
TIMESTAMP差值:-184 daysTIMESTAMPTZ差值:-183 days -23:00:00
先搞懂两个类型的本质
首先得明确这俩类型到底存的是什么:
TIMESTAMP(不带时区的时间戳):它存的就是你输入的“本地时间字符串”,不带任何时区信息,相当于你墙上挂的钟显示的时间——不管你在纽约还是北京,它显示的都是2018-03-01,但这个字符串对应的绝对物理时间会随你的时区变化而变化。TIMESTAMPTZ(带时区的时间戳):它本质上存的是UTC时间(世界标准时间)。当你输入一个不带时区的字符串时,PostgreSQL会用当前会话的时区把它转换成UTC存储;查询时再转回到你的会话时区显示。它代表的是一个唯一的、不随时区改变的绝对时间点。
你的例子里差值不同的原因
你的结果差异,核心是夏令时(DST)的影响。假设你的会话时区是北美东部时区(EST/EDT):
'2018-03-01'::TIMESTAMPTZ:3月1日还在冬令时(EST,UTC-5),所以转换成UTC是2018-03-01 05:00:00'2018-09-01'::TIMESTAMPTZ:9月1日已经进入夏令时(EDT,UTC-4),转换成UTC是2018-09-01 04:00:00
这两个UTC时间的差值是:2018-09-01 04:00:00 - 2018-03-01 05:00:00 = 183天23小时,反过来减就是-183 days -23:00:00,和你得到的结果一致。
而TIMESTAMP的计算就简单粗暴了:直接算两个日期字符串的日历差,3月1日到9月1日刚好是184天,所以反过来减就是-184 days——它完全不管时区和夏令时的变化,只看“墙上的日期”差。
哪个结果才是正确的?
这取决于你的需求:
- 如果你要算的是日历上的日期差(比如“两个日期之间隔了多少天,不管实际流逝的时间”),那
TIMESTAMP的结果符合预期,但这种场景非常少(比如纯本地的日程安排,完全不跨时区)。 - 如果你要算的是实际流逝的物理时间差(比如这两个时间点之间真正过了多久),那
TIMESTAMPTZ的结果才是正确的——因为它考虑了时区转换和夏令时带来的时间偏移,反映的是绝对时间点的间隔。
为什么大家都推荐用TIMESTAMPTZ?
绝大多数业务场景中,我们需要处理的都是绝对时间点:比如用户的下单时间、系统的日志时间、航班起飞时间……这些时间应该是唯一的,不管你在哪个时区查看,都能对应到同一个物理时刻。
如果用TIMESTAMP,很容易出问题:比如你在EST时区存了一个订单时间'2018-03-01 12:00:00',当用户在UTC时区查看时,系统会直接显示这个字符串,但它在UTC时区对应的绝对时间和EST时区完全不同,会导致数据混乱。
而TIMESTAMPTZ存储的是UTC时间,不管你在哪个时区访问,都会自动转换成当地时间,保证时间的准确性和一致性。
内容的提问来源于stack exchange,提问作者Liondancer
相关产品推荐
相关产品推荐

