【问题标题】:SQL Server DateTimeOffset matching same "day"SQL Server DateTimeOffset 匹配相同的“天”
【发布时间】:2012-09-05 00:12:17
【问题描述】:

我的应用程序需要收集全球所有地点的“星期二”购买,其中“星期二”是该地点的星期二(不考虑时区)。如果用户下周需要重新运行报表,我仍然需要获取“上周二”的数据。我们所有的数据都使用 DateTimeOffset 存储。

所以 2012 年 9 月 4 日 00:00:00 -7 到 2012 年 9 月 4 日 23:59:59 -7 必须匹配 2012 年 9 月 4 日 00:00:00 +11 至 2012 年 9 月 4 日 23:59:59 +11 当我执行 WHERE 子句时。

我无法在 WHERE 子句中转换为 UTC,因为这将获取伦敦“星期二”的数据(取决于 DST),而不是该地点的星期二。

我尝试从 DateTimeOffset 转换为 DateTime,但这似乎转换为 UTC。 (在我的测试中,通过 9/1/12 到 9/30/12 获得了 8/31/12 的数据。)

用 TSQL 做这样的事情有诀窍吗?

谢谢!

【问题讨论】:

  • 澄清一下,每个位置的datetime 有不同的时区?

标签: sql tsql datetimeoffset


【解决方案1】:

恕我直言

DateTimeOffset = DateTime+Offset(来自 UTC)

因此,您的数据已经代表了客户的本地日期和时间。只需将其转换为 DateTime,您将获得客户端的本地日期和时间。

但是,如果您想将偏移量添加到日期时间并想要生成的日期时间,那么

DECLARE @PurchaseDate DATETIMEOFFSET(7) = CAST('2007-05-08 12:30:29.1234567 +5:00' AS  datetimeoffset(7)) 

SELECT  CAST(SWITCHOFFSET (@PurchaseDate , '+00:00') AS DATETIME)

查看此博客了解更多信息。

http://blogs.msdn.com/b/bartd/archive/2009/03/31/the-death-of-datetime.aspx

【讨论】:

  • SWITCHOFFSETDATETIMEOFFSET 调整到不同的时区,但它仍然指的是相同的实际时刻。我相信您的 SELECT 示例实际上只会显示原始购买的 UTC 时间,而不是所需的本地时间。 (您答案的第一部分对于该问题是正确的。第二部分可能不相关,但可能会误导所提出的问题。)
  • 在 Entity Framework 6 中这到底是怎么做到的?似乎没有任何方法可以获取或比较 DateTimeOffset 的 DateTime 部分。它不支持 DateTime.Date 也不支持 DateTimeOffset.DateTime,因此无法进行此转换。最接近它的是 DbFunctions.TruncateTime(但没有 TruncateOffset),它无论如何都会生成讨厌的 SQL。
【解决方案2】:

当将DATETIMEOFFSET 转换为DATETIME 时,它会将日期和时间作为值中的偏移量,然后简单地删除时区。转换为DATETIME 时也是如此。因此,我认为您可以简单地将列转换为 DATE 并将其与您希望匹配的仅日期值进行比较:

DECLARE @targetDate DATETIME2 = '2012-09-04' --Or we could use DATE here
SELECT [PurchaseId], [PurchaseTime], CAST([PurchaseTime] AS DATE) AS "PurchaseDate"
FROM [Purchases]
WHERE CAST([PurchaseTime] AS DATE) = @targetDate

我不确定这会有多高效(如果提供者真的很聪明,希望不会很糟糕——SQL Server 可能会这样),但您也可以通过限制原始列值来改进它:

DECLARE @targetDate DATETIME2 = '2012-09-04' --DATETIME2 so we can adjust by hours
SELECT [PurchaseId], [PurchaseTime], CAST([PurchaseTime] AS DATE) AS "PurchaseDate"
FROM [Purchases]
WHERE CAST([PurchaseTime] AS DATE) = @targetDate --Keep only the local-date matches
    AND [PurchaseTime] >= DATEADD(hh, -14, @targetDate) --Up to 14-hour time zone offset
    AND [PurchaseTime] <= DATEADD(hh, 38, @targetDate) --24 hours later plus 14

这应该有效地索引到一组可能性,然后在转换为本地日期时正确过滤。请注意,时区偏移最长可达 14 小时(我知道新西兰最远的时间是 +13:00,但根据MSDN,它们可以达到 +/- 14:00)并且@targetDate 将在开始当天,所以它比较早 14 小时和 24+14=38 小时后。 DATETIME2 与 DATETIMEOFFSET 具有相同的范围和精度,因此它比原始 DATETIME 更好(但它也可以与 DATETIME 一起使用)。

【讨论】:

  • 这对 DateTimeOffset 字段的索引有何影响?一旦你转换列,这不会阻止索引被使用吗?我开始认为 DateTimeOffset 没用,我们最好同时存储本地 DateTime 和 UTC DateTime。
  • @Triynko,这就是为什么我建议在原始列值上设置边界。我的示例使用一个 DATETIME2 初始化为简单日期(意味着午夜开始该日期)并通过两端的最大时区偏移量进行调整,以将 [PurchaseTime] DATETIMEOFFSET 列(可能是索引)缩小到 @targetDate 可能属于的范围任何当地购买时间。 DATETIMEOFFSET 是通过绝对(UTC)时间而不是它们的本地时间表示来相互比较的,所以它应该可以工作。本地转换测试适用于这些匹配项。如果您好奇,请进一步试验。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-19
  • 2018-11-10
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 2013-08-17
相关资源
最近更新 更多