DennyHiu
DennyHiu

Reputation: 6110

Mongodb aggregate: convert date to another timezone

I save my transaction with something like :

{code: "A", total: 250000, timestamp: ISODate("2016-01-20T23:57:05.771Z")},
{code: "B", total: 300000, timestamp: ISODate("2016-01-20T05:57:05.771Z")}

each of transaction has timestamp field under UTC timezone in it. Since I live in Jakarta (UTC+7) timezone, I need to add 7 hours to my timestamp before aggregation. Here's my mongo syntax:

db.transaction.aggregate(
[
 {
   $project:
     {
       year: { $year: "$timestamp" },
       month: { $month: "$timestamp" },
       day: { $dayOfMonth: "$timestamp" }
     }
 }
])

It returns:

    {
        "_id" : ObjectId("56a01ed143f2fd071793d63b"),
        "year" : 2016,
        "month" : 1,
        "day" : 20
    },
    {
        "_id" : ObjectId("56a01ed143f2fd071793d63b"),
        "year" : 2016,
        "month" : 1,
        "day" : 20
    }

which is wrong since the first transaction (code A), is happened at 21st January, but since it was converted to UTC (-7 Hours), it became ISODate("2016-01-20T23:57:05.771Z")

Note: I'm aware about the same problem over here, here's what I've been tried so far:

db.transaction.aggregate(
[
 {$project: {"timestamp": {$add: [7 * 60 * 60 * 1000]}}},
 {
   $project:
     {
       year: { $year: "$timestamp" },
       month: { $month: "$timestamp" },
       day: { $dayOfMonth: "$timestamp" }
     }
 }
])

but it returns can't convert from BSON type NumberDouble to Date error. Any suggestions?

Upvotes: 19

Views: 38810

Answers (5)

Pradeepvina
Pradeepvina

Reputation: 120

You can use the $dateToString method with $project to get specified timezone result.

Note this requires a format argument, see Format Specifiers for more info.

$project:{
     created_at: {
            $dateToString: {
              date: '$createdAt',
              timezone: '+07:00',
              format: '%d-%m-%Y %H:%M'
          }
     }
 }

Upvotes: 7

kevinadi
kevinadi

Reputation: 13795

As an update, MongoDB 3.6 has a new timezone parameter for date manipulation in the aggregation framework. Most date-related operators accept this optional parameter, see $hour for one example.

For example, if we have a document where the date is exactly the new year in UTC:

> db.test.find()
{"_id": 1, "dt": ISODate("2018-01-01T00:00:00Z")}

We can display the date in New York timezone:

> db.test.aggregate([
        {$project:{
            date:{$dayOfMonth:{date:'$dt',timezone:'America/New_York'}},
            month:{$month:{date:'$dt',timezone:'America/New_York'}},
            year:{$year:{date:'$dt',timezone:'America/New_York'}},
            hour:{$hour:{date:'$dt',timezone:'America/New_York'}}
        }}
    ])
{ "_id": 1, "date": 31, "month": 12, "year": 2017, "hour": 19 }

We can also display the date in Sydney timezone:

> db.test.aggregate([
        {$project:{
            date:{$dayOfMonth:{date:'$dt',timezone:'Australia/Sydney'}},
            month:{$month:{date:'$dt',timezone:'Australia/Sydney'}},
            year:{$year:{date:'$dt',timezone:'Australia/Sydney'}},
            hour:{$hour:{date:'$dt',timezone:'Australia/Sydney'}}
        }}
    ])
{ "_id": 1, "date": 1, "month": 1, "year": 2018, "hour": 11 }

Or could use a single $dateToParts

> db.test.aggregate([
        {$project:{
            dateToParts:{$dateToParts:{date:'$dt',timezone:'Australia/Sydney'}}
        }}
    ])
{ "_id": 1, "dateToParts": { "day": 1, "hour": 11, "millisecond": 0, "minute": 0, "month": 1, "second": 0, "year": 2018 } }

The timezone description is using the standard Olson Timezone Identifier string.

Upvotes: 42

Mateus Barros
Mateus Barros

Reputation: 11

You need to add the name of the timezone like so:

$project: {  
  releaseDate: {
    $dateToString: {
      date: "$releaseDate",
      timezone: "America/Sao_Paulo",
      format: "%d/%m/%Y %H:%M:%S"
    }
  }
}

Upvotes: 1

Hi sharing my solution used on one of my extracts using mongodb version 3.4 or below and need it to covert from utc to manila time +08:00

see the sample query below

db.collection.aggregate([
{$project:{
   "created_at":{"$dateToString": { "date":{ "$add": [ "$yourtimestampfield", 8 * 60 * 60 * 1000 ] }, "format": "%Y-%m-%d %H:%M:%S"}},
}}])

enter image description here

Upvotes: 2

Sede
Sede

Reputation: 61253

MongoDB 3.6 added timezone parameter to the date manipulation operators. See Kevin's answer.

We can add the "timestamp" to 7 * 60 * 60 * 1000 in a $project stage.

The following pipeline seems to work in MongoDB 3.4 or older.

db.collection.aggregate([
    { "$project": {
        "year": { "$year": { "$add": [ "$timestamp", 7 * 60 * 60 * 1000 ] } }, 
        "month": { "$month": { "$add": [ "$timestamp", 7 * 60 * 60 * 1000 ] } }, 
        "day": { "$dayOfMonth": { "$add": [ "$timestamp", 7 * 60 * 60 * 1000 ] } } 
    } }
])

Upvotes: 9

Related Questions