关于datetime:PostgreSQL – 如何在不同的时区呈现日期?

PostgreSQL - how to render date in different time zone?

我的服务器在中心时间。我想用东部时间呈现时间戳。

例如,我想把2012-05-29 15:00:00变为2012-05-29 16:00:00 EDT

我怎样才能做到?

to_char('2012-05-29 15:00:00'::timestamptz at time zone 'EST5EDT', 'YYYY-MM-DD HH24:MI:SS TZ')给出2012-05-29 16:00:00的值(无区域)。

to_char('2012-05-29 15:00:00'::timestamp at time zone 'EST5EDT', 'YYYY-MM-DD HH24:MI:SS TZ')2012-05-29 14:00:00 CDT错误。

这是可行的,但它是如此荒谬复杂,必须有一个更简单的方法:replace(replace(to_char(('2012-05-29 15:00:00'::timestamptz at time zone 'EST5EDT')::timestamptz, 'YYYY-MM-DD HH24:MI:SS TZ'), 'CST', 'EST'), 'CDT', 'EDT')


关键是在事务期间将本地时区切换到所需的显示时区:

1
2
3
4
5
BEGIN;
SET LOCAL timezone TO 'EST5EDT';
SELECT to_char('2012-05-29 15:00:00'::TIMESTAMP at TIME zone 'CDT',
  'YYYY-MM-DD HH24:MI:SS TZ');
END;

结果是:

2012-05-29 16:00:00 EDT

请注意,使用set [local] timezone时,需要使用完整的时区名称而不是缩写(例如,CST不起作用)。在pg_timezone_names视图中查找有效的选择。

要在类似于to_char()调用的上下文中使用该方法,我相信此函数可以完成以下任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE FUNCTION display_in_other_tz(
      in_t timestamptz,
      in_tzname text,
      in_fmt text) RETURNS text
AS $$
DECLARE
 v text;
 save_tz text;
BEGIN
  SHOW timezone INTO save_tz;
  EXECUTE 'SET local timezone to ' || quote_literal(in_tzname);
  SELECT to_char(in_t, in_fmt) INTO v;
  EXECUTE 'SET local timezone to ' || quote_literal(save_tz);
  RETURN v;
END;
$$ LANGUAGE plpgsql;


In fact, PG knows it all - to_char(x, 'TZ') differentiates CST from
CDT correctly, and at time zone EST5EDT respects DST as well.

当处理时间戳时,Postgres知道:

  • guc EDOCX1的设置〔0〕。
  • 数据类型。
  • 该值与"1970-1-1 0:0 UTC"以来timestamptimestamptz的秒数相同。(或者,准确地说:UT1。)
  • 日期/时间配置文件中其他时区的详细信息

在解释输入时,Postgres使用有关所提供时区的信息。在呈现时间戳值时,Postgres使用当前的timezone设置,但时区偏移量、缩写或名称仅用于计算输入的正确值。它们不会被保存。以后不可能提取这些信息。此相关答案中的详细信息:

你的"正确"例子几乎是正确的。to_char()TZ返回"cdt",用于中央时间和"cst"其他夏令时期间的时间戳。东部时间(EST/EDT)在当地时间切换夏令时-我引用维基百科:

The time is adjusted at 2:00 AM local time.

这两个时区在每年两小时内不同步。当然,这不会影响15:0016:00上的时间戳,仅在02:00附近。

一个完全正确的解决方案-很像@daniel已经发布的内容,稍微简化了:

1
2
3
4
5
6
BEGIN;
SET LOCAL timezone TO 'EST5EDT';
SELECT to_char('2012-05-29 15:00 CST6CDT'::timestamptz
             , 'YYYY-MM-DD HH24:MI:SS TZ')
RESET timezone;  -- only if more commands follow in this transactions
END;

The effects of SET LOCAL last only till the end of the current transaction.

关于SET LOCAL的手册。