はじめに
GoでMongo周りのコーディングをしてた際に、「TimestampをMongoDBに詰めると値が変わってしまう」って話が出てたので、仕様を確認しました。
Mongoの時刻に関する挙動: time.Timeと情報が異なる
例えば以下のようなMongoDBにtime.Timeで時刻を取って、Mongoにinsert, findしてみます。
package main
import (
"fmt"
"time"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"github.com/google/uuid"
)
type MyDoc struct {
ID uuid.UUID
Time time.Time
}
func main() {
dbname := "sampleDocument"
connection, _ := mgo.Dial("mongodb://localhost/" + dbname)
db := connection.DB(dbname)
//元ネタとなる時刻取得
doc := MyDoc{uuid.New(), time.Now()}
fmt.Printf("data in time.Time:[%v]\n", doc)
//insert
collection := db.C("doc")
collection.Insert(doc)
//中身の確認
var getFromMongo MyDoc
collection.Find(bson.M{"id": doc.ID}).One(&getFromMongo)
fmt.Printf("data in Mongo:[%v]\n", getFromMongo)
}
上記の実行結果はこんな感じ。
項目 | time.Time.Now() | Mongo |
---|---|---|
精度 | nanosecond | millisecond |
タイムゾーン | JST | UTC |
$ go run main.go
data in time.Time:[{b7350e5d-05d0-429a-9d51-1eb365e71d76 2019-06-25 23:11:36.706508427 +0900 JST m=+0.001236469}]
data in Mongo:[{b7350e5d-05d0-429a-9d51-1eb365e71d76 2019-06-25 14:11:36.706 +0000 UTC}]
実際のMongoDBに入っている情報を確認しても情報が消えています。つまりmongo自体の仕様か、可能性は低そうですがGolangのinsert部分に関するmongo用ドライバの問題である可能性があります。
ISODateという形式で入っていることから、mongoの仕様として変換してるんだろうなということが予想されます。
> db.doc.find()
{ "_id" : ObjectId("5d122aa6bf374055deac672e"), "id" : BinData(0,"ugCCZy0ERyWRZRk+V2fxIw=="), "time" : ISODate("2019-06-25T14:07:34.331Z") }
Mongoの時刻仕様
Mongoでは、BSONと呼ばれるデータフォーマットでデータを保存しています。その仕様を確認すると以下のようになっています。
下記で分かるように1. タイムゾーンはUTC, 2. データの精度はmillisecondとなります。
UTC datetime - The int64 is UTC milliseconds since the Unix epoch.
MongoDBのDate仕様も確認。Date()の記載より
new Date("YYYY-mm-ddTHH:MM:ss") specifies the datetime in the client’s local timezone and returns the ISODate with the specified datetime in UTC.
実行時にタイムゾーン指定がない場合は、マシンのタイムゾーンで計算⇒UTC時刻を返却としていますね。
同様にBehaviorより、millisecondなのが分かります。
Internally, Date objects are stored as a signed 64-bit integer representing the number of milliseconds since the Unix epoch (Jan 1, 1970).
というわけで、以下が仕様であることが分かりました。
項目 | time.Time.Now() | Mongo |
---|---|---|
精度 | nanosecond | millisecond |
タイムゾーン | JST | UTC |
参考
BSON Specification Version 1.1
MongoDB Date()
Stack overflow: Nanoseconds lost coming from MongoDB ISODate Object
mongoのISODateのtimezone問題に対処する