在 PostgreSQL 上将 YYYYMMDD 转换为具有特定时区的午夜的最简单方法?

Easiest way to convert YYYYMMDD to the midnight with specific timezone on PostgreSQL?

我得到 YYYYMMDD 格式的日期。 我需要将此列转换为具有特定时区的时间戳。 best/easiest 的方法是什么?

所以如果我有 20210101 我想在我的 TZ 中得到 2021-01-01 00:00:00.000000

这是对 timestamp with time zone 的转换。您可以将其格式化为您想要的任何文本表示形式。我的时区是东欧时间 EET (GMT + 2),所以

select to_date('20211203', 'YYYYMMDD')::timestamp at time zone 'EET';
-- 2021-12-03 00:00:00.000 +0200

严格来说,你的要求是矛盾:

I want to get 2021-01-01 00:00:00.000000 in my TZ.

你展示了一个 timestamp (timestamp without time zone) 文字,它完全与时区的概念正交(并且不知道)。

但你想要它 "in my TZ",这意味着 timestamptz (timestamp with time zone) 值,其中相应的文字包含时间偏移量如:2021-01-01 00:00:00.000000+01.

由于格式 YYYYMMDD 是明确的 ISO 格式,您可以直接安全地转换为 datetimestamp。强制转换为 timestamp 会自动假定时间分量 00:00。产生你想要的 timestamp '2021-01-01 00:00'.

SELECT '20211203'::timestamp;

如果你想要结果类型 timestamp,我们到这里就完成了。

如果你想要结果类型 timestamptz,有一个快捷的快捷方式:

SELECT '20211203'::timestamptz;

类型转换采用当前时区设置。 但是 这引入了对运行时设置的依赖。出了名的不可靠,仅在您可以确定当前设置的情况下才可取...

确定且通常可取的方法是使用 AT TIME ZONE 结构显式定义目标时区。假设您的时区是 'Europe/Vienna':

SELECT '20211203'::timestamp AT TIME ZONE 'Europe/Vienna';

使用 time zone name. Time zone abbreviations 对输入转换来说是危险的,并且可能会因夏令时 (DST) 或其他官僚废话而失败。 'CET'(欧洲中部时间)适用于“标准时间”期间的时间戳。在 DST 期间,您必须使用 'CEST'(欧洲中部夏令时)。

夏令时完全是胡说八道,但包括欧盟在内的一些国家仍然没有摆脱它。

db<>fiddle here - 注意 dbfiddle 默认运行时区 GB

现在您有 timestamptz 值代表给定时区中一天的开始 (00:00)。不要被 显示 timestamptz 值所迷惑。它始终根据当前时区设置进行调整,但它始终代表那个唯一的时间点,只是显示方式不同。

您明白时区本身永远不会存储在 timestamptz 值中,对吗?尽管 timestamp with time zone 听起来 可能如此。参见:

要强制显示,请使用to_char()或其他一些函数来生成所需的字符串。手册中有专门的页面Data Type Formatting Functions

相关:

为了避免事情过于复杂:

create table dt_test (id integer, ts_fld timestamp, tsz_fld timestamp with time zone);

insert into dt_test values (1, '20211203'::date, '20211203'::date);

 select * from dt_test ;
 id |       ts_fld        |        tsz_fld         
----+---------------------+------------------------
  1 | 2021-12-03 00:00:00 | 2021-12-03 00:00:00-08

A date 将被视为午夜用于时间戳目的。因此,要么将它用作 date 知道它将是午夜,要么进行显式转换:

select '20211203'::date::timestamp, '20211203'::date::timestamptz;
      timestamp      |      timestamptz       
---------------------+------------------------
 2021-12-03 00:00:00 | 2021-12-03 00:00:00-08

--Which can be shortened to:

select '20211203'::timestamp, '20211203'::timestamptz;
      timestamp      |      timestamptz       
---------------------+------------------------
 2021-12-03 00:00:00 | 2021-12-03 00:00:00-08

取决于您是否要保留时区偏移量。对于便携性 2021-12-03 00:00:00-08 将是更好的选择。

这个问题似乎假设时间戳可以在写入时格式化并且它们保留时区信息。他们不能也不会,这将问题一分为二:

1:如何解释基于文本的时间戳。

create table test_timestamps as 
select ('20211203'||'Japan')::timestamptz as "your_timestamp";

PostgreSQL 将其保存在 binary format in UTC 中,因此格式化和时区的整个概念不适用于存储的 timestamp 类型数据。它仅在读取时格式化,并在需要时转换为不同的时区。

2:如何在读取时以特定格式获取它们。

select to_char(
         "your_timestamp" AT TIME ZONE 'UTC', 
         'YYYY-MM-DD HH24:MI:SS.US TZHTZM') 
from test_timestamps;

其中 at time zone 仅当您希望它 return 其他地方观察到的时间戳时才是必需的。