如何在忽略时区问题的情况下从 SQL Server 获取 DateTime 数据?
Posted
技术标签:
【中文标题】如何在忽略时区问题的情况下从 SQL Server 获取 DateTime 数据?【英文标题】:How can I get DateTime data from SQL Server ignoring time zone issues? 【发布时间】:2012-11-23 10:30:20 【问题描述】:我的情况是我们将数据存储在SQL Server 数据库中,支持2005 年起。存储 DateTime 值时,它是客户端的本地时间。我需要能够在任何地方的任何其他客户端上获取该日期,而不考虑其他客户端可能位于的任何时区。
因此,例如,当纽约的用户输入“2012-12-20 00:00”的 DateTime 值时,我希望加利福尼亚的用户看到相同的 DateTime 值。这些 DateTime 值不应该尊重时区的差异,但这就是我看到的情况。目前,SQL Server 会将 DateTime 作为“2012-12-19 21:00”交付给加利福尼亚用户。请注意由于从 EST 更改为 PST 导致的 -3 小时回滚,现在是前一天的情况(出于本次讨论的目的,请忘记 DST 问题)。
逐字逐句获取数据,而不是按时区翻译,这是我需要完成的工作。那么你能提供什么建议呢?
需要考虑的一些代码:
这是表格:
CREATE TABLE [dbo].[tblMeterReadings](
[fldMeterReadingsID] [int] IDENTITY(1,1) NOT NULL,
[fldMeterID] [int] NOT NULL,
[fldUser] [varchar](50) NULL,
[fldBy] [varchar](50) NULL,
[fldDateOfReading] [datetime] NULL,
[fldReading] [float] NULL,
[fldIsArchived] [bit] NULL DEFAULT ((0)),
我们有一个 Sql 类来完成这项工作,在下面的示例中,“sql”是该类的一个对象。使用参数化查询进行调用,其中@data0 是要存储在 SQL DateTime 字段中的 DateTime 对象:
sql.Execute(@"INSERT INTO tblMeterReadings (fldMeterID, fldDateOfReading) VALUES (" + MeterID + ", @data0)", Date);
最终,这会设置一个 SqlCommand,分配参数,并触发一个 command.ExecuteNonQuery() 调用。
现在,要检索日期,我只需将其选择到 DataTable 中(同样,使用我们的 Sql 类助手):
DataTable myTable = sql.readDataTable(@"SELECT fldMeterID, fldDateOfReading FROM tblMeterReadings");
所以我看到的是,在数据库本身中,日期是“2012-12-20 00:00”,正如我所料,但是当我在调试中检查 myTable 内容时,我看到该表中的日期是"2012-12-19 21:00"。
数据库是在 SQL Server 上创建的,该 SQL Server 运行在处于 EST 状态的机器上。但是,我的机器设置为 PST。因此,在 SELECT 中将 DateTime 值传递给我的方式有所不同。
我错过了什么?
【问题讨论】:
相关:***.com/q/2532729/1583 如何将日期写入数据库?您的问题应该通过简单地从日期时间中删除时区信息来解决,然后再将其保存到数据库中。 @DanielHilgarth - 如果您的意思是将其存储为 UTC,那么我会同意。但是已经有很多数据了,在我们能够通过大规模转换之前,我只能试着按我的方式按摩它。 除了发布您的代码,正如其他人建议的那样,您应该给出存储日期时间值的表的定义。 @DonBoitnott:我并不是说将其存储为 UTC。对于 NY 和 CA,这仍然会有所不同。我的意思是存储它没有时区信息,因为那是你想要的。 【参考方案1】:感谢各位好心人的意见,以及根据您的建议进行的一些研究,我们已经解决了我们的问题。
特别感谢ebyrob 的帮助和此链接:http://support.microsoft.com/kb/842545
该链接最终被证明是灵丹妙药。
基本问题是,当您构造 DataTable 或 DataSet 时,有关创建它的时区的信息会用它进行编码。然后,如果您将该对象通过连接传递到存在于不同时区的计算机,则其中的任何 DateTime 值都将根据时区的差异进行调整。
根据这种理解,解决方法是将所有 DateTime 数据列的 DateTimeMode
属性设置为 DataSetDateTime.Unspecified
。这可以防止序列化后的日期调整,而是逐字传递 DateTime 值。
我还想指出,文章指定了 DataSet,但我们已经证明 DataTable 也存在漏洞。事实上,我很确定它归结为 DataColumn 本身(无论如何,那些具有 DateTime 类型的)。我认为这篇文章也说明了这一点。
再次感谢;我想我们现在明白了!
【讨论】:
感谢发布调查结果。【参考方案2】:您可以在任何地方查看特定时区的日期:
DateTime dtDateOfReading = TimeZoneInfo.ConvertTime(
Convert.ToDateTime(myTables.Rows[i]["fldDateOfReading"]),
dtTimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
);
如果您想要查看多个时区,则需要存储时区以及日期或它所在的客户站点。
注意:请注意转换后的 DateTime 值,因为它们会有一种 Unspecified ,在某些情况下可能(错误地)假定为本地。
编辑:为 SQL 连接设置时区以匹配 SQL Server 的时区可能更容易,但这篇文章:http://support.microsoft.com/kb/842545 似乎表明 SqlDataAdapter 至少可能不支持时区配置。
【讨论】:
我考虑过这个。但是这种方法不是假设我知道创建 DateTime 值的时区吗?我的例子可能暗示了这一点,但事实并非如此。我只是有一个 DateTime,但必须假设我对此一无所知。事实上,我什至不在乎。如果它在数据库中为“2012-12-20 00:00”,这就是我想要的,好像时区根本不存在。 你知道SQL服务器的时区是EST。例如,您可能还知道所有客户 NYSEG 的时间都在美国东部标准时间。 (我认为您真正看到的是服务器时区的差异,而不是单个记录时区......) 正如您在编辑中所说的,我四处寻找如何“设置 SQL 连接的时区”,但我不知道如何。你能给我指一个页面,或者提供一个示例代码行吗?顺便说一句:那篇知识库文章很好读,我认为这正是我面临的根本问题。 不幸的是,这是知识库文章的重点......我认为您无法更改行为,因为它是“设计使然”。【参考方案3】:将 UTC 日期存储在数据库中。仅在客户端显示时转换为本地时间。
服务器端,使用getutcdate()
而不是getdate()
。
【讨论】:
我完全同意。不幸的是,这在短期内是不可能的。事实上,一些客户已经拥有 20 多年的数据。这实际上是因为我们在 Access 数据库中总是“侥幸成功”,而最近迁移到 SQL Server 的举动暴露了这个弱点。以上是关于如何在忽略时区问题的情况下从 SQL Server 获取 DateTime 数据?的主要内容,如果未能解决你的问题,请参考以下文章
我可以在不使用管道的情况下从 Azure Synapse 查询 SQL Server 数据库吗?
我可以在不中断复制的情况下从 SQL Server 复制订阅者数据库中删除特定数据吗?
如何让 Json.Net 在不忽略子属性的情况下从 documentDB 序列化/反序列化动态/通用对象?