问题描述:
gorm中使用下面的CreatedAt 和UpdateAt,可以实现在记录创建和更新时自动更新下面两个字段。虽然使用默认的json解析,从json中到golang中,从golang中写入到数据库(mysql)都是标准的格式“2006-01-02 15:04:05”,但是从golang中解析到json中时是“RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"”。
原因:
time.Time的反序列化使用的是RFC3339Nano,不是希望的标准时间,返回到前台,前台表示拒绝……。只能后台改一下了。
type User struct {
Id int64 `json:"id"`
CreatedAt time.Time `json:"created_at" `
UpdatedAt time.Time `json:"updated_at" `
}
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)
// MarshalJSON implements the json.Marshaler interface.
// The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
func (t Time) MarshalJSON() ([]byte, error) {
if y := t.Year(); y < 0 || y >= 10000 {
// RFC 3339 is clear that years are 4 digits exactly.
// See golang.org/issue/4556#c15 for more discussion.
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}
b := make([]byte, 0, len(RFC3339Nano)+2)
b = append(b, '"')
b = t.AppendFormat(b, RFC3339Nano)
b = append(b, '"')
return b, nil
}
解决方法:
1.自定义解析方式。在从golang中解析到json时自定义方法重载原生time的解析方法。
详细见下文。
2.使用string作为时间的接收类型。
使用string,不过需要把数据库里的字段类型也改,不然返回还是“2006-01-02T15:04:05.999999999Z07:00”。
3.使用时间戳,只发送给前端时间戳。
使用时间戳,只保存时间戳,返回给前端时间戳,让前端自己格式化。
第一种方法代码:
定义自己的时间类型(建议放在utils通用函数包里):
//完整方法
type ProgramModel struct {
BaseModel
Name string `json:"name" gorm:"column:name"`
StartTime BaseModel `json:"start_time" gorm:"column:start_time"`
EndTime BaseModel `json:"end_time" gorm:"column:end_time"`
}
type BaseModel struct {
gorm.Model
Id uint64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"id"`
CreatedAt MyTime `gorm:"column:created_at" json:"created_at"`
UpdatedAt MyTime `gorm:"column:updated_at" json:"updated_at"`
}
type MyTime struct {
time.Time
}
//重写 MarshaJSON 方法,在此方法中实现自定义格式的转换;程序中解析到JSON
func (t MyTime) MarshalJSON() ([]byte, error) {
formatted := fmt.Sprintf("\"%s\"", t.Format(formatTime))
return []byte(formatted), nil
}
//JSON中解析到程序中
func (t *MyTime) UnmarshalJSON(data []byte) (err error) {
now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local)
*t = MyTime{Time: now}
return
}
//写入数据库时会调用该方法将自定义时间类型转换并写入数据库
func (t MyTime) Value() (driver.Value, error) {
var zeroTime time.Time
if t.Time.UnixNano() == zeroTime.UnixNano() {
return nil, nil
}
return t.Time, nil
}
//读取数据库时会调用该方法将时间数据转换成自定义时间类型
func (t *MyTime) Scan(v interface{}) error {
value, ok := v.(time.Time)
if ok {
*t = MyTime{Time: value}
return nil
}
return fmt.Errorf("can not convert %v to timestamp", v)
}
//钩子函数
func (v BaseModel) BeforeCreate(scope *gorm.Scope) error {
scope.SetColumn("created_at", time.Now())
scope.SetColumn("updated_at", time.Now())
return nil
}
//钩子函数
func (v BaseModel) BeforeUpdate(scope *gorm.Scope) error {
scope.SetColumn("updated_at", time.Now())
return nil
}
gorm的hook钩子函数:https://gorm.io/docs/hooks.html
使用,dutils是上面的包。而且也是时间类型。
type MineTime struct {
Id int64 `json:"id"`
CreatedAt dutils.MyTime `json:"created_at" gorm:"column:created_at"`
UpdatedAt dutils.MyTime `json:"updated_at" gorm:"column:updated_at"`
}
结果:
这样就可以保证时间格式都是标准的格式了,而且会自动创建,更新时间。
参考博客:
https://www.cnblogs.com/mrylong/p/11326792.html