我有一对需要在运行时使用公共时间戳的程序。一个程序将使用timestamp without time zone
写入列类型为now()
的Postgres表。另一个程序将使用gettimeofday()
C函数返回的值写入二进制文件。
我们的目标是两个版本的Debian / Postgres。第一个是Wheezy和Postgres 9.1。很长一段时间以来,我们使用以下函数来获得常用值:
C(编译为程序gettimeofday
)
struct timeval tv;
memset(&tv, 0, sizeof(tv));
gettimeofday(&tv, NULL);
unsigned long val = tv.tv_sec * 1000000;
val += tv.tv_usec;
val /= 1000000;
printf("%lu\n", val);
PSQL
CREATE FUNCTION ms_ts(ts timestamp without time zone) RETURNS bigint
LANGUAGE sql IMMUTABLE STRICT
AS $_$select cast(round(1000 * extract('epoch' from $1)) as bigint);$_$;
这返回了我们可以使用的公共值。我们不关心时区 - 只是在两个程序之间获得共同的价值。当我们转到Debian Jessie和Postgres 9.4时,看起来这个psql extract()
函数的行为发生了变化。
Machine 1,Debian Wheezy,Postgres 9.1,psql 9.1.20
date: Tue Mar 8 09:06:30 CST 2016
hwclock: Tue 08 Mar 2016 09:15:11 AM CST -0.375327 seconds
select now() -> 2016-03-08 09:07:23.183816-06
select extract('epoch' from cast('2016-03-08 09:00:08.277701' as timestamp without time zone));
-> 1457449208.2777
Machine 2,Debian Jessie,Postgres 9.4,psql 9.4.6
date: Tue Mar 8 09:06:31 CST 2016
hwclock: Tue 08 Mar 2016 09:15:12 AM CST -0.890904 seconds
select now() -> 2016-03-08 09:07:23.60542-06
select extract('epoch' from cast('2016-03-08 09:00:08.277701' as timestamp without time zone));
-> 1457427608.2777
所以我研究并发现extract()
有一个选项at time zone [...]
来选择提取时代的时区。使用'uct'
作为时区似乎可以解决Jessie / Postgres 9.4上的问题,但它会弄乱Wheezy / Postgres 9.1。
计算机1,Debian Wheezy,Postgres 9.1,psql 9.1.20(值可能会有六个小时,可能基于当地时区CST)
select extract('epoch' from now() at time zone 'uct'); -> 1457579365
./gettimeofday -> 1457557765
Machine 2,Debian Jessie,Postgres 9.4,psql 9.4.6(值匹配)
select extract('epoch' from now() at time zone 'uct'); -> 1457558544.44372
./gettimeofday -> 1457558543
使用extract()
匹配gettimeofday()
的适当方式是什么?或者只是改变实施方式,以至于我需要两个版本?
答案 0 :(得分:0)
好的,这显然只是extract()
功能的行为改变。
http://www.postgresql.org/docs/9.5/static/release-9-2.html
制作EXTRACT(EPOCH FROM timestamp without time zone)测量纪元 从当地午夜,而不是UTC午夜(汤姆巷)
此更改将恢复在7.3版中进行的考虑不周的更改。 从UTC午夜测量是不一致的,因为它制作了 结果取决于时区设置,计算的时间 不应该是没有时区的时间戳。以前的行为 通过将输入值随时间推移到时间戳来保持可用 区。
因此,此实现将跨平台工作:
CREATE FUNCTION ms_ts(ts timestamp without time zone) RETURNS bigint
LANGUAGE sql IMMUTABLE STRICT
AS $_$select cast(round(1000 * extract('epoch' from $1::timestamptz)) as bigint);$_$;
我真的不关心时区中的结果因素,只是它与gettimeofday()
的行为相同。
附录:另一个因素是Postgres设定的内部时区。可以使用以下方式查看:
show timezone;
如果它不是'localtime',那么该值将与gettimeofday()不匹配。要为给定用户设置为localtime:
alter user yourusername set timezone='localtime';
答案 1 :(得分:0)
“纪元”是自固定点以来的秒数。传统上基于unix的系统,这是1970年1月1日,UTC的午夜。
当然,如果您更改时区,则会更改与该固定点的差异。说实话,我不确定从没有时区的时代开始测量时间的想法很有意义。实际上,我从来没有真正找到整个“没有时区的时间戳”的东西。
所以 - 如果你想要一个绝对的时间点,总是提供一个时区(就像你在这种情况下那样)。
richardh=> SELECT extract(epoch from '1970-01-01 00:00:00+00'::timestamptz);
date_part
-----------
0
(1 row)
richardh=> SELECT extract(epoch from '1970-01-01 00:00:00+01'::timestamptz);
date_part
-----------
-3600
(1 row)