web-dev-qa-db-ja.com

mongodを使用してUTCに日付を保存する際のタイムゾーンの問題に対処するにはどうすればよいですか?

各ドキュメントにいくつかの属性とutcタイムスタンプがあるmongodbコレクションがあります。コレクションのデータを使用してユーザーインターフェイスにいくつかのグラフを表示するため、コレクションからデータを引き出して集計フレームワークを使用する必要があります。ただし、ユーザーのタイムゾーンに従って集計を行う必要があります。ユーザーのタイムゾーン(ブラウザからのリクエストまたは他の方法で渡された)を知っていると仮定すると、[クライアントの]タイムゾーンに基づいて集約フレームワークを使用する方法はありますか?

31
Hrishi

あなたが求めていることは、現在 MongoDB issue SERVER-631 で議論されています。

ディスカッションスレッド からのリンクでこれを見つけました。

この問題は、SQLデータベースやNoSQLデータベースなど、日付によるグループ化で一般的です。実際、私は最近、RavenDBでこの問題に取り組みました。問題の適切な説明とRavenDBソリューションがあります here

MongoDBの問題では、上記のコメントで説明したものと同様の回避策が説明されています。関心のある現地時間を事前計算し、代わりにそれらでグループ化します。

どちらのアプローチでも、世界のすべてのタイムゾーンをカバーすることは困難です。 RavenDBの記事で説明したオフィスごとのアプローチなど、ユーザーベースにとって意味のある少数のターゲットゾーンを決定する必要があります。

更新:この問題は、2017年7月(バージョン3.5.11)にMongoDBで解決されました。解決策は、上記の最初のリンクで説明されていますが、集計式の日付に新しいオブジェクト形式を導入しました:{ date: <dateExpression>, timezone: <tzExpression> }集約時に使用するタイムゾーンを指定できます。 Mongoドキュメントの別の例については、 here を参照してください。

11

Matt Johnsonが言及したSERVER-6310とは別に、もう1つの回避策は$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" } } })
37
Astral

上記で提案したすべてのアプローチは完全に機能しますが、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"の戻り値であることに注意してください。

16
Sebastian

mongoose plugin に保存された日付のタイムゾーンを正規化するソリューションが見つかりました。

1
Emir Mamashov