Преобразование времени из БД в пользовательское время не удается - PullRequest
3 голосов
/ 06 мая 2020

Мне нужно прочитать даты из БД, преобразовать их в определенную метку времени и преобразовать в JSON.

У меня есть следующий код:

package usages

import (
    "fmt"
    "time"
)

type SpecialOffer struct {
    PublishedDate   jsonTime    `gorm:"column:publishing_date" json:"published_date"`
    ExpirationDate  jsonTime    `gorm:"column:expiration_date" json:"expiration_date"`
}

type jsonTime struct {
    time.Time
}

func (tt jsonTime) MarshalJSON() ([]byte, error) {
    jsonTime := fmt.Sprintf("\"%s\"", tt.Format("20060102"))
    return []byte(jsonTime), nil
}

Когда я запускаю вот так, я получаю следующую ошибку:

sql: Scan error on column index 8, name "publishing_date": unsupported Scan, storing driver.Value type time.Time into type *usages.trvTime 

И данные неверны:

{"published_date":"00010101","expiration_date":"00010101"}

Если я изменю структуру SpecialOffer на использование time.Time, она вернет правильный , но явно неправильный формат:

{"published_date":"2020-03-12T00:00:00Z","expiration_date":"2020-06-12T00:00:00Z"}

Что я делаю не так?

1 Ответ

3 голосов
/ 06 мая 2020

Вы должны реализовать интерфейсы sql.Scanner и driver.Valuer.

Примерно так:

func (j *jsonTime) Scan(src interface{}) error {
    if t, ok := src.(time.Time); ok {
        j.Time = t
    }
    return nil
}

func (j jsonTime) Value() (driver.Value, error) {
    return j.Time, nil
}

Это необходимо, потому что пакет database/sql, который используется gorm и некоторыми другими go ORM, если не всеми из них, обеспечивает готовую поддержку только для нескольких типов.

Большинство поддерживаемых типов - это встроенные типы языка basi c, такие как string, int, bool, et c. по расширению он также поддерживает любой настраиваемый пользовательский тип, чей базовый тип является одним из вышеупомянутых базовых c типов, затем есть поддержка типа []byte и связанного с ним типа sql.RawBytes, и, наконец, Тип time.Time также поддерживается ootb.

Любой другой тип, который вы можете захотеть записать или прочитать из базы данных, должен будет реализовать два вышеуказанных интерфейса. Метод Scan sql.Scanner вызывается автоматически после значение столбца декодируется в один из поддерживаемых типов (поэтому вам нужно вводить assert против time.Time, а не против, скажем []byte). Метод driver.Valuer s Value вызывается автоматически перед драйвер кодирует его в формат, допустимый для целевого столбца (поэтому вы можете вернуть time.Time, а чем кодирование самостоятельно).

И имейте в виду, что

type jsonTime struct {
    time.Time
}

или даже

type jsonTime time.Time

объявляет новый тип что не равно и time.Time, и поэтому он не принимается пакетом database/sql.

...