首页 > 解决方案 > 将时间从 DB 转换为自定义时间失败

问题描述

我需要从数据库中读取日期,将其转换为某个时间戳并将其转换为 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结构更改为 use time.Time,它返回正确,但显然格式错误:

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

我究竟做错了什么?

标签: gogo-gorm

解决方案


您应该实现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
}

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

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

您可能想要写入或从数据库读取的任何其他类型都需要实现上述两个接口。在将列的值解码为一种受支持的类型sql.Scanner,会自动调用'Scan方法(这就是为什么您需要键入 assert 反对而不是反对,比如说)。's方法在驱动程序将其编码为对目标列有效的格式之前自动调用(就是为什么您可以直接返回不是自己进行编码)。time.Time[]bytedriver.ValuerValuetime.Time

请记住,

type jsonTime struct {
    time.Time
}

甚至

type jsonTime time.Time

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


推荐阅读