如何为psql(Postgres)编写一个与C中gettimeofday()的返回值匹配的函数?

时间:2016-03-08 15:13:38

标签: postgresql debian

我有一对需要在运行时使用公共时间戳的程序。一个程序将使用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()的适当方式是什么?或者只是改变实施方式,以至于我需要两个版本?

2 个答案:

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