如何将
SET TIME ZONE
应用到NpgsqlCommand?
尝试 1. CommandText 中的
SET TIME ZONE
,用 ;
与选择分开
using var cmd = connection.CreateCommand();
cmd.CommandText = "SET TIME ZONE '+02:00'; select \"t0\".\"start_time\" from \"task\".\"tasks\" \"t0\" where \"t0\".\"number\" = 217";
await connection.OpenAsync();
using var reader = await cmd.ExecuteReaderAsync();
尝试 2.使用
NpgsqlBatch
await connection.OpenAsync();
using var batch = new NpgsqlBatch(connection);
batch.BatchCommands.Add(new NpgsqlBatchCommand("SET TIME ZONE '+02:00'"));
batch.BatchCommands.Add(new NpgsqlBatchCommand("select \"t0\".\"start_time\" from \"task\".\"tasks\" \"t0\" where \"t0\".\"number\" = 217"));
await batch.PrepareAsync();
using var reader = await batch.ExecuteReaderAsync();
start_time
列的类型为 timestamptz
两次尝试都没有成功。据我了解:1次尝试没有成功,因为
SET TIME ZONE
用分号npgsql
将命令文本分成批次。所以 ;
是单独批次的,不适用于 SET TIME ZONE
。有关批次的更多信息,请参见 select
https://www.infoq.com/news/2022/06/Database-Command-Batching/当我在 PgAdmin 中运行查询时,
npgsql
有效 - 我得到了不同的结果
SET TIME ZONE
SET TIME ZONE '+02:00';
select
"t0"."start_time"
from "task"."tasks" "t0"
where "t0"."number" = 217
没有效果是有充分理由的。NpgSQL 将
SET TIME ZONE
作为 DateTime 值返回,其 timestamp with time zone
是 DateTimeKind.UTC。这在类型映射文档中进行了解释。 PostgreSQL 本身将
Kind
存储为 UTC 值,没有原始偏移量或时区名称。来自 PostgreSQL 文档: 对于带时区的时间戳,内部存储的值始终为 UTC(通用协调时间,传统上称为格林威治标准时间,GMT)。指定了显式时区的输入值将使用该时区的适当偏移量转换为 UTC。如果输入字符串中未指定时区,则假定其处于系统 TimeZone 参数指示的时区,并使用时区的偏移量将其转换为 UTC。
当输出带有时区值的时间戳时,它始终从 UTC 转换为当前时区,并显示为该区域的本地时间。要查看另一个时区的时间,请更改时区或使用 AT TIME ZONE 构造
NpgSQL 返回的值是准确的 - UTC 日期时间。如果应用程序将其视为 UTC 时间,那么这应该不是问题。不幸的是,像
timestamp with timezone
ignore
ToString
属性这样的函数。在这种情况下,一个不错的选择是使用 DateTime.ToLocalTime将值转换为 C# 中的本地日期,例如:
Kind
另一个选项是将值转换为
while(reader.Read())
{
...
var date=reader.GetDateTime(3).ToLocalTime();
...
}
,确保 UTC 和本地永远不会混淆:
DateTimeOffset
另一种选择是使用
var date=new DateTimeOffset(reader.GetDateTime(3));
让 PostgreSQL 将时间转换为特定时区。但不要使用偏移量,因为这会根据 DST 规则而改变。使用 IANA 时区名称要安全得多,例如:
AT TIME ZONE