
本文介绍一种无需为每个结构体重复编写 marshaljson 方法的方式,通过自定义类型别名 + 类型转换,实现所有 time.time 字段自动以整数 unix 时间戳格式(秒级)进行 json 序列化。
本文介绍一种无需为每个结构体重复编写 marshaljson 方法的方式,通过自定义类型别名 + 类型转换,实现所有 time.time 字段自动以整数 unix 时间戳格式(秒级)进行 json 序列化。
在 Go 语言中,type Timestamp time.Time 是一个具名类型(named type),而非 time.Time 的别名或子类型。Go 不支持传统面向对象意义上的“继承”,因此 Timestamp 不会自动拥有 time.Time 的任何方法(如 Unix()、Format() 等)。这是初学者常遇到的认知误区:类型别名(type T = time.Time)才等价于原类型,而具名类型(type T time.Time)是全新、独立的类型,需显式转换才能调用底层类型的方法。
正确做法是:在 MarshalJSON 实现中,先将 Timestamp 显式转换为 time.Time,再调用其方法:
package main
import (
"encoding/json"
"fmt"
"strconv"
"time"
)
type Timestamp time.Time
func (t Timestamp) MarshalJSON() ([]byte, error) {
// ✅ 正确:显式转换为 time.Time 后调用 Unix()
return []byte(strconv.FormatInt(time.Time(t).Unix(), 10)), nil
}
// 使用示例
type Event struct {
ID int `json:"id"`
CreatedAt Timestamp `json:"created_at"`
UpdatedAt Timestamp `json:"updated_at"`
}
func main() {
event := Event{
ID: 123,
CreatedAt: Timestamp(time.Date(2024, 1, 15, 10, 30, 45, 0, time.UTC)),
UpdatedAt: Timestamp(time.Date(2024, 1, 15, 11, 22, 18, 0, time.UTC)),
}
data, _ := json.Marshal(event)
fmt.Println(string(data))
// 输出:{"id":123,"created_at":1705314645,"updated_at":1705318538}
}
⚠️ 注意事项:
- 不可直接类型断言:
t.(time.Time)会编译失败,因为Timestamp不是接口类型,无法做接口断言;必须使用time.Time(t)进行类型转换。- 精度选择:上述示例使用
Unix()(秒级)。如需毫秒级,可改用UnixMilli();纳秒级则用UnixNano()(注意 JSON 数值精度限制,通常推荐毫秒级并转为字符串避免溢出)。- 反序列化支持:仅实现
MarshalJSON不足以完成双向编解码。若需json.Unmarshal支持,还应实现UnmarshalJSON方法(例如解析数字或 ISO8601 字符串并转换为time.Time)。- 可读性权衡:虽然
fmt.Sprintf("%d", t.Unix())更直观,但strconv.FormatInt(..., 10)性能更优(无格式解析开销),在高频序列化场景下推荐后者。
总结:通过定义具名类型 Timestamp 并在其 MarshalJSON 方法中执行 time.Time(t).Unix() 转换,即可优雅、统一地将任意含该字段的结构体序列化为 Unix 时间戳。该方案零侵入、易复用,是处理大量时间字段 JSON 格式标准化的推荐实践。











