
本文详解如何为自定义时间类型 myTime 正确实现 UnmarshalJSON 方法,并解决指针赋值错误与方法缺失问题,确保其能安全解析 Unix 时间戳 JSON 数据。
本文详解如何为自定义时间类型 `mytime` 正确实现 `unmarshaljson` 方法,并解决指针赋值错误与方法缺失问题,确保其能安全解析 unix 时间戳 json 数据。
在 Go 中,当通过类型别名(如 type myTime time.Time)定义新类型时,该类型不会自动继承原类型的任何方法——包括 time.Time 的 String()、Unix() 等方法,也包括其方法集中的指针接收者方法。同时,json.Unmarshal 要求 UnmarshalJSON 方法必须能修改调用者本身,因此必须使用指针接收者,并显式解引用赋值。
以下是修正后的完整可运行代码:
package main
import (
"encoding/json"
"fmt"
"log"
"strconv"
"time"
)
// myTime 是 time.Time 的别名,需自行实现所需方法
type myTime time.Time
// String 实现 fmt.Stringer 接口,便于格式化输出
func (m myTime) String() string {
return time.Time(m).String()
}
// UnmarshalJSON 解析 JSON 中的 Unix 时间戳(支持数字或字符串格式)
func (t *myTime) UnmarshalJSON(data []byte) error {
// 去除首尾空格和引号(兼容字符串形式如 "123123123" 或 "123123123.123")
s := string(data)
if len(s) > 0 && (s[0] == '"' || s[0] == '\'') {
var f float64
if err := json.Unmarshal(data, &f); err != nil {
return err
}
*t = myTime(time.Unix(int64(f), int64((f-float64(int64(f)))*1e9)))
return nil
}
// 直接解析数字(JSON number)
f, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return err
}
sec := int64(f)
nsec := int64((f-float64(sec))*1e9)
*t = myTime(time.Unix(sec, nsec))
return nil
}
func main() {
var t myTime
// 示例 1:JSON 数字时间戳
if err := json.Unmarshal([]byte("123123123"), &t); err != nil {
log.Fatal("parse int timestamp:", err)
}
fmt.Printf("Unix timestamp (int): %s\n", t.String())
// 示例 2:带毫秒的浮点数时间戳
if err := json.Unmarshal([]byte("123123123.456"), &t); err != nil {
log.Fatal("parse float timestamp:", err)
}
fmt.Printf("Unix timestamp (float): %s\n", t.String())
// 示例 3:字符串形式(常见于某些 API)
if err := json.Unmarshal([]byte("\"123123123.789\""), &t); err != nil {
log.Fatal("parse quoted timestamp:", err)
}
fmt.Printf("Quoted timestamp: %s\n", t.String())
}✅ 关键修正说明:
-
*t = myTime(time.Unix(...)):因t是*myTime类型指针,必须解引用*t才能赋值;直接写t = ...是将指针重新指向新地址(无效且无法影响原变量)。 -
String()方法需显式定义:通过time.Time(m).String()将myTime转回time.Time调用原生方法。 -
UnmarshalJSON支持多种 JSON 时间格式(纯数字、浮点数、带引号字符串),增强健壮性;原始代码中误用binary.Varint(适用于二进制编码,不适用于 JSON 文本解析)。
⚠️ 注意事项:
- 自定义类型别名后,所有依赖
time.Time方法的功能(如Format、Add、Before等)均需手动委托实现;若需完整功能,建议直接嵌入time.Time(组合)而非类型别名。 - 生产环境推荐使用标准库
time.Time配合json:"-"+ 自定义字段解析,或采用 RFC 3339 格式字符串(更通用、无需额外实现)。
掌握类型别名与方法集的关系,是写出可维护、符合 Go 惯例的序列化逻辑的关键基础。


















