在写 Golang 之前, 我是 Node.js 开发者. 在接触 Golang 之后迅速遇到了一个时间序列化的问题.

在 Javascript 中, 时间序列化是 ISO8601 格式: YYYY-MM-DDTHH:mm:ss.sssZ, 而Golang 使用的是 RFC3339Nano格式: 2006-01-02T15:04:05.999999999Z07:00.

具体表现:

Javascript: https://jsfiddle.net/bgnuLjxs/1/

image

Golang: https://play.golang.org/p/BG-GzlW5r2a

image

由于历史原因, 移动端未能完美兼容 Golang 输出的时间格式, 导致异常崩溃.

解决方案

定义新类型

如果单纯的解决这个问题, 可以简单定义另一个类型, 并自定义该类型的 MarshalJSON 方法: https://github.com/isayme/go-iso8601

这个方法的问题是会丢失 time.Time 的很多实用方法(如 After / Format 等等), 并且还需要已经在用 time.Time的地方全部更换为新定义的类型, 对既有的代码破坏性很大.

订制 time.go 包

由于目前 Golang 的服务都使用 Docker 发布, 我们可以把基础 Golang 镜像中的time.go中的MarshalJSON方法改写后再重新 install, Dockerfile 片段:

ADD time-1.10.1.go /usr/local/go/src/time/time.go
RUN go install -a time

此处的基础镜像是 Golang 1.10.1, 其中 time-1.10.1.go 是改过之后的文件, 其中改过之后的MarshalJSON是:

image

此方法基本满足当前的场景, 存在的缺点是一旦想升级新的基础 Golang Docker 版本, 就需要重新订制func (t Time) MarshalJSON() ([]byte, error) 方法.

使用 monkey 订制

使用 monkey 替换 Golang 标准库方法 中简单介绍了 mongkey 的使用, 其实它同样可以做到替换实例方法: https://play.golang.org/p/UtF4uHDC9nK

	var t time.Time
	monkey.PatchInstanceMethod(reflect.TypeOf(t), "MarshalJSON", func(t time.Time) ([]byte, error) {
		t = t.UTC()

		s := t.Format("\"2006-01-02T15:04:05.000Z\"")
		return []byte(s), nil
	})

相比前两种方法, 此方法的优势还是比较明显的: 最小的改动达到期望的效果.