我如何为 psql (Postgres) 编写一个函数来匹配 C 中 gettimeofday() 的 return 值?
How do I write a function for psql (Postgres) that will match the return value of gettimeofday() in C?
我有一对程序需要在 运行 时使用公共时间戳。一个程序将使用 now()
写入列类型为 timestamp without time zone
的 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 )) as bigint);$_$;
这返回了一个我们可以使用的通用值。我们不关心时区——只关心在两个程序之间获得共同的价值。当我们迁移到 Debian Jessie 和 Postgres 9.4 时,这个 psql extract()
函数的行为似乎发生了变化。
机器 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
机器 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(值相差 6 小时,可能基于当地时区 CST)
select extract('epoch' from now() at time zone 'uct'); -> 1457579365
./gettimeofday -> 1457557765
机器 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()
的正确方法是什么?还是实施方式发生了变化,以至于我需要两个版本?
好吧,这显然只是 extract()
函数的行为变化。
http://www.postgresql.org/docs/9.5/static/release-9-2.html
Make EXTRACT(EPOCH FROM timestamp without time zone) measure the epoch
from local midnight, not UTC midnight (Tom Lane)
This change reverts an ill-considered change made in release 7.3.
Measuring from UTC midnight was inconsistent because it made the
result dependent on the timezone setting, which computations for
timestamp without time zone should not be. The previous behavior
remains available by casting the input value to timestamp with time
zone.
所以这个实现将跨平台工作:
CREATE FUNCTION ms_ts(ts timestamp without time zone) RETURNS bigint
LANGUAGE sql IMMUTABLE STRICT
AS $_$select cast(round(1000 * extract('epoch' from ::timestamptz)) as bigint);$_$;
我真的不在乎结果是否考虑时区因素,只是它的行为与 gettimeofday()
相同。
附录:另一个因素是 Postgres 设置的内部时区。可以使用以下方式查看:
show timezone;
如果不是 'localtime',则该值将与 gettimeofday() 不匹配。要为给定用户设置本地时间,请使用:
alter user yourusername set timezone='localtime';
"epoch" 是从固定点开始的秒数。传统上,在基于 unix 的系统上,这是 1970 年 1 月 1 日,UTC 午夜。
当然,如果您更改时区,那么它会更改与该固定点的时差。老实说,我不确定测量时间的想法,因为没有时区的时代是否有意义。实际上,我从来没有真正发现整个 "timestamp without time zone" 东西有多大用处。
所以 - 如果您想要一个绝对时间点(就像您在本例中所做的那样),请始终提供时区。
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)
我有一对程序需要在 运行 时使用公共时间戳。一个程序将使用 now()
写入列类型为 timestamp without time zone
的 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 )) as bigint);$_$;
这返回了一个我们可以使用的通用值。我们不关心时区——只关心在两个程序之间获得共同的价值。当我们迁移到 Debian Jessie 和 Postgres 9.4 时,这个 psql extract()
函数的行为似乎发生了变化。
机器 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
机器 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(值相差 6 小时,可能基于当地时区 CST)
select extract('epoch' from now() at time zone 'uct'); -> 1457579365
./gettimeofday -> 1457557765
机器 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()
的正确方法是什么?还是实施方式发生了变化,以至于我需要两个版本?
好吧,这显然只是 extract()
函数的行为变化。
http://www.postgresql.org/docs/9.5/static/release-9-2.html
Make EXTRACT(EPOCH FROM timestamp without time zone) measure the epoch from local midnight, not UTC midnight (Tom Lane)
This change reverts an ill-considered change made in release 7.3. Measuring from UTC midnight was inconsistent because it made the result dependent on the timezone setting, which computations for timestamp without time zone should not be. The previous behavior remains available by casting the input value to timestamp with time zone.
所以这个实现将跨平台工作:
CREATE FUNCTION ms_ts(ts timestamp without time zone) RETURNS bigint
LANGUAGE sql IMMUTABLE STRICT
AS $_$select cast(round(1000 * extract('epoch' from ::timestamptz)) as bigint);$_$;
我真的不在乎结果是否考虑时区因素,只是它的行为与 gettimeofday()
相同。
附录:另一个因素是 Postgres 设置的内部时区。可以使用以下方式查看:
show timezone;
如果不是 'localtime',则该值将与 gettimeofday() 不匹配。要为给定用户设置本地时间,请使用:
alter user yourusername set timezone='localtime';
"epoch" 是从固定点开始的秒数。传统上,在基于 unix 的系统上,这是 1970 年 1 月 1 日,UTC 午夜。
当然,如果您更改时区,那么它会更改与该固定点的时差。老实说,我不确定测量时间的想法,因为没有时区的时代是否有意义。实际上,我从来没有真正发现整个 "timestamp without time zone" 东西有多大用处。
所以 - 如果您想要一个绝对时间点(就像您在本例中所做的那样),请始终提供时区。
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)