MongoDB $减去一个持续时间的日期,并考虑给定时区的DST变化

问题描述 投票:0回答:2

我们有一个特定的需求,我们需要在时间字段上执行聚合,同时考虑到特定的“边际”并处理跨越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中重现这种行为吗?您是否有关于如何解决这些问题的一些提示?

也许这可能是一个解决方案,但我听说过一种在聚合中操纵时间的新方法,这可以解决我的问题吗?

mongodb time aggregation-framework mongoid dst
2个回答
1
投票

处理带有时区的日期的最佳做法是以UTC格式执行所有数学运算。这意味着:

  • 如果用户输入是本地时间,则在存储之前将日期/时间转换为UTC。
  • 按UTC时间执行所有操作。
  • 向用户呈现/呈现日期/时间时,转换为当时适合的任何时区(用户查看最初输入时间的时间或时区)。

本地(非UTC)时间的以下两个操作之间也存在差异:

  • 一次添加24小时
  • 每天添加1天(时间可能会改变23,24或25小时,具体取决于是否越过DST边界)

应用程序需要明确它想要的两种行为中的哪一种。

鉴于问题中提供的代码,如果starts_atsafety_margin_starts_at是模型字段,它们应该以UTC格式存储时间。

Mongoid文档在此处提供了有关时区的其他指导:https://docs.mongodb.com/mongoid/master/tutorials/mongoid-configuration/#time-zones


0
投票

示例文档的UTC日期没有DST,可以像你一样安全地减去millis。我想你的代码中会出现导致错误的内容。

预期2019-03-25 13:35:52.357000000 +0000在2019-03-25 15:35:52 +0100的0.1之内

在13:35和15:35之间有2小时的差异,这看起来不正确。

这就是你在聚合中获得法国时间的方式:

{ '$project': {
  safety_margin_starts_at: {
    '$subtract': ['$starts_at', safety_margin_duration]
  },
},
{ '$addFields': {
        safety_margin_starts_at_fr: { $dateToString: {
            date: "$d",
            format: "%Y-%m-%dT%H:%M:%S.%L%z",
            timezone: "Europe/Paris"
        } }    
} }
© www.soinside.com 2019 - 2024. All rights reserved.