Go中自定义Time结构体MarshalJSON未转换UTC时间的原因
问题:自定义Time结构体的MarshalJSON方法未生效
我尝试定义一个实现Marshaler接口的Time结构体,使其序列化为JSON时以YYYY-mm-ddTHH:MM:SSZ格式呈现(转换为UTC时间并四舍五入到最近秒数),编写了如下代码:
package main import ( "encoding/json" "fmt" "log" "time" ) type Time struct { time.Time } func (t *Time) MarshalJSON() ([]byte, error) { return []byte(t.Time.UTC().Round(time.Second).Format(time.RFC3339)), nil } func main() { tm := time.Now() // tm := time.Now().UTC().Round(time.Second) tmJSON, err := json.Marshal(tm) if err != nil { log.Fatalf("marshal time: %v", err) } fmt.Println(string(tmJSON)) }
运行程序后输出:
> go run main.go "2022-12-07T16:32:51.494597-08:00"
若改用注释行的输入time.Now().UTC().Round(time.Second),输出符合预期:
> go run main.go "2022-12-08T00:41:10Z"
为什么无法在MarshalJSON方法内直接完成UTC转换和秒级四舍五入操作?
原因与修复方案
核心原因:序列化对象类型不匹配
你在main中创建的tm是标准库的time.Time类型,而非自定义的Time结构体。json.Marshal会根据传入对象的类型调用对应实现的MarshalJSON方法——这里执行的是标准库time.Time自带的序列化逻辑,完全没触发你自定义的方法。
次要问题:JSON格式错误
你自定义的MarshalJSON返回的字符串没有添加双引号,直接返回格式字符串会导致生成的JSON不符合规范(标准库序列化time.Time时会自动添加双引号)。
修正后的代码
package main import ( "encoding/json" "fmt" "log" "time" ) type Time struct { time.Time } // 改为值接收者,避免空指针问题(可选,但更健壮) func (t Time) MarshalJSON() ([]byte, error) { // 给格式化后的UTC时间字符串添加双引号,符合JSON语法 formattedStr := fmt.Sprintf(`"%s"`, t.UTC().Round(time.Second).Format(time.RFC3339)) return []byte(formattedStr), nil } func main() { // 使用自定义Time类型包裹标准time.Time实例 tm := Time{Time: time.Now()} tmJSON, err := json.Marshal(tm) if err != nil { log.Fatalf("marshal time: %v", err) } fmt.Println(string(tmJSON)) }
运行修正后的代码,即可得到预期的"YYYY-mm-ddTHH:MM:SSZ"格式输出。
内容的提问来源于stack exchange,提问作者Kurt Peek
相关产品推荐
相关产品推荐

