将时间从数据库转换为自定义时间失败
Converting time from DB to custom time fails
我需要从数据库中读取日期,将其转换为某个时间戳并将其转换为 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
,它 return 正确,但显然格式错误:
{"published_date":"2020-03-12T00:00:00Z","expiration_date":"2020-06-12T00:00:00Z"}
我做错了什么?
您应该实现 sql.Scanner
and 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
}
这是必要的,因为 gorm
和其他一些 go ORM 使用的 database/sql
包(如果不是全部的话)只为少数几个提供开箱即用的支持类型。
大多数支持的类型是语言的 basic 内置类型,如 string
、int
、bool
等。通过扩展它还支持任何自定义用户定义类型,其 underlying 类型是上述基本类型之一,然后支持 []byte
类型和相关的 sql.RawBytes
类型,并且最后 time.Time
类型也支持 ootb.
您可能想要写入数据库或从数据库读取的任何其他类型都需要实现上述两个接口。 sql.Scanner
的 Scan
方法在 后自动调用 列的值被解码为支持的类型之一(这就是为什么你需要针对 [= 键入断言22=] 而不是反对,比如 []byte
)。 driver.Valuer
的 Value
方法在 在 之前自动调用 驱动程序 将其编码为对目标有效的格式列(这就是为什么您可以直接 return time.Time
而不是自己进行编码的原因)。
请记住
type jsonTime struct {
time.Time
}
甚至
type jsonTime time.Time
声明一个 new 类型 不等于 到 time.Time
这就是为什么它没有被 database/sql
包.
我需要从数据库中读取日期,将其转换为某个时间戳并将其转换为 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
,它 return 正确,但显然格式错误:
{"published_date":"2020-03-12T00:00:00Z","expiration_date":"2020-06-12T00:00:00Z"}
我做错了什么?
您应该实现 sql.Scanner
and 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
}
这是必要的,因为 gorm
和其他一些 go ORM 使用的 database/sql
包(如果不是全部的话)只为少数几个提供开箱即用的支持类型。
大多数支持的类型是语言的 basic 内置类型,如 string
、int
、bool
等。通过扩展它还支持任何自定义用户定义类型,其 underlying 类型是上述基本类型之一,然后支持 []byte
类型和相关的 sql.RawBytes
类型,并且最后 time.Time
类型也支持 ootb.
您可能想要写入数据库或从数据库读取的任何其他类型都需要实现上述两个接口。 sql.Scanner
的 Scan
方法在 后自动调用 列的值被解码为支持的类型之一(这就是为什么你需要针对 [= 键入断言22=] 而不是反对,比如 []byte
)。 driver.Valuer
的 Value
方法在 在 之前自动调用 驱动程序 将其编码为对目标有效的格式列(这就是为什么您可以直接 return time.Time
而不是自己进行编码的原因)。
请记住
type jsonTime struct {
time.Time
}
甚至
type jsonTime time.Time
声明一个 new 类型 不等于 到 time.Time
这就是为什么它没有被 database/sql
包.