我正在开发一个查询 PostgreSQL 数据库的 Spring Boot 应用程序。
我有一个
timestamptz
专栏。我的应用程序的最终用户希望在自己的时区查看这些时间戳,并包含偏移量。例如,
给定一行包含
2023-08-10T12:00:00.000+00
当用户从时区查询时
'US/Eastern'
然后他们应该看到的输出是
2023-08-10T08:00:00.000-04
听起来很简单,但考虑到会有来自不同时区的多个并发请求,我很难弄清楚如何处理这个问题。
如果我在 SQL 控制台中执行此操作,它会给出我想要的精确结果,
SET LOCAL TIME ZONE 'US/Eastern';
SELECT to_char('2023-08-10T12:00:00.000+00'::TIMESTAMPTZ, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF');
2023-08-10T08:00:00.000000-04
但是,我不知道如何在我的 Spring Boot 应用程序中使用它。
;
中执行由
PreparedStatement
因此,回答这些问题中的任何一个都会有很大的帮助:
只需使用子句
at time zone
将服务器的时间戳转换为用户的时区,这里是一个例子:
select '2023-08-10T12:00:00.000+00' original_timestamptz
,'2023-08-10T12:00:00.000+00' at time zone 'US/Eastern' converted,
split_part(to_char('2023-08-10T12:00:00.000+00' at time zone 'US/Eastern',
'YYYY-MM-DD"T"HH24:MI:SS.MSOF'),'+',1)||
(select split_part(utc_offset::text,':',1) from pg_timezone_names where name = 'US/Eastern')
with_offset;
有必要“手动”将偏移量添加到所需结果,因为表达式
timestamp with time zone AT TIME ZONE zone
返回 timestamp without time zone
,如 官方文档 中所示
事实证明这相当复杂。这是我通过结合 Pepe 的回答和评论中的所有想法而提出的满足原始要求的函数:
CREATE OR REPLACE FUNCTION display_at_zone(
tstz TIMESTAMPTZ,
display_zone VARCHAR
) RETURNS VARCHAR
AS
$$
DECLARE
at_zone TIMESTAMP;
utc_offset INTERVAL;
hours INT;
minutes INT;
string_no_zone VARCHAR;
BEGIN
at_zone = tstz AT TIME ZONE display_zone;
utc_offset = at_zone - tstz;
hours = EXTRACT(HOURS FROM utc_offset);
minutes = ABS(EXTRACT(MINUTES FROM utc_offset));
string_no_zone = TO_CHAR(at_zone, 'YYYY-MM-DD"T"HH24:MI:SS.MS');
RETURN string_no_zone || TO_CHAR(hours, 'SG00:') || TO_CHAR(minutes, 'FM00');
END
$$ IMMUTABLE LANGUAGE plpgsql;
这(至少)有两个问题:
java.time
执行完全相同的操作相比,性能是 更差,而不是更好。display_timezone
参数是偏移量,则不起作用,但这可以轻松解决。