Go 时间的格式化

Go 时间的格式化

Go 的 time.Time 在序列化和反序列化时默认使用 RFC3339 规范的格式进行解析或输出,我们需要将其转换成更易读的格式。

解决方案

实现 MarshalJSON 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Log struct {
Id int64 `json:"id"`
UserId string `json:"user_id"`
Operation string `json:"operation"`
Result string `json:"result"`
CreatedAt time.Time `json:"created_at"`
}

func (log *Log) MarshalJSON() ([]byte, error) {
type Alias Log
return json.Marshal(&struct {
Alias
CreatedAt string `json:"created_at"`
}{
Alias: Alias(*log),
CreatedAt: log.CreatedAt.Format("2006-01-02 15:04:05"),
})
}

这种方式比较简单,但是如果你有很多 struct 都包含时间,那么需要为每个 struct 实现 MarshalJSON,比较麻烦。并且在反向序列化时也存在 created_at 为 null 则反序列化失败的问题。

至于为什么时间格式是 2006-01-02 15:04:05,可以参考 time 这个包的 fromat.go 中定义的常量。

自已定一个 Time 数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package utils

import (
"time"
)

type (
Time time.Time
)

const DefaultTimeFormat = "2006-01-02 15:04:05"

func (t *Time) UnmarshalJSON(data []byte) error {
now, err := time.ParseInLocation(`"`+DefaultTimeFormat+`"`, string(data), time.Local)
*t = Time(now)
return err
}

func (t *Time) MarshalJSON() ([]byte, error) {
if time.Time(*t).IsZero() {
return []byte("null"), nil
}
b := make([]byte, 0, len(DefaultTimeFormat)+2)
b = append(b, '"')
b = time.Time(*t).AppendFormat(b, DefaultTimeFormat)
b = append(b, '"')
return b, nil
}

func (t Time) String() string {
return time.Time(t).Format(DefaultTimeFormat)
}

func (t *Time) IsZero() bool {
return time.Time(*t).Second() == 0 && time.Time(*t).Nanosecond() == 0
}

定义好时间的类型后,在其他的 struct 中使用该类型作为时间的类型。

1
2
3
4
5
6
Node struct {
Id string `json:"id"`
Name string `json:"name"`
CreatedAt utils.Time `json:"created_at"`
UpdatedAt utils.Time `json:"updated_at"`
}

这样当 Node 在序列化和反序列化时会自动调用自定义 utils.TimeMarshalJSONUnmarshalJSON 方法。

注意:在 MarshalJSON 方法中,如果你的 utils.Time 为空,那么将其转换为 null,而不是 0001-01-01 00:00:00 +0000 UTC。

如果你有更好的方案,请告诉我,谢谢!

# Go

评论