json序列化时间字段应于controller中手动格式化为“2006-01-02 15:04:05”字符串,避免使用json:",string"标签或全局替换marshal,防止污染模型、orm兼容问题及第三方库失效。

JSON序列化时时间字段总是输出为RFC3339格式
Iris默认用Go原生json.Marshal处理结构体转JSON,而time.Time类型会固定输出为类似"2026-09-20T20:25:00+08:00"的RFC3339格式,无法直接改成"2026-09-20 20:25:00"这种常见业务格式。
在MVC Controller中返回结构体前手动格式化
最直接可控的方式:不在结构体字段上做手脚,而是在Controller方法里把time.Time转成字符串再塞进响应结构体。这样既不污染模型定义,又避免全局影响其他模块。
- 不要在model struct里给
CreatedAt字段加json:"created_at,string"标签——这会让json.Marshal调用Time.String(),输出的是"2006-01-02 15:04:05.999999999 -0700 MST",不是你想要的格式 - 推荐写法:
type UserResp struct { ID int64 `json:"id"` Name string `json:"name"` CreatedAt string `json:"created_at"` } func (c *UserController) GetByID(id int64) { user, err := c.Service.FindByID(id) if err != nil { ctx.StatusCode(500) ctx.JSON(zerr.BizError{Code: 50002, Msg: "查询失败"}) ctx.StopWithStatus(500) return } resp := UserResp{ ID: user.ID, Name: user.Name, CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"), } ctx.JSON(resp) } - 如果多个接口都要用这个格式,可抽成工具函数:
FormatTime(t time.Time) string,统一管理格式字符串
用自定义Time类型实现全局控制(慎用)
想一劳永逸?可以定义一个包装time.Time的类型并实现MarshalJSON方法,但要注意副作用:
- 必须确保所有需要格式化的字段都显式声明为该自定义类型,比如
CreatedAt MyTime,不能混用原生time.Time - 数据库ORM(如GORM)可能不识别你的自定义类型,需额外注册
Scan/Value方法,否则查出来是零值 - 第三方库传入的
time.Time无法自动转成你的类型,容易漏掉某些字段 - 示例:
type MyTime time.Time func (t MyTime) MarshalJSON() ([]byte, error) { s := time.Time(t).Format("2006-01-02 15:04:05") return []byte(`"` + s + `"`), nil }
别碰iris.Context.JSON的底层序列化逻辑
有人试图替换iris.JSON使用的encoder,比如传入自定义json.Marshal函数——Iris没有提供这个钩子。它的ctx.JSON()直接调用标准库,不可插拔。强行改全局json.Marshal会影响整个进程,包括日志、中间件、错误响应等,得不偿失。
真正容易被忽略的是:格式化时机必须在Controller方法内完成。MVC不会在ctx.JSON()之前自动扫描结构体里的time.Time字段并转换——它只管按字段标签和类型规则走原生序列化流程。











