Go中time.Time类型别名JSON反序列化后打印输出异常
问题场景
你在为基于time.Time定义的自定义Time类型实现UnmarshalJSON反序列化方法时,已经通过time.Parse()拿到了合法的time.Time值,但执行*st = Time(t)赋值后,打印结果不符合预期。
相关实现代码:
package main import ( "encoding/json" "fmt" "strings" "time" ) type Time time.Time func (st *Time) UnmarshalJSON(b []byte) error { // "2021-05-21T03:10:20.958450" -> "2021-05-21T03:10:20.958450Z" s := strings.Trim(string(b), "\"") t, err := time.Parse(time.RFC3339, fmt.Sprintf("%s%s", s, "Z")) if err != nil { return fmt.Errorf("parse time: %w", err) } *st = Time(t) return nil } type User struct { Name string TS Time } const data = `{"id":3, "name":"Name", "ts":"2021-05-21T03:10:20.958450"}` func main() { user := new(User) json.Unmarshal([]byte(data), &user) fmt.Printf("%v\n", user) }
当前代码运行输出:
&{Name {958450000 63757163420 <nil>}}
期望输出格式:
&{Name 2021-05-21 03:10:20.95845 +0000 UTC}
核心认知误区
你的赋值逻辑本身没有错误,存储的时间值完全合法,问题出在自定义类型的方法继承规则和fmt包的打印逻辑上:
- 你用
type Time time.Time定义的是一个全新的独立类型,不是time.Time的别名,它不会继承time.Time上已经实现的任何方法,包括负责格式化输出的String() string方法。 fmt包打印值时,会自动检测该类型是否实现了String() string方法:如果实现了就调用该方法返回格式化字符串;如果没实现,就直接遍历打印结构体的内部字段。- 你看到的
{958450000 63757163420 <nil>}其实是time.Time结构体的三个内部字段(wall时钟值、ext扩展时间值、loc时区指针),只是因为你的自定义Time类型没有String()方法,才被直接打印出来。
修复方案
- 方案1:给自定义
Time类型补充String()方法,复用原生time.Time的格式化逻辑:
func (st Time) String() string { return time.Time(st).String() }
加上该方法后,再打印就能得到你期望的时间格式。如果需要支持JSON序列化,也可以同步补上MarshalJSON方法。
- 方案2:如果不需要给
Time类型增加额外的自定义字段/逻辑,可以直接用类型别名定义:
type Time = time.Time
这种写法下Time就是time.Time本身,会直接继承所有原生方法,但你给Time实现的UnmarshalJSON会全局覆盖原生time.Time的反序列化逻辑,使用前要确认场景是否允许。
- 临时验证:如果只是想确认存储的时间值是否正确,可以直接把自定义类型强转回
time.Time打印:
fmt.Println(time.Time(user.TS))
执行后就能看到格式化后的正确时间。
内容的提问来源于stack exchange,提问作者mrfr
相关产品推荐
相关产品推荐

