首页 > 解决方案 > Go中mongodb驱动程序中比较的时间精度问题,可能在其他语言和其他数据库中

问题描述

我正在学习 Go 和 Mongodb,目前使用的是alpha 官方 mongodb 驱动程序。虽然它是 alpha 版,但我认为它对于基本用法来说非常实用。但是我在这个数据库驱动程序中遇到了一个有趣的时间转换问题。

基本上,我创建了一个自定义类型的结构对象,并将其编组为 bson 文档,然后将 bson 文档转换回结构对象。

//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

所以我想就此提出很多问题。

  1. 在这种情况下如何正确比较时间?

  2. 将时间存储在数据库中以避免此类精度问题的最佳方法是什么?我认为数据库中的时间不应该是一个字符串。

  3. 这是一个数据库驱动程序错误吗?

标签: databasemongodbgotime

解决方案


BSON 中的时间表示为自 Unix 纪元 ( spec ) 以来的 UTC 毫秒数。Go 中的时间值具有纳秒精度。

要通过 BSON 编组往返 time.Time 值,请使用自 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.Millisecond),
}

这种方法依赖于 Unix epoch 和 Go zero time 相差整数毫秒这一事实。


推荐阅读