database - 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
所以我想就此提出很多问题。
在这种情况下如何正确比较时间?
将时间存储在数据库中以避免此类精度问题的最佳方法是什么?我认为数据库中的时间不应该是一个字符串。
这是一个数据库驱动程序错误吗?
解决方案
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 相差整数毫秒这一事实。
推荐阅读
- c# - 会话变量导致控制器操作一次触发一个
- r - 如何使用美学为 geom_hline() 着色?
- android - 如何计算哪个视图占据了屏幕的一半以上
- sql - Postgres在一定时间内按前缀选择然后对其进行排序..性能缓慢
- material-ui - 生成的jss类名自定义?
- sql - inner join combined with a count
- c# - NLog:如果填充了字段,则记录到单个文件
- c++ - C++ std::swap 无效参数
- asp.net - 在 ASP.Net WebAPI 中选择性地接受客户端证书
- reporting-services - ssrs 过滤 tablix 表达式,将使用参数值过滤 tablix