mongodb - MongoDB $减去具有持续时间的日期并考虑给定时区的 DST 更改
问题描述
我们有一个特定的需求,我们需要对时间字段执行聚合,同时考虑到特定的“边距”并处理跨越 DST 边界。
假设我们有一个项目,该项目在某个时间开始并在我们实际想要全局创建的所有事件时间starts_at
结束ends_at
starts_at - safety_margin
ends_at
所以我们有看起来像这样的文件
project: { "starts_at": "2019-04-01T10:28:05.711Z", "ends_at": "2019-01-29T10:28:05.711Z" }
safety_margin
现在是 1.weeks 的常数(在我们的 MongoDB 聚合中转换为毫秒)
在我们的聚合中,我们有以下阶段
{ '$project': {
safety_margin_starts_at: {
'$subtract': ['$starts_at', safety_margin_duration]
},
}
对于给定上下文的 DST,这将完全失败:
- 法国时区(在 4 月 1 日之前偏移 +2,在 4 月 1 日之后变为 +1)
- 一个在 1 日或 4 月之后开始的项目(因此在 FR 中偏移 +1)并且具有安全边际它成为 3 月 25 日(偏移 +2)
在我们的规范中,这将产生错误
project.starts_at # => Mon, 01 Apr 2019 15:35:52 CEST +02:00
project.safety_margin_starts_at # => Mon, 25 Mar 2019 15:35:52 CET +01:00
# [Running our test]
expected 2019-03-25 13:35:52.357000000 +0000 to be within 0.1 of 2019-03-25 15:35:52 +0100
# The first figure is the one returned from the aggregation
我们的应用程序代码实际上做了一个智能减法,starts_at - safety_margin_duration
在减法时无论 DST 变化如何,都变成同一天/同一小时。是否可以在 MongoDB 中重现这种行为?您对如何解决这些问题有一些建议吗?
也许这可能是一个解决方案,但我听说过一种在聚合中操纵时间的新方法,这能解决我的问题吗?
解决方案
处理带有时区的日期的最佳实践是以 UTC 执行所有数学运算。这表示:
- 如果用户输入是本地时间,则在存储之前将日期/时间转换为 UTC。
- 在 UTC 时间执行所有操作。
- 在向用户呈现/呈现日期/时间时,转换为当时适当的时区(用户查看时间或最初输入时间的时区)。
以下两个操作在本地(非 UTC)时间上也存在差异:
- 将 24 小时添加到时间
- 将 1 天添加到时间(时间可能会更改 23、24 或 25 小时,具体取决于是否跨越 DST 边界)
应用程序需要清楚它想要的两种行为中的哪一种。
鉴于问题中提供的代码,如果starts_at
和safety_margin_starts_at
是模型字段,它们应该以 UTC 存储时间。
Mongoid 文档在此处提供有关时区的其他指南:https ://docs.mongodb.com/mongoid/master/tutorials/mongoid-configuration/#time-zones
推荐阅读
- c++ - 当您更喜欢 C++ 中的虚函数而不是模板时?
- macos - .dmg 和 .app 文件有什么区别?
- c# - 将数据库中的 3 列合并到 datagridview 中的 1 列
- reactjs - React Router v5 - 没有精确道具的嵌套路由
- ios - 为什么我收到线程 1:信号 SIGABRT
- git - 如何正确设置上游并将主分支中的更改反转为先前的提交状态?
- php - PHP发布值但数据库未在托管上更新
- reactjs - 反应原生导航 5 从底部选项卡注销?
- r - 在 R 中以具有修改名称的先前列的值为条件创建多个新列
- sequelize.js - 多对多场景中的多个包含