截断带时区的时间戳时如何保留时区信息

问题描述 投票:0回答:1

TL;博士:

如何选择

'1999-12-31 20:59:59+00:00'::timestamptz
作为截断的日期时间信息 在不同的时区(例如没有分钟和秒),显示所使用的时区,例如
2000-01-01 07:00:00 +10:30


在我的多时区应用程序中,时间戳存储在 UTC 中,我想计算作为 SQL 查询的一部分提供的本地时区中按分钟、小时、天等分组的行数(本地时区)是用户设置的主题,而不是服务器配置的主题)

我尝试在时间戳上使用

date_trunc
,但时区信息未显示在最终产品中。

原因是我随后获取结果日期并将其传递给 PHP 的 DateTime 对象。在此过程中,如果时区不是结果字符串的一部分,则信息将丢失。

这是我到目前为止所尝试的,在 UTC 中进行了一些测试,试图验证我正在使用正确的输入数据:

CREATE TEMPORARY TABLE tstest (
    ts timestamptz not null
);

INSERT INTO tstest VALUES ('1999-12-31 20:59:59'::timestamptz at time zone 'UTC');

select 1 AS row, ts::text, 'Control: Original value. Correct in local time zone, showing TZ' AS observation from tstest union
select 2, (ts AT TIME ZONE 'UTC')::text, 'Control: Correct in UTC, but no TZ' from tstest union
select 3, (ts::timestamptz AT TIME ZONE 'Australia/Adelaide')::text, 'Test case: Correct in Adelaide, **but no TZ information**' from tstest union
select 4, date_trunc('hour', ts::timestamptz)::text, 'Truncated Control: Correct in local time zone, showing TZ' from tstest UNION
select 5, date_trunc('hour', ts::timestamptz AT TIME ZONE 'UTC')::text, 'Truncated Control: Correct in UTC, but no TZ' from tstest UNION
select 6, date_trunc('hour', ts::timestamptz AT TIME ZONE 'Australia/Adelaide')::text, 'Truncated Test case: Correct time in Adelaide, **but no TZ information**' from tstest UNION
select 7, to_char(date_trunc('hour', ts::timestamptz AT TIME ZONE 'Australia/Adelaide'), 'YYYY-mm-dd HH:MI:ss OF')::text, 'Truncated test case: Correct time in Adelaide, **but the TZ shown is incorrect**' from tstest UNION
select 8, concat(to_char(date_trunc('hour', ts::timestamptz AT TIME ZONE 'Australia/Adelaide'), 'YYYY-mm-dd HH:MI:ss')::text, ' Australia/Adelaide'), 'Desired result, **but concatenated manually**.' from tstest
;

导致:

ts 观察
1 1999-12-31 20:59:59+00 控制:原始值。本地时区正确,显示 TZ
2 1999-12-31 20:59:59 控制:UTC 正确,但 TZ 不正确
3 2000-01-01 07:29:59 测试用例:在阿德莱德正确,**但没有 TZ 信息**
4 1999-12-31 20:00:00+00 截断控制:在本地时区正确,显示 TZ
5 1999-12-31 20:00:00 截断控制:UTC 正确,但 TZ 不正确
6 2000-01-01 07:00:00 截断的测试用例:阿德莱德的正确时间,**但没有 TZ 信息**
7 2000-01-01 07:00:00 +00 截断的测试用例:阿德莱德的正确时间,**但显示的 TZ 不正确**
8 2000-01-01 07:00:00 澳大利亚/阿德莱德 期望的结果,**但手动连接**。

第 3 行、第 6 行和第 7 行是我想要的时间,但缺少(正确的)时区。第 8 行是我最终想要的(我认为由于 DST,TZ 名称比偏移量更好),但我发现很难接受我需要在将 TZ 感知时间戳转换为字符串后自己附加 TZ 信息。

我错过了什么?

以下是这些日期如何转换为 PHP 对象(= 为什么我需要时区成为 SELECT 结果中字符串输出的一部分):https://3v4l.org/u7A8q#v8.2.10

<?php

// @see https://stackoverflow.com/questions/77131068/how-to-retain-the-time-zone-info-when-truncating-a-timestamp-with-time-zone

date_default_timezone_set('UTC');

$dates = [
    '1999-12-31 20:59:59+00',
    '1999-12-31 20:59:59',
    '2000-01-01 07:29:59',
    '1999-12-31 20:00:00+00',
    '1999-12-31 20:00:00',
    '2000-01-01 07:00:00',
    '2000-01-01 07:00:00 +00',
    '2000-01-01 07:00:00 Australia/Adelaide'
];

foreach ($dates as $dateString) {
    $date = new \DateTimeImmutable($dateString);
    echo $dateString."\n-> ".$date->format('r')."\n\n";
}

生产:

1999-12-31 20:59:59+00
-> Fri, 31 Dec 1999 20:59:59 +0000

1999-12-31 20:59:59
-> Fri, 31 Dec 1999 20:59:59 +0000

2000-01-01 07:29:59
-> Sat, 01 Jan 2000 07:29:59 +0000

1999-12-31 20:00:00+00
-> Fri, 31 Dec 1999 20:00:00 +0000

1999-12-31 20:00:00
-> Fri, 31 Dec 1999 20:00:00 +0000

2000-01-01 07:00:00
-> Sat, 01 Jan 2000 07:00:00 +0000

2000-01-01 07:00:00 +00
-> Sat, 01 Jan 2000 07:00:00 +0000

2000-01-01 07:00:00 Australia/Adelaide
-> Sat, 01 Jan 2000 07:00:00 +1030

我见过类似的问题(时区感知date_trunc函数date_trunc在带有原始时间戳的时区date_trunc在带有原始时间戳的时区),但没有人关注我认为是我的问题:事实上,当使用

date_trunc
::timestamptz

时,时区信息会从结果中删除
php postgresql datetime timestamp-with-timezone
1个回答
0
投票

您的选择是将会话时区设置为您想要用于会话的时区,或者禁止手动连接字符串。如果您想在不可见重复的情况下执行后者,您可以编写一个函数来执行后者。

(本地时区是用户设置的主题,而不是服务器配置的主题)

这几乎已经是 PostgreSQL 的工作原理了。如果会话忽略为自己声明一个时区,服务器配置仅控制默认会话时区。因此,让每个会话的第一个或第二个命令设置用户首选时区(在数据库中查找,如果这是存储首选项的位置)

© www.soinside.com 2019 - 2024. All rights reserved.