golang time类型常见问题解决方案

golang修改time类型字段的json格式

背景大概如下:
项目框架为gin, 数据库orm为gorm, 一个比较简单的model, 包含某个time类型字段(如: CreateTime), 返回数据给前端时, 返回的格式为2022-01-17T00:00:00+08:00, 这种数据结构, 返回给前端时, 前端如果直接用来渲染, 这种格式的时间的可读性不高, 故需要转换成用户较好理解的格式, 如: 2022-01-17 00:00:00

解决

原始代码
package main

import (
	"encoding/json"
	"fmt"
	"time"
)

type Person struct {
	Id        int       `json:"id"`
	BirthTime time.Time `json:"birth_time"`
}

func main() {
	birthTime, _ := time.ParseInLocation("2006-01-02 15:04:05", "2022-01-17 00:00:00", time.Local)
	out, _ := json.Marshal(
		Person{
			Id:        55211,
			BirthTime: birthTime,
		},
	)
	fmt.Println(string(out))
	//{"id":55211,"birth_time":"2022-01-17T00:00:00+08:00"}
}

此时输出如下:
{"id":55211,"birth_time":"2022-01-17T00:00:00+08:00"}

解决方案
  1. 数据类型改成string, 不过不推荐这种方式, 如数据比较和校验合法性等存在难度
  2. 重写MarshalJSON方法
修改后代码
package main

import (
	"encoding/json"
	"fmt"
	"time"
)

type Person struct {
	Id        int       `json:"id"`
	BirthTime time.Time `json:"birth_time"`
}

func (d Person) MarshalJSON() ([]byte, error) {
	type Temp Person
	return json.Marshal(&struct {
		Temp
		BirthTime string `json:"birth_time"`
	}{
		Temp:      (Temp)(d),
		BirthTime: d.BirthTime.Format("2006-01-02 15:04:05"),
	})
}

func main() {
	birthTime, _ := time.ParseInLocation("2006-01-02 15:04:05", "2022-01-17 00:00:00", time.Local)
	out, _ := json.Marshal(
		Person{
			Id:        55211,
			BirthTime: birthTime,
		},
	)
	fmt.Println(string(out))
	//{"id":55211,"birth_time":"2022-01-17 00:00:00"}
}

此时输出如下:
{"id":55211,"birth_time":"2022-01-17 00:00:00"}

gin请求时, time类型数据的参数绑定问题

golang对 time.Time 类型默认解析的日期格式是 RFC3339 标准(2006-01-02T15:04:05Z07:00)。在使用 gin 的 shouldBindJSON进行参数绑定时,对YYYY-MM-DD hh:mm:ss格式的日期格式作为 time.Time 类型的值,就会引发类似于 parsing time xx as xx: cannot parse xx as xx 的报错信息。这是因为 time.Time 类型默认支持的日期格式与我们传入的格式不同,导致解析出错

解决方案

1. 自定义时间类型DateTime
type DateTime time.Time
var TimeFormats = []string{"2006-01-02 15:04:05", "20060102150405"}
2. 解析 JSON 格式数据 - UnmarshalJSON 与 MarshalJSON

c.ShouldBindJSON进行参数绑定时,会调用 field.UnmarshalJSON方法,所以需要重写 UnmarshalJSON

func (t *DateTime) UnmarshalJSON(data []byte) (err error) {
	fmt.Println(string(data))
	// 空值不进行解析
	if len(data) == 2 {
		*t = DateTime(time.Time{})
		return
	}

	var now time.Time
	for _, format := range TimeFormats {
		// 指定解析的格式
		if now, err = time.ParseInLocation(format, string(data), time.Local); err == nil {
			*t = DateTime(now)
			return
		}
		// 指定解析的格式
		if now, err = time.ParseInLocation(`"`+format+`"`, string(data), time.Local); err == nil {
			*t = DateTime(now)
			return
		}
	}

	return
}

用于接口返回时, 需要调用c.JSON, 会调用 field.MarshalJSON方法, 所以需要重写 MarshalJSON

func (t DateTime) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(TimeFormats[0])+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, TimeFormats[0])
	b = append(b, '"')
	return b, nil
}
demo
package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)

type TestTime struct {
	StartTime time.Time `json:"startTime" form:"startTime" binding:"required"`
	EndTime   time.Time `json:"endTime" form:"endTime"`
}

type DateTime time.Time
type TestDateTime struct {
	StartTime DateTime `json:"startTime" form:"startTime" binding:"required"`
	EndTime   DateTime `json:"endTime" form:"endTime"`
}

var TimeFormats = []string{"2006-01-02 15:04:05", "20060102150405"}

func (t *DateTime) UnmarshalJSON(data []byte) (err error) {
	fmt.Println(string(data))
	// 空值不进行解析
	if len(data) == 2 {
		*t = DateTime(time.Time{})
		return
	}

	var now time.Time
	for _, format := range TimeFormats {
		// 指定解析的格式
		if now, err = time.ParseInLocation(format, string(data), time.Local); err == nil {
			*t = DateTime(now)
			return
		}
		// 指定解析的格式
		if now, err = time.ParseInLocation(`"`+format+`"`, string(data), time.Local); err == nil {
			*t = DateTime(now)
			return
		}
	}

	return
}
func (t DateTime) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(TimeFormats[0])+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, TimeFormats[0])
	b = append(b, '"')
	return b, nil
}
func (t DateTime) String() string {
	return time.Time(t).Format(TimeFormats[0])
}

func main() {
	router := gin.New()

	router.GET("/test", func(c *gin.Context) {
		var form TestTime
		c.ShouldBind(&form)
		c.JSON(200, gin.H{
			"message": form,
		})
	})
	router.POST("/test2", func(c *gin.Context) {
		var form TestDateTime
		fmt.Println(c.ShouldBind(&form))
		fmt.Println(form)
		c.JSON(200, gin.H{
			"message": form,
		})
	})
	router.GET("/test2", func(c *gin.Context) {
		var form TestDateTime
		fmt.Println(c.ShouldBind(&form))
		fmt.Println(form)
		c.JSON(200, gin.H{
			"message": form,
		})
	})

	s := &http.Server{
		Addr:           ":8000",
		Handler:        router,
		ReadTimeout:    60,
		WriteTimeout:   60,
		MaxHeaderBytes: 1 << 20,
	}

	s.ListenAndServe()
}

gorm的update问题, 可参考: https://github.com/a1029563229/Blogs/tree/master/BugFix/go/time

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值