web-dev-qa-db-fra.com

Comment gérer le problème de fuseau horaire lors du stockage de dates dans utc à l'aide de mongod?

J'ai une collection mongodb où chaque document a des attributs et un horodatage utc. J'ai besoin d'extraire des données de la collection et d'utiliser le cadre d'agrégation car j'utilise les données de la collection pour afficher des graphiques sur l'interface utilisateur. Cependant, je dois faire l'agrégation selon le fuseau horaire de l'utilisateur. En supposant que je connaisse le fuseau horaire de l'utilisateur (transmis dans la demande du navigateur ou d'une autre manière), existe-t-il un moyen d'utiliser le cadre d'agrégation pour agréger en fonction du fuseau horaire [du client]?

31
Hrishi

Ce que vous demandez est en cours de discussion dans MongoDB issue SERVER-631 .

J'ai trouvé cela dans un lien de n fil de discussion .

Le problème est courant pour tout regroupement par date, y compris les bases de données SQL et les bases de données NoSQL. En fait, je me suis récemment adressé à cette tête dans RavenDB. Il y a une bonne description du problème et une solution RavenDB ici .

Les problèmes MongoDB discutent d'une solution de contournement, qui est similaire à ce que j'ai décrit dans les commentaires ci-dessus. Vous précalculez les heures locales qui vous intéressent et les regroupez à la place.

Il sera difficile de couvrir tous les fuseaux horaires du monde avec l'une ou l'autre approche. Vous devez décider d'une petite poignée de zones cibles qui ont du sens pour votre base d'utilisateurs, comme l'approche par bureau que j'ai décrite dans l'article de RavenDB.

MISE À JOUR: Ce problème a été résolu dans MongoDB en juillet 2017 (version 3.5.11). La solution est décrite dans le premier lien ci-dessus, mais en bref, ils ont introduit un nouveau format d'objet pour les dates dans les expressions d'agrégation: { date: <dateExpression>, timezone: <tzExpression> } qui vous permet de spécifier un fuseau horaire à utiliser lors de l'agrégation. Voir ici pour un autre exemple dans les documents Mongo.

11
Matt Johnson-Pint

Mis à part le SERVER-6310 mentionné par Matt Johnson, une autre solution consiste à utiliser le $project opérateur pour ajouter ou soustraire du fuseau horaire UTC pour "décaler l'heure" dans le fuseau local correct. Il s'avère que vous pouvez ajouter ou soustraire du temps en millisecondes.

Par exemple, en supposant que j'ai un champ Date appelé orderTime. Je voudrais demander EDT. C'est à -4 heures d'UTC. C'est 4 * 60 * 60 * 1000 millisecondes.

J'écrirais donc la projection suivante pour obtenir day_ordered en heure locale pour tous mes enregistrements:

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

Chaque approche suggérée ci-dessus fonctionne parfaitement, mais comme il existe une nouvelle version de mongodb, à partir de 2.6, vous pouvez utiliser $let dans le cadre d'agrégation, cela vous permettra de créer des variables à la volée, évitant ainsi la nécessité de $project avant de regrouper. Vous pouvez maintenant créer une variable avec $let qui contiendra l'heure localisée et l'utilisera dans le $group opérateur.

Quelque chose comme:

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}
     }
 }])

Notez que vous utilisez $let à l'intérieur de la définition d'un bloc/variable, et la valeur de ce bloc/variable est la valeur renvoyée de la sous-expression "in", où les variables définies ci-dessus sont utilisées.

16
Sebastian

J'ai trouvé une solution dans le plugin mangouste pour normaliser le fuseau horaire des dates stockées.

1
Emir Mamashov