将时间从数据库转换为自定义时间失败

Alb*_*ert 3 go go-gorm

我需要从数据库中读取日期,将其转换为某个时间戳并将其转换为 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
}
Run Code Online (Sandbox Code Playgroud)

当我像这样运行它时,我收到以下错误:

sql: Scan error on column index 8, name "publishing_date": unsupported Scan, storing driver.Value type time.Time into type *usages.trvTime 
Run Code Online (Sandbox Code Playgroud)

而且数据是错误的:

{"published_date":"00010101","expiration_date":"00010101"}
Run Code Online (Sandbox Code Playgroud)

如果我将SpecialOffer结构更改为 use time.Time,它返回正确,但显然格式错误:

{"published_date":"2020-03-12T00:00:00Z","expiration_date":"2020-06-12T00:00:00Z"}
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

mko*_*iva 5

您应该实现sql.Scannerdriver.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
}
Run Code Online (Sandbox Code Playgroud)

这是必要的,因为和其他一些 go ORMdatabase/sql使用的包gorm(如果不是全部)只为少数类型提供开箱即用的支持。

大多数支持的类型是语言的基本内置类型,如stringintbool等。通过扩展,它还支持任何自定义用户定义类型,其底层类型是上述基本类型之一,然后支持该[]byte类型和相关sql.RawBytes类型,以及最后该time.Time类型也支持ootb。

您可能想要写入或读取数据库的任何其他类型都需要实现上述两个接口。的sql.ScannerScan方法被调用自动列的值被解码为所支持的类型之一(这就是为什么你需要对输入断言的time.Time,而不是反对,说[]byte)。该driver.ValuerValue方法自动调用之前驱动程序编码成一个格式,这是有效的目标列(这就是为什么你可以返回time.Time,而不是直接将其做自己的编码)。

请记住

type jsonTime struct {
    time.Time
}
Run Code Online (Sandbox Code Playgroud)

甚至

type jsonTime time.Time
Run Code Online (Sandbox Code Playgroud)

声明了一个新的类型不等于time.Time,这就是为什么它没有被拾起database/sql包。