小编典典

在Go中的mongodb驱动程序中进行比较以及可能以其他语言和其他数据库进行比较时的时间精度问题

go

我正在学习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

因此,我想问很多问题。

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

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

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


阅读 308

收藏
2020-07-02

共1个答案

小编典典

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零时间相差整数毫秒的事实。

2020-07-02