在 Golang 中,时间的默认序列化格式是 RFC3339。由于使用标准库中的 time.Time 类型,开发者在处理 JSON 时可能会希望使用不同的格式。本文将介绍如何在 Golang 中自定义 time.Time 类型字段的 JSON 序列化格式,并在 Gorm 中自定义 JSON 时间字段格式。

Golang 中时间格式的定义

Golang 中的时间格式通过常量进行定义,最常用的格式包括:

const (
    RFC3339 = "2006-01-02T15:04:05Z07:00"
    // 其他格式省略
)

默认的 JSON 序列化格式

当 Golang 将包含 time.Time 类型字段的结构体序列化为 JSON 时,默认使用 RFC3339 格式。例如,序列化后的时间格式通常为 2006-01-02T15:04:05Z07:00。这种格式在处理大多数时间相关的应用中是足够的,但在某些场景下,开发者可能需要更具可读性的格式,如 2006-01-02 15:04:05

Golang 如何实现自定义时间序列化格式

为了自定义 time.Time 类型字段的 JSON 序列化格式,需要重写 json.Marshaler 接口。由于 Go 不允许在包外定义方法,我们需要通过别名或内嵌结构体的方式实现这一点。

别名方式(不推荐)

type JSONTime time.Time

内嵌方式(推荐)

type JSONTime struct {
    time.Time
}

// MarshalJSON 覆盖 time.Time 的 MarshalJSON
func (t JSONTime) MarshalJSON() ([]byte, error) {
    formatted := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
    return []byte(formatted), nil
}

使用内嵌方式的优点是可以直接访问 time.Time 的方法和字段。

示例代码

以下是一个完整的示例,展示了如何使用自定义的 JSONTime 类型:

package main

import (
	"encoding/json"
	"fmt"
	"time"
)

type MyStruct struct {
	CreatedAt time.Time
}

type JSONTime struct {
	time.Time
}

// MarshalJSON 覆盖 time.Time 的 MarshalJSON
func (t JSONTime) MarshalJSON() ([]byte, error) {
	formatted := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
	return []byte(formatted), nil
}

type MyStruct2 struct {
	CreatedAt JSONTime
}

func main() {
	ms := MyStruct{
		CreatedAt: time.Now(),
	}
	m, _ := json.Marshal(ms)
	fmt.Println("time.Time: ", string(m))

	ms2 := MyStruct2{
		CreatedAt: JSONTime{time.Now()},
	}
	m2, _ := json.Marshal(ms2)
	fmt.Println("JSONTime: ", string(m2))
}

输出结果

time.Time:  {"CreatedAt":"2009-11-10T23:00:00Z"}
JSONTime:  {"CreatedAt":"2009-11-10 23:00:00"}

自定义 Gorm Model 中 time.Time 类型字段的 JSON 序列化格式

在 Gorm 中,要自定义时间字段的 JSON 序列化格式,除了重写 MarshalJSON 方法外,还需实现 database/sql 中的 ValueScan 方法,以确保与数据库的交互正常。

完整实现示例

package main

import (
    "database/sql/driver"
    "fmt"
    "time"
)

type JSONTime struct {
    time.Time
}

// MarshalJSON 自定义时间格式
func (t JSONTime) MarshalJSON() ([]byte, error) {
    formatted := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
    return []byte(formatted), nil
}

// Value 实现 database/sql 的驱动接口
func (t JSONTime) Value() (driver.Value, error) {
    if t.Time.IsZero() {
        return nil, nil
    }
    return t.Time, nil
}

// Scan 从数据库读取时间值
func (t *JSONTime) Scan(v interface{}) error {
    value, ok := v.(time.Time)
    if ok {
        *t = JSONTime{Time: value}
        return nil
    }
    return fmt.Errorf("cannot convert %v to timestamp", v)
}

// BaseModel 用于 Gorm 模型的时间字段
type BaseModel struct {
    ID        int      `gorm:"primary_key" json:"id"`
    CreatedAt JSONTime `json:"createdAt"`
    UpdatedAt JSONTime `json:"updatedAt"`
    DeletedAt JSONTime `gorm:"index" json:"-"`
}

在定义 Gorm 模型时,只需将时间字段使用自定义的 JSONTime 类型即可。

相关阅读推荐:Golang JSON 操作完全指南:从基础到进阶,看这一篇就够了!

总结

自定义 Golang 中 time.Time 类型字段的 JSON 序列化格式可以通过实现 MarshalJSON 方法以及与 Gorm 的数据库交互功能来完成。通过灵活使用内嵌结构体,开发者能够轻松实现所需的时间格式,提升 API 的可读性和用户体验。


也可以看看