Проблема точности времени при сравнении в драйвере mongodb в Go и, возможно, на другом языке и в другой базе данных - PullRequest
0 голосов
/ 28 августа 2018

Я изучал Go и Mongodb, в настоящее время использую официальный альфа-драйвер mongodb . Хотя он в альфа-версии, он вполне функционален для базового использования. Но у меня возникла интересная проблема с преобразованием времени в этом драйвере БД.

По сути, я создал пользовательский типизированный объект 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")
}
}

Однако, поскольку у моего объекта структуры есть поле времени, объект структуры результата содержит менее точное значение времени, чем оригинал. Тогда сравнение не удалось.

=== 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. это ошибка драйвера БД?

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Время в BSON представляется как UTC миллисекунды с эпохи Unix ( spec ). Значения времени в 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 отличаются на целое число миллисекунд.

0 голосов
/ 28 августа 2018

Вы правильно определили, что проблема связана с точность .

Тип Date MongoDB - это"64-битное целое число, представляющее число миллисекунд ...".

Тип time.Time Голанга"представляет собой момент времени с наносекундами точностью".

Таким образом, если вы сравните эти соответствующие значения как типы Голанга, вы получите эквивалентность только в том случае, если Время Голанга имеет разрешение в миллисекундах (например, нули для мест с микро- и наносекундами).

Например:

gotime := time.Now() // Nanosecond precision
jstime := gotime.Truncate(time.Millisecond) // Milliseconds
gotime == jstime // => likely false (different precision)

isoMillis := "2006-01-02T15:04:05.000-0700Z"
gomillis := gotime.Format(isoMillis)
jsmillis := jstime.Format(isoMillis)
gomillis == jsmillis // => true (same precision)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...