在 mongodb 的本地时区聚合
Aggregating in local timezone in mongodb
我正在 mongodb 和 nodejs 中构建将在意大利使用的应用程序。意大利时区为 +02:00。这意味着如果有人在 7 月 11 日的 01:am 保存了一些数据,那么它将被保存为 7 月 10 日的 11:00 下午,因为 mongo 以 UTC 保存日期。
我们需要显示日期明智的 tx 计数。所以我按日期分组。但它显示前一天的交易。应该有什么解决方法。
> db.txs.insert({txid:"1",date : new Date("2015-07-11T01:00:00+02:00")})
> db.txs.insert({txid:"2",date : new Date("2015-07-11T05:00:00+02:00")})
> db.txs.insert({txid:"3",date : new Date("2015-07-10T21:00:00+02:00")})
> db.txs.find().pretty()
{
"_id" : ObjectId("55a0a55499c6740f3dfe14e4"),
"txid" : "1",
"date" : ISODate("2015-07-10T23:00:00Z")
}
{
"_id" : ObjectId("55a0a55599c6740f3dfe14e5"),
"txid" : "2",
"date" : ISODate("2015-07-11T03:00:00Z")
}
{
"_id" : ObjectId("55a0a55699c6740f3dfe14e6"),
"txid" : "3",
"date" : ISODate("2015-07-10T19:00:00Z")
}
> db.txs.aggregate([
{ $group:{
_id: {
day:{$dayOfMonth:"$date"},
month:{$month:"$date"},
year:{$year:"$date"}
},
count:{$sum:1}
}}
])
{ "_id" : { "day" : 11, "month" : 7, "year" : 2015 }, "count" : 1 }
{ "_id" : { "day" : 10, "month" : 7, "year" : 2015 }, "count" : 2 }
它显示 7 月 10 日有 2 个 tx,7 月 11 日有 1 个。但是我们需要显示 7 月 11 日的 2 个交易和 7 月 10 日的 1 个交易。
在意大利
实际上是 7 月 11 日
db.txs.insert({txid:"1",date : new Date("2015-07-11T01:00:00+02:00")})
发生但 mongo 存储日期为:
ISODate("2015-07-10T23:00:00Z")
处理时区是一个 "client" 问题,因此您应该通过时区偏移修改 "query" 次,以便允许 "local" 在 UI 中选择时间,并且等等。 UI 显示也是如此,其中日期以当地时间表示。
这同样适用于您的聚合原则。只需调整时区偏移量即可。应用日期数学而不是使用日期聚合运算符:
var tzOffset = 2;
db.txs.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$add": [
{ "$subtract": [ "$date", new Date("1970-01-01") ] },
tzOffset * 1000 * 60 * 60
]},
{ "$mod": [
{ "$add": [
{ "$subtract": [ "$date", new Date("1970-01-01") ] },
tzOffset * 1000 * 60 * 60
]},
1000 * 60 * 60 * 24
]}
]
},
"count": { "$sum": 1 }
}}
]).forEach(function(doc){
printjson({ "_id": new Date(doc._id), "count": doc.count })
});
这给你:
{ "_id" : ISODate("2015-07-10T00:00:00Z"), "count" : 1 }
{ "_id" : ISODate("2015-07-11T00:00:00Z"), "count" : 2 }
所以当你 $subtract
一个 BSON 日期与另一个 BSON 日期时,结果是自 unix 纪元以来的毫秒数。然后简单地通过 "adding" 再次调整 "timezone offset" 正数为正数或负数为后数,再次从时间值转换为有效毫秒数。
然后四舍五入是一个简单的模 $mod
以从 "number of milliseconds in a day" 中获取余数并将其删除以仅将调整后的日期四舍五入到当天。
由于所有语言库 "Date" 对象都将 epoch 中的毫秒(或秒)作为构造函数参数,因此此处生成的数值很容易重新转换回日期。
所以,这又是关于修改数据响应以从您的 "client" 的 "locale" 呈现,而不是关于更改数据的存储方式。如果你想在你的应用程序中真正定位,那么你可以在任何地方应用时区偏移量的修改,就像上面介绍的那样。
--
实际上,您只需在聚合框架本身中创建日期,再进行一些日期计算。只需将纪元日期添加回转换后的日期即可:
db.txs.aggregate([
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$add": [
{ "$subtract": [ "$date", new Date(0) ] },
tzOffset * 1000 * 60 * 60
]},
{ "$mod": [
{ "$add": [
{ "$subtract": [ "$date", new Date(0) ] },
tzOffset * 1000 * 60 * 60
]},
1000 * 60 * 60 * 24
]}
]},
new Date(0);
]
},
"count": { "$sum": 1 }
}}
])
在 mongo 版本 3.6 时区已添加,mongo doc
提取带有时区的日期部分的表达式是
{ date: <dateExpression>, timezone: <tzExpression> }
我们可以在获取日期部分时指定时区或偏移量
流水线
> db.txs.aggregate([
... { $group:{
... _id: {
... day: {$dayOfMonth: {date :"$date", timezone : "Europe/Rome"}}, // timezone
... month: {$month: {date : "$date", timezone : "+02:00"}}, //offset
... year: {$year: {date : "$date", timezone : "+02:00"}} //offset
... },
... count:{$sum:1}
... }}
... ])
结果
{ "_id" : { "day" : 10, "month" : 7, "year" : 2015 }, "count" : 1 }
{ "_id" : { "day" : 11, "month" : 7, "year" : 2015 }, "count" : 2 }
>
列表 timezone
我正在 mongodb 和 nodejs 中构建将在意大利使用的应用程序。意大利时区为 +02:00。这意味着如果有人在 7 月 11 日的 01:am 保存了一些数据,那么它将被保存为 7 月 10 日的 11:00 下午,因为 mongo 以 UTC 保存日期。 我们需要显示日期明智的 tx 计数。所以我按日期分组。但它显示前一天的交易。应该有什么解决方法。
> db.txs.insert({txid:"1",date : new Date("2015-07-11T01:00:00+02:00")})
> db.txs.insert({txid:"2",date : new Date("2015-07-11T05:00:00+02:00")})
> db.txs.insert({txid:"3",date : new Date("2015-07-10T21:00:00+02:00")})
> db.txs.find().pretty()
{
"_id" : ObjectId("55a0a55499c6740f3dfe14e4"),
"txid" : "1",
"date" : ISODate("2015-07-10T23:00:00Z")
}
{
"_id" : ObjectId("55a0a55599c6740f3dfe14e5"),
"txid" : "2",
"date" : ISODate("2015-07-11T03:00:00Z")
}
{
"_id" : ObjectId("55a0a55699c6740f3dfe14e6"),
"txid" : "3",
"date" : ISODate("2015-07-10T19:00:00Z")
}
> db.txs.aggregate([
{ $group:{
_id: {
day:{$dayOfMonth:"$date"},
month:{$month:"$date"},
year:{$year:"$date"}
},
count:{$sum:1}
}}
])
{ "_id" : { "day" : 11, "month" : 7, "year" : 2015 }, "count" : 1 }
{ "_id" : { "day" : 10, "month" : 7, "year" : 2015 }, "count" : 2 }
它显示 7 月 10 日有 2 个 tx,7 月 11 日有 1 个。但是我们需要显示 7 月 11 日的 2 个交易和 7 月 10 日的 1 个交易。
在意大利
实际上是 7 月 11 日db.txs.insert({txid:"1",date : new Date("2015-07-11T01:00:00+02:00")})
发生但 mongo 存储日期为:
ISODate("2015-07-10T23:00:00Z")
处理时区是一个 "client" 问题,因此您应该通过时区偏移修改 "query" 次,以便允许 "local" 在 UI 中选择时间,并且等等。 UI 显示也是如此,其中日期以当地时间表示。
这同样适用于您的聚合原则。只需调整时区偏移量即可。应用日期数学而不是使用日期聚合运算符:
var tzOffset = 2;
db.txs.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$add": [
{ "$subtract": [ "$date", new Date("1970-01-01") ] },
tzOffset * 1000 * 60 * 60
]},
{ "$mod": [
{ "$add": [
{ "$subtract": [ "$date", new Date("1970-01-01") ] },
tzOffset * 1000 * 60 * 60
]},
1000 * 60 * 60 * 24
]}
]
},
"count": { "$sum": 1 }
}}
]).forEach(function(doc){
printjson({ "_id": new Date(doc._id), "count": doc.count })
});
这给你:
{ "_id" : ISODate("2015-07-10T00:00:00Z"), "count" : 1 }
{ "_id" : ISODate("2015-07-11T00:00:00Z"), "count" : 2 }
所以当你 $subtract
一个 BSON 日期与另一个 BSON 日期时,结果是自 unix 纪元以来的毫秒数。然后简单地通过 "adding" 再次调整 "timezone offset" 正数为正数或负数为后数,再次从时间值转换为有效毫秒数。
然后四舍五入是一个简单的模 $mod
以从 "number of milliseconds in a day" 中获取余数并将其删除以仅将调整后的日期四舍五入到当天。
由于所有语言库 "Date" 对象都将 epoch 中的毫秒(或秒)作为构造函数参数,因此此处生成的数值很容易重新转换回日期。
所以,这又是关于修改数据响应以从您的 "client" 的 "locale" 呈现,而不是关于更改数据的存储方式。如果你想在你的应用程序中真正定位,那么你可以在任何地方应用时区偏移量的修改,就像上面介绍的那样。
--
实际上,您只需在聚合框架本身中创建日期,再进行一些日期计算。只需将纪元日期添加回转换后的日期即可:
db.txs.aggregate([
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$add": [
{ "$subtract": [ "$date", new Date(0) ] },
tzOffset * 1000 * 60 * 60
]},
{ "$mod": [
{ "$add": [
{ "$subtract": [ "$date", new Date(0) ] },
tzOffset * 1000 * 60 * 60
]},
1000 * 60 * 60 * 24
]}
]},
new Date(0);
]
},
"count": { "$sum": 1 }
}}
])
在 mongo 版本 3.6 时区已添加,mongo doc
提取带有时区的日期部分的表达式是
{ date: <dateExpression>, timezone: <tzExpression> }
我们可以在获取日期部分时指定时区或偏移量
流水线
> db.txs.aggregate([
... { $group:{
... _id: {
... day: {$dayOfMonth: {date :"$date", timezone : "Europe/Rome"}}, // timezone
... month: {$month: {date : "$date", timezone : "+02:00"}}, //offset
... year: {$year: {date : "$date", timezone : "+02:00"}} //offset
... },
... count:{$sum:1}
... }}
... ])
结果
{ "_id" : { "day" : 10, "month" : 7, "year" : 2015 }, "count" : 1 }
{ "_id" : { "day" : 11, "month" : 7, "year" : 2015 }, "count" : 2 }
>
列表 timezone