使用mongodb在utc中存储日期时如何处理时区问题?

问题描述 投票:31回答:4

我有一个mongodb集合,其中每个文档都有一些属性和一个utc时间戳。我需要从集合中提取数据并使用聚合框架,因为我使用集合中的数据在用户界面上显示一些图表。但是,我需要根据用户的时区进行聚合。假设我知道用户的时区(通过浏览器或其他方式传递请求),有没有办法使用聚合框架根据[客户端]时区进行聚合?

mongodb timezone mongoid aggregation-framework
4个回答
11
投票

你要求的东西目前正在MongoDB issue SERVER-6310讨论。

我在a discussion thread的链接中找到了这个。

对于按日期进行的任何分组,这个问题很常见,包括SQL数据库和NoSQL数据库。事实上,我最近在RavenDB中解决了这个问题。有一个很好的描述问题和RavenDB解决方案here

MongoDB问题讨论了一种解决方法,类似于我在上面的评论中所描述的。您可以预先计算您感兴趣的当地时间,然后按这些时间进行分组。

用这两种方法很难覆盖世界上的每个时区。您应该决定对您的用户群有意义的少数目标区域,例如我在RavenDB文章中描述的每个办公室方法。

更新:此问题已于2017年7月在MongoDB中解决(版本3.5.11)。解决方案在上面的第一个链接中描述,但简而言之,它们为聚合表达式中的日期引入了一种新的对象格式:{ date: <dateExpression>, timezone: <tzExpression> },允许您指定聚合时使用的时区。有关Mongo文档中的另一个示例,请参阅here


37
投票

除了Matt Johnson提到的SERVER-6310之外,另一个解决方法是使用$project运算符在UTC时区中添加或减去“将时间”转移到正确的本地区域。事实证明,您可以以毫秒为单位添加或减去时间。

例如,假设我有一个名为orderTime的Date字段。我想查询EDT。那是距UTC的-4小时。那是4 * 60 * 60 * 1000毫秒。

所以我会写下面的投影,以便在当地时间获取所有记录的day_ordered

db.table.aggregate( 
    { $project : { orderTimeLocal : { $subtract : [ "$orderTime", 14400000] } } },
    { $project : { day_ordered : { $dayOfYear : "$orderTimeLocal" } } })

16
投票

上面提出的每种方法都可以很好地完成,但是由于有一个新版本的mongodb,你可以在聚合框架中使用$let,这样你就可以动态创建变量,从而避免在分组之前需要$project。现在,您可以使用$let创建一个变量,该变量将保存本地化时间,并在$group运算符中使用它。

就像是:

db.test.aggregate([
   {$group: { 
        _id: { 
             $let: { 
                 vars: {  
                     local_time: { $subtract: ["$date", 10800000]} 
                 }, 
                 in: { 
                    $concat: [{$substr: [{$year: "$$local_time"}, 0, 4]}, 
                              "-", 
                              {$substr: [{$month: "$$local_time"}, 0, 2]}, 
                              "-", 
                              {$substr: [{$dayOfMonth: "$$local_time"}, 0, 2]}]
                 }
              }
         }, 
         count: {$sum: 1}
     }
 }])

请注意,在块/变量的定义中使用$let,并且该块/变量的值是子表达式"in"的返回值,其中使用了上面定义的变量。


1
投票

我在mongoose plugin中找到了一个解决方案来规范化存储日期时区。

© www.soinside.com 2019 - 2024. All rights reserved.