You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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包的打印逻辑上:

  1. 你用type Time time.Time定义的是一个全新的独立类型,不是time.Time的别名,它不会继承time.Time上已经实现的任何方法,包括负责格式化输出的String() string方法。
  2. fmt包打印值时,会自动检测该类型是否实现了String() string方法:如果实现了就调用该方法返回格式化字符串;如果没实现,就直接遍历打印结构体的内部字段。
  3. 你看到的{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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 10:57:20