【问题标题】:MSSQL and EntityFramework - Storing and reading utc datetimeMSSQL 和 EntityFramework - 存储和读取 utc 日期时间
【发布时间】:2021-09-29 19:00:24
【问题描述】:

我请求更改当前类型为“时间”的列,而不是只捕获时间,我需要捕获所谓的“UTC 时间”。

我的想法是创建一个包含所有时区的固定代码列表,然后将其作为 FK 引用到适当的表中。

我的问题是:

  • “时间”类型的列是否也包含有关时区的信息(UTC,例如 15:00:00 +2 (gmt + 2)),如果没有,您能否建议我为该列提供另一种类型?
  • 我可能需要将它分成两列吗?例如:[15:00:00] - 开始时间,[+2:00] - UtcOffset
  • EF 插入:当我插入到数据库时,对于那个特定的列,我是否应该将我的 DateTime 对象转换为例如 DateTimeOffset

提前致谢。

【问题讨论】:

  • 如果您只是将 UTC 时间存储在您的列中,难道一切都没有问题吗?无需存储任何时区信息,因为它始终是 UTC,如果您需要在特定时区显示某些内容,只需在前端执行?
  • 嗯,实际上,我在世界各地有一些房子,每个房子都有自己的时区。所以基本上,我需要将偏移信息与每个房子一起存储。因此,当我从不同的时区预约/访问那所房子时,我需要知道我的房子在哪个时区,然后在那个时区保存预约。
  • 是的,您需要为 house 设置时区,而不是时间。因此,当那所房子有问题时,请在前端显示正确的时间。
  • 所以基本上,我想出了 mssql 中的列 StartTime time,以及其他 nvarchar 列名称 TimeZone。例如,StartTime 是 "12:00:00",TimeZone 是 "+2:00"。那么如何在 c# 中结合这两个 props 来获取 DateTimeOffset obj?
  • @JamesZ 有很多人认为存储 UTC 就足够了。事实并非如此,因为补偿确实会发生变化——一年两次。 DST 规则也会发生变化。当您必须显示日期时,您不知道时区规则是什么。在Falsehoods programmers believe about time UTC 中出现了两次,其中第一个谬误最重要:The local time offset (from UTC) will not change during office hours.

标签: asp.net sql-server entity-framework timezone-offset


【解决方案1】:

从您问题中的 cmets 来看,听起来您正在构建一个预约调度系统。我将以此为基础回答,因为您的具体问题与您描述的场景不太一致。

首先,了解 time zoneoffset 之间的关系是一对多的关系,这一点很重要。一个时区可以有多个偏移量。换句话说,时区不是偏移量,而是时区偏移量。

  • 时区表示本地时间始终相同的地理区域。它由字符串 ID 标识,例如 "America/Los_Angeles"(IANA 时区 ID)或 "Pacific Standard Time"(Windows 时区 ID)。在 .NET 中,您将在具有 Id 实例属性或 FindSystemTimeZonesById 之类的方法的 TimeZoneInfo 对象上使用它们。

  • 偏移量类似于-07:00+05:30 甚至+13:45。任何给定的偏移量仅适用于特定的日期和时间。例如,在America/Los_Angeles 中,-08:00-07:00 应用取决于夏令时是否在给定时间点生效。请记住,DST 并不是偏移量不同的唯一原因 - 许多时区在其历史上的某个时刻改变了它们的标准时间。

此外,它被称为偏移量,因为它与 UTC 有一定的偏差。 UTC 本身总是有一个零偏移量,由+00:00 或有时由Z 描述。它类似于 GMT,除了它的定义方式。 UTC 普遍适用,无处不在。 GMT 技术上仅适用于本初子午线。它们都指零偏移。在大多数情况下,您应该更喜欢说“UTC”。

接下来,您应该将应用程序逻辑在未来调度和当前/过去记录保存之间分开。

现在/过去是两者中较容易的。由于实际发生的时间点,本地时间及其与 UTC 的偏移量是永远固定的。您可以将本地时间及其偏移量存储在单个 .NET DateTimeOffset 结构中(映射到 SQL Server 中的 datetimeoffset 字段)。换句话说,您可以简单地存储2021-07-27T12:00:00-08:00

  • 请注意,您可以改为存储等效的 UTC 日期和时间,即 2021-07-27T20:00:00+00:00。但是,您已经丢失了本地时间,因此如果您想查看那个时间,则需要使用原始时区转换回来。有些人更喜欢这样,但我认为存储原始值更有用。

对于未来的调度,情况有点不同。考虑到一个约会的偏移量可能与同一时区的下一个约会的偏移量不同。还要考虑到,在您安排约会的时间和约会的时间之间,适用偏移量的定义可能更改。 (您安排得越远,这种可能性就越大。)

因此,对于每个位置,您不应存储偏移量,而应存储时区标识符。将TimeZoneId 添加到存储每个位置(或每个约会,具体取决于您的模型架构)的对象。使用TimeZoneInfo.GetSystemTimeZones 列出可用时区。 DisplayName 属性可以显示给您的用户,并且选定的Id 属性被分配给TimeZoneId

接下来,您必须考虑是安排单个约会还是创建定期约会模式。

  • 对于单个约会,您只需要该约会的本地日期和时间。您可以使用 .NET DateTime 结构(在 SQL 中使用 datetime2)。不要应用偏移量,也不要转换为 UTC。只需存储提供的信息。

  • 对于定期约会,您需要仔细考虑所提供的信息并准确存储所提供的信息。例如,如果约会是每隔一个星期二的 10:00,您需要存储“10:00”、“星期二”和“2 周间隔”。每种数据类型将根据您选择存储和应用它们的方式而有所不同。例如,您可以为 10:00 使用 time 类型,但您可以使用整数存储其他值。不同模式的约会可以以不同的方式存储。

    • 另外,有些人喜欢使用包含 CRON 表达式的字符串来存储模式。您可以通过 Google 搜索了解更多详情。

现在,您拥有安排约会和在约会发生后记录约会所需的一切。但是缺少一个部分 - 您可能需要一些即将到来的约会表,这些表很容易查询。为此,您有几个选择:

  • 您可以通过某种后台作业创建单独的表。它会定期查询所有约会,使用他们的信息来计算下一个即将到来的约会时间,并将其插入。您可以将其存储在DateTimeOffset 中,可以是本地时间,也可以是UTC。 (无论哪种方式,SQL Server 都将始终在等效的 UTC 时间计算索引。)

  • 您可以在约会中添加另一个字段,以显示下一个实际约会时间。然后,您可以在创建或更新约会时或当该约会发生时计算下一个即将到来的约会,并相应地更新表。

请记住,无论采用哪种方法,您都需要定期检查时区数据更新(通过 Windows 更新或在 Linux 上保持tzdata 软件包最新)。您还需要定期重新计算未来的约会时间,以防时区数据被修改而影响约会。

如果所有这些听起来都超级复杂 - 抱歉,但确实如此。在全球范围内正确地跨时区进行调度具有挑战性。如果您希望它更简单,您可能需要查看预制解决方案,例如 Quartz.NET,您可以将其集成到您的应用程序中。

【讨论】:

  • 现在,如果我们能说服 SQL Server 团队完全支持 IANA tzdb 就好了。 (想想看,自 2019 年以来,他们是否宣布了任何新功能?)
猜你喜欢
  • 2018-03-03
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 2023-03-21
  • 2011-06-06
  • 2013-08-10
  • 1970-01-01
  • 2019-06-15
相关资源
最近更新 更多