我正在学习Go和Mongodb,目前正在使用alpha官方mongodb驱动程序。尽管它是alpha版本,但我认为它对于基本用法还是很有用的。但是在这个数据库驱动程序中,我在时间转换上遇到了一个有趣的问题。
基本上,我创建了一个自定义类型的struct对象,并将其编组为bson文档,然后将bson文档转换回struct对象。
//check github.com/mongodb/mongo-go-driver/blob/master/bson/marshal_test.go func TestUserStructToBsonAndBackwards(t *testing.T) { u := user{ Username: "test_bson_username", Password: "1234", UserAccessibility: "normal", RegisterationTime: time.Now(), //.Format(time.RFC3339), adding format result a string } //Struct To Bson bsonByteArray, err := bson.Marshal(u) if err != nil { t.Error(err) } //.UnmarshalDocument is the same as ReadDocument bDoc, err := bson.UnmarshalDocument(bsonByteArray) if err != nil { t.Error(err) } unameFromBson, err := bDoc.LookupErr("username") //so here the binding is working for bson object too, the bind field named username ratherthan Username if err != nil { t.Error(err) } if unameFromBson.StringValue() != "test_bson_username" { t.Error("bson from user struct Error") } //Bson Doc to User struct bsonByteArrayFromDoc, err := bDoc.MarshalBSON() if err != nil { t.Error(err) } var newU user err = bson.Unmarshal(bsonByteArrayFromDoc, &newU) if err != nil { t.Error(err) } if newU.Username != u.Username { t.Error("bson Doc to user struct Error") } //here we have an issue about time format. if newU != u { log.Println(newU) log.Println(u) t.Error("bson Doc to user struct time Error") } }
但是,由于我的struct对象具有一个时间字段,因此结果struct对象包含的时间值比原始对象的时间值精度低。然后比较失败。
=== RUN TestUserStructToBsonAndBackwards {test_bson_username 1234 0001-01-01 00:00:00 +0000 UTC 2018-08-28 23:56:50.006 +0800 CST 0001-01-01 00:00:00 +0000 UTC normal } {test_bson_username 1234 0001-01-01 00:00:00 +0000 UTC 2018-08-28 23:56:50.006395949 +0800 CST m=+0.111119920 0001-01-01 00:00:00 +0000 UTC normal } --- FAIL: TestUserStructToBsonAndBackwards (0.00s) model.user_test.go:67: bson Doc to user struct time Error
因此,我想问很多问题。
在这种情况下如何正确比较时间?
在数据库中存储时间以避免这种精度问题的最佳方法是什么?我认为数据库中的时间不应为字符串。
这是数据库驱动程序错误吗?
BSON中的时间以自Unix纪元(spec)以来的UTC毫秒表示。Go中的时间值具有纳秒级精度。
为了往返时间,通过BSON编组的时间值,请使用自Unix时代以来截断为毫秒的时间:
func truncate(t time.Time) time.Time { return time.Unix(0, t.UnixNano()/1e6*1e6) } ... u := user{ Username: "test_bson_username", Password: "1234", UserAccessibility: "normal", RegisterationTime: truncate(time.Now()), }
您还可以使用Time.Truncate方法:
u := user{ Username: "test_bson_username", Password: "1234", UserAccessibility: "normal", RegisterationTime: time.Now().Truncate(time.Milliseconds), }
这种方法依赖于Unix纪元和Go零时间相差整数毫秒的事实。