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

时间:2013-08-17 09:59:10

标签: mongodb timezone mongoid aggregation-framework

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

4 个答案:

答案 0 :(得分:37)

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

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

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

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

答案 1 :(得分:16)

上面提到的每种方法都可以很好地完成,但是由于有一个新版本的mongodb,从2.6开始你可以在聚合框架中使用$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"的返回值,其中使用了上面定义的变量。 / p>

答案 2 :(得分: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

答案 3 :(得分:1)

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