当数据依赖于日期时间时,在数据库中保存日期时间和时区信息的最佳实践

2024-04-02

关于在数据库中保存日期时间和时区信息有很多问题,但更多的是总体水平。这里我想讲一个具体的案例。

系统规格

  • 我们有一个订单系统数据库
  • 它是一个多租户系统,租户可以使用任意时区(它是任意的,但每个租户只有一个时区,保存在租户表中一次并且永不更改)

DB需要覆盖业务规则

  • 当租户向系统下订单时,订单号根据本地日期时间计算(它实际上不是一个数字,而是某种标识符,例如ORDR-13432-Year-Month-Day)。目前精确计算并不重要,重要的是它取决于租户本地日期时间
  • 我们还希望能够在系统级别选择放置在某些 UTC 日期时间之间的所有订单,而不管租户如何(用于一般系统统计/报告)

我们最初的想法

  • 我们最初的想法是在整个数据库中保存 UTC 日期时间,当然,保持租户相对于 UTC 的时区偏移,并让使用数据库的应用程序始终将日期时间转换为 UTC,以便数据库本身始终使用 UTC 运行。

方法一

  • 保存本地租户的日期时间对于每个租户来说都是很好的,但是我们在查询时遇到了问题,例如:

    SELECT * FROM ORDERS WHERE OrderDateTime BETWEEN UTCDateTime1 AND UTCDateTime2
    

这是有问题的,因为OrderDateTime此查询中的意思是根据租户的不同时刻。当然,这个查询可能包括 join toTenants表获取本地日期时间偏移量,然后计算OrderDateTime即时进行调整。这是可能的,但不确定这是否是一个好方法?

方法2

  • 另一方面,当保存 UTC 日期时间时,当我们计算 OrderNumber 时,因为 UTC 中的日/月/年可能与本地日期时间中的不同

让我们举个极端的例子;假设租户比 UTC 早 6 小时,他的本地日期时间是2017-01-01 02:00。 世界标准时间将是2016-12-31 20:00。此时下的订单应获取 OrderNumber'ORDR-13432-2017-1-1'但如果保存 UTC 则会得到ORDR-13432-2016-12-31.

在这种情况下,在数据库中创建订单时,我们应该获取 UTC 日期时间、租户偏移量,并根据重新计算的租户本地时间编译 OrderNumber,但仍以 UTC 保存 DateTime 列。

问题

  1. 处理这种情况的首选方法是什么?
  2. 是否有一个很好的解决方案来保存 UTC 日期时间,因为由于系统级报告,该解决方案对我们来说非常好?
  3. 如果要保存 UTC,方法 2) 是处理这些情况的好方法还是有更好/推荐的方法?

[UPDATE]

基于 Gerard Ashton 和 Hugo 的评论:

最初的问题不清楚租户是否可以更改时区以及如果政治当局更改时区属性或某些领土的时区会发生什么。 当然,这是极其重要的,但这不是这个问题的中心。我们可以在一个单独的问题中解决这个问题。

为了回答这个问题,我们假设租户不会改变位置。该位置的时区属性或时区本身可能会发生变化,这些变化将在系统中与该问题分开处理。


Hugo的回答大部分是正确的,但我会补充几个要点:

  • 当您存储客户的时区时,请勿存储数字偏移量。正如其他人指出的那样,与 UTC 的偏移仅适用于单个时间点,并且很容易因 DST 和其他原因而更改。相反,您应该存储时区标识符,最好是 IANA 时区标识符作为字符串,例如"America/Los_Angeles"。阅读更多内容时区标签 wiki https://stackoverflow.com/tags/timezone/info.

  • Your OrderDateTime字段应该绝对代表 UTC 时间。但是,根据您的数据库平台,您可以选择多种存储方式。

    • 例如,如果使用 Microsoft SQL Server,一个好的方法是将本地时间存储在datetimeoffset列,保留与 UTC 的偏移量。请注意,您在该列上创建的任何索引都将基于 UTC 等效值,因此在执行范围查询时您将获得良好的查询性能。

    • 如果使用其他数据库平台,您可能希望将 UTC 值存储在timestamp场地。有些数据库还有timestamp with time zone,但要明白这并不意味着stores时区或偏移量,它只是意味着它可以在您存储和检索值时隐式为您进行转换。如果您打算始终代表 UTC,那么通常timestamp(没有时区)或只是datetime更合适。

  • 由于上述任一方法都将存储 UTC 时间,因此您还需要考虑如何执行需要本地时间值索引的操作。例如,您可能需要根据用户所在时区的日期创建每日报告。为此,您需要按本地日期进行分组。如果您尝试在查询时根据 UTC 值计算该值,您最终将扫描整个表。

    处理这个问题的一个好方法是为本地创建一个单独的列date(或者甚至可能是当地的datetime根据您的需要,但是not a datetimeoffset or timestamp)。这可以是您单独填充的完全隔离的列,也可以是基于其他列的计算/计算列。在索引中使用此列,以便您可以按本地日期进行筛选或分组。

  • 如果您采用计算列方法,您将需要知道如何在数据库中的时区之间进行转换。有些数据库有一个convert_tz内置函数可以理解 IANA 时区标识符。

    如果您使用的是 Microsoft SQL Server,则可以使用新的AT TIME ZONE https://learn.microsoft.com/en-us/sql/t-sql/queries/at-time-zone-transact-sqlSQL 2016 和 Azure SQL DB 中的函数,但仅适用于 Microsoft 时区标识符。要使用 IANA 时区标识符,您需要第三方解决方案,例如我的SQL Server 时区支持 https://github.com/mj1856/SqlServerTimeZoneSupport项目。

  • 在查询时,避免使用BETWEEN陈述。它是完全包容的。它适用于整个日期,但是当您有时间参与时,最好进行半开范围查询,例如:

    ... WHERE OrderDateTime >= @t1 AND OrderDateTime < @t2
    

    例如,如果@t1是今天的开始,@t2将是明天的开始。

关于评论中讨论的用户时区已更改的场景:

  • 如果您选择计算数据库中的本地日期,则您需要担心的唯一情况是位置或企业是否在没有发生“区域分割”的情况下切换时区。区域分割是指引入新的时区标识符,该标识符覆盖已更改的区域,包括其旧规则和新规则。

    例如,在撰写本文时添加到 IANA tzdb 的最新区域是America/Punta_Arenas,当智利南部决定保留 UTC-3 而智利其他地区 (America/Santiago) 在 DST 结束时返回 UTC-4。

    但是,如果两个时区边界上的一个小地区决定更改它们所遵循的一侧,并且区域分割不合理,那么您可能会针对旧数据使用新时区的规则。

  • 如果您单独存储本地日期(在应用程序中计算,而不是在数据库中计算),那么您不会有任何问题。用户将时区更改为新时区,所有旧数据仍然完好无损,并且新数据与新时区一起存储。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当数据依赖于日期时间时,在数据库中保存日期时间和时区信息的最佳实践 的相关文章

随机推荐

  • 如何在 vb.net 中使用 openfiledialog 打开文件?

    如何使用 openfiledialog 打开文件 下面是我的代码 Dim Fs As StreamReader With OpenFD FileName Title Open Text File InitialDirectory c Fil
  • 更改字符串字符时出现分段错误(核心转储)

    为什么更改字符串字符会导致分段错误 核心转储 char str string str 0 S segmentation fault core dumped 解决方案很简单 用以下方式声明你的字符串 char str string 您应该这样
  • AWS API Gateway 不存在“Access-Control-Allow-Origin”标头

    我遇到了 API 网关的问题 我已经浏览了 AWS 论坛上的所有其他答案 也浏览了他们的文档 但仍然没有任何乐趣 我正在尝试使用 AWS API 网关设置一个 API 该网关调用 Lambda 函数来读取 写入 DynamoDB 中的表 D
  • SSIS 中的别名参数

    我在 SSIS 中使用 OLE DB 命令 其 SQL 命令如下所示 UPDATE DBO CLIENT SET TimeZoneID DaylightSavingTime ModifiedBy MicrosPropertyID IsOff
  • Haskell 函子隐含定律

    类型分类百科全书 http www haskell org haskellwiki Typeclassopedia says 类似的论点还表明 任何满足第一定律 fmap id id 的 Functor 实例也将自动满足第二定律 实际上 这
  • 检测 Asp.net 上的浏览​​器关闭

    我想在注销时执行一些功能 如果用户直接关闭浏览器 则需要执行相同的功能 我们无法在页面卸载上执行此操作 因为我的网站中有 100 多个页面 因为这将在每个页面的重定向上起作用页 谢谢
  • 操作员 '??'不能应用于“System.DateTime”类型的操作数

    我收到以下错误 Operator cannot be applied to operands of type System DateTime foreach EndServReward r in reward if con State Co
  • R TwitteR 包授权错误

    我正在关注最新更新推特主页 https github com geoffjentry twitteR 我无法通过授权流程 library devtools install github twitteR username geoffjentr
  • php 中 eregi() 的替代方案 [重复]

    这个问题在这里已经有答案了 因此 我在邮件脚本中使用了 eregi 但最近 我收到该函数已弃用的错误 那么 替换以下代码的最简单方法是什么 if eregi A Z0 9 A Z0 9 A Z 2 4 trim POST email 任何帮
  • Pandas:如何创建年周变量?

    我有一个带有日期时间的数据框 dates pd date range 9 25 2010 periods 10 freq D df pd DataFrame col dates df col pd to datetime df col df
  • 连接无序线段

    我的算法生成一个 通常 数千条线段 全是二维 的列表 我需要将它们连接成大型折线 这些生成的折线可能是闭合的或开放的 但它们永远不会自相交 线段没有方向 即可能需要翻转线段才能将其连接到相邻线段 找到这些折线的极快方法是什么 我必须实时执行
  • 什么是可以轻松集成到现有应用程序的优秀 Ruby on Rails 论坛?

    什么是可以轻松集成到现有应用程序的优秀开源 RoR 3 论坛 可选功能 OpenID 支持 Haml SCSS 模板 支持表情符号 YouTube 图像等 我可能会对其进行很多更改 而且我对 Ruby 的了解仍然很弱 因此干净的 带注释的代
  • 如何反转 sed 输出?

    我正在阅读大约 500 个字符的行 我怎样才能让 sed 不用某些东西替换该字符串 而是用某些东西替换该行的其余部分 简而言之 我想删除指定字符串周围的所有文本 使用 awk 删除列不起作用 因为匹配的字符串前后有不确定数量的字符 有任何想
  • 如何将 JFreechart 添加到面板?

    我尝试寻找答案来使其正常工作 但没有成功 我快要失去理智了 所以这是我的问题 我最近下载了 JFreeChart 来创建图表并在我的 GUI 中实现它们 这是我想要插入图形的 GUI 和面板 以白色标记 报告1 http www freei
  • Fiddler 测试 API Post 传递 [Frombody] 类

    我有一个非常简单的 C APIController 名为 TestController 其 API 方法如下 HttpPost public string HelloWorld FromBody Testing t return t Nam
  • 组合框架更新 UI 无法正常工作

    我想尝试Combine框架 使用非常简单 按aUIButton 并更新UILabel 我的想法是 添加发布者 Published var cacheText String 订阅 cacheText assign to text on cac
  • 无法在类似字节的对象上使用字符串模式 - python 的重新错误[重复]

    这个问题在这里已经有答案了 我正在做 python 挑战并试图熟悉 python 所以在不看答案的情况下 我尝试使用 python 的 url 阅读器来读取 html 然后找到所需的字母 但是在下面的代码中我收到一个错误 最初是 pytho
  • 为 Windows 服务创建 MSI 安装程序

    我在 Visual Studio 2008 中有一个 Windows 服务项目 我还添加了安装和部署项目 不幸的是 当我安装它时 该服务没有显示在服务 MMC 管理单元中 我已经将 ProjectInstaller 添加到服务项目中 并且已
  • 使用APNS发送远程推送通知的送达报告 - IOS

    我正在使用 APNS 发送远程推送通知 要求是当通知发送给应用程序用户时 即使用户没有点击通知甚至没有看到通知 根据通知 id i 将发送收据发送到 Web 服务器 即调用 Web 服务 已收到 APNS 没有提供交付报告 他们是 APNS
  • 当数据依赖于日期时间时,在数据库中保存日期时间和时区信息的最佳实践

    关于在数据库中保存日期时间和时区信息有很多问题 但更多的是总体水平 这里我想讲一个具体的案例 系统规格 我们有一个订单系统数据库 它是一个多租户系统 租户可以使用任意时区 它是任意的 但每个租户只有一个时区 保存在租户表中一次并且永不更改