web-dev-qa-db-ja.com

異なるタイムゾーンで年月日ごとに集計する方法

日付オブジェクトをUTCで保存するMongoDBがあります。さて、別のタイムゾーン(CET)で年、月日ごとに集計を実行したいと思います。

これを行うと、UTCで正常に機能します。

    BasicDBObject group_id = new BasicDBObject("_id", new BasicDBObject("year", new BasicDBObject("$year", "$tDate")).
                append("month", new BasicDBObject("$month", "$tDate")).
                append("day", new BasicDBObject("$dayOfMonth", "$tDate")).
                append("customer", "$customer"));

    BasicDBObject groupFields = group_id.
            append("eventCnt", new BasicDBObject("$sum", "$eventCnt")); 

    BasicDBObject group = new BasicDBObject("$group", groupFields);

または、コマンドラインを使用する場合(テストされていない場合は、Javaバージョン)のみをテストしました):

{
    $group: {
        _id: {
            "year": {
                "$year", "$tDate"
            },
            "month": {
                "$month", "$tDate"
            },
            "day": {
                "$dayOfMonth", "$tDate"
            },
            "customer": "$customer"
        },
        "eventCount": {
            "$sum": "$eventCount"
        }
    }
}

集計フレームワーク内でこれらの日付をCETに変換するにはどうすればよいですか?

たとえば、「2013-09-16 23:45:00UTC」は「2013-09-1700:45:00 CET」ですが、これは別の日です。

23
SQL.injection

私はCETとUTCとの関係の専門家ではありませんが、次のコード(シェル用)は、MongoDB日付型に適切に変換(1時間を追加)する必要があります。

db.dates.aggregate(
  {$project: {"tDate":{$add: ["$tDate", 60*60*1000]}, "eventCount":1, "customer":1}}
)

パイプラインの残りの部分の前にそのプロジェクトコマンドを実行すると、結果はCETになります。

15
3rf

時間を検索した後、これは私のために働いた解決策です。それもとても簡単です。ミリ秒単位のタイムゾーンオフセットを減算して、タイムゾーンを変換するだけです。

25200000 = 7時間オフセット// 420分* 60秒* 1000ミリ

$group: {
    _id = { 
        year: { $year : [{ $subtract: [ "$timestamp", 25200000 ]}] }, 
        month: { $month : [{ $subtract: [ "$timestamp", 25200000 ]}] }, 
        day: { $dayOfMonth : [{ $subtract: [ "$timestamp", 25200000 ]}] }
    },
    count = { 
        $sum : 1
    }
};
7
Trevor Meier

3.6以降の日付演算子に timezone を指定できます。

タイムゾーンを自分のタイムゾーンに置き換えます。

{
  "$group":{
    "_id":{
      "year":{"$year":{"date":"$tDate","timezone":"America/Chicago"}},
      "month":{"$month":{"date":"$tDate","timezone":"America/Chicago"}},
      "dayOfMonth":{"$dayOfMonth":{"date":"$tDate","timezone":"America/Chicago"}}
    },
    "count":{"$sum":1}
  }
}
7
Sagar Veeram

たとえばmoment.jsを使用して、CETの現在のタイムゾーンオフセットを決定しますが、この方法で夏と冬のオフセットを取得します

var offsetCETmillisec = moment.tz.zone('Europe/Berlin').offset(moment())* 60 * 1000;

  $group: {
    _id: {
      'year': {'$year': [{ $subtract: [ '$createdAt', offsetCETmillisec ]}] },
      'month': {'$month': [{ $subtract: [ '$createdAt', offsetCETmillisec ]}] },
      'day': {'$dayOfMonth': [{ $subtract: [ '$createdAt', offsetCETmillisec ]}] }
    },
    count: {$sum: 1}
  }
}
4
helgetan

MongoDBのドキュメントが示唆している タイムスタンプと一緒にタイムゾーンオフセットを保存すること:

var now = new Date();
db.data.save( { date: now,
                offset: now.getTimezoneOffset() } );

もちろん、これは理想的なソリューションではありませんが、MongoDbの集約パイプラインに適切な$ utcOffset関数が含まれるまでは、機能します。

2
Waldo

タイムゾーンを使用したソリューションは優れていますが、バージョン3.6では、タイムゾーンを使用して出力をフォーマットすることもできるため、結果をすぐに使用できます。

{
"$project":{
    "year_month_day": {"$dateToString": { "format": "%Y-%m-%d", "date": "$tDate", "timezone": "America/Chicago"}}
},
"$group":{
    "_id": "$year_month_day",
    "count":{"$sum":1}
}
}

「$ match」もタイムゾーンを考慮していることを確認してください。そうしないと、間違った結果が得られます。

Mongoは日付をUTCで保存するため、これは他のゾーンで日付を取得する手順です。

  • mongoが日付をUTCで保存していることを確認し、いくつかのレコードを挿入します。
  • 指定したタイムゾーンについて、moment-timezone.jsを使用してタイムゾーンオフセットを取得します。例:moment()。tz( 'Europe/Zagreb')。utcOffset()関数
  • $ matchステージ用に$ gteと$ lteを準備します(たとえば、日付1.1.2019〜13.1.2019のユーザー入力):
    • オフセットが正の場合、$ matchステージのそれらの秒をsubtract()します。オフセットが負の場合add()$ matchステージのそれらの秒
  • 次に、日付を次のようにゾーンに正規化します($ matchステージはUTCでそれらを返すため)。-タイムゾーンオフセットが正の場合、$ projectステージの秒をadd()します。 -タイムゾーンオフセットが負の場合、$ projectステージのそれらの秒を減算()します。
  • $ groupは最後になりますが、これは重要です($ match-edではなく、正規化された結果をグループ化する必要があるため)

基本的にはこれです:入力を$ match(UTC)にシフトしてから、タイムゾーンに正規化します。

0
TomoMiha