У меня есть коллекция mongodb, где каждый документ имеет некоторые атрибуты и временную метку utc. Мне нужно вытащить данные из коллекции и использовать структуру агрегации, потому что я использую данные из коллекции, чтобы отобразить некоторые диаграммы на пользовательском интерфейсе. Однако мне нужно выполнить агрегацию в соответствии с часовым поясом пользователя. Предполагая, что я знаю часовой пояс пользователя (переданный в запросе из браузера или каким-либо другим способом), есть ли способ использовать структуру агрегации для агрегирования на основе часового пояса [клиента]?
Как бороться с проблемой часового пояса при хранении дат в utc с использованием mongod?
Ответ 1
То, о чем вы просите, в настоящее время обсуждается в проблема MongoDB SERVER-6310.
Я нашел это в ссылке из темы обсуждения.
Проблема распространена для любой группировки по дате, включая базы данных SQL и базы данных NoSQL. Фактически, я недавно обратился к этой главе в RavenDB. Существует хорошее описание проблемы и решение RavenDB здесь.
Вопросы MongoDB обсуждают обходное решение, подобное тому, что я описал в комментариях выше. Вы заранее рассчитываете местное время, в котором вы заинтересованы, и группируйте их вместо этого.
Трудно будет охватывать каждый часовой пояс в мире любым из них. Вы должны принять решение о небольшом количестве целевых зон, которые имеют смысл для вашей пользовательской базы, например, подход, созданный для офиса, который я описал в статье RavenDB.
Ответ 2
Помимо SERVER-6310, упомянутого Мэттом Джонсоном, еще одним обходным решением является использование оператора $project
для добавления или вычитания из часового пояса UTC для "сдвига времени" в правильную локальную зону. Оказывается, вы можете добавлять или вычитать время в миллисекундах.
Например, если у меня есть поле Date, называемое orderTime
. Я бы хотел запросить EDT. Это -4 часа с UTC. Это 4 * 60 * 60 * 1000 миллисекунд.
Итак, я бы написал следующий прогноз, чтобы получить day_ordered
по местному времени для всех моих записей:
db.table.aggregate(
{ $project : { orderTimeLocal : { $subtract : [ "$orderTime", 14400000] } } },
{ $project : { day_ordered : { $dayOfYear : "$orderTimeLocal" } } })
Ответ 3
Каждый предложенный выше подход работает отлично, но поскольку существует новая версия 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"
, где используются указанные выше vars.
Ответ 4
Я нашел решение в плагин mongoose, чтобы нормализовать часовой пояс сохраненных дат.