同事把一段代码从 Java 搬到 Go,日期格式化照抄了 SimpleDateFormat 里的 yyyy-MM-dd HH:mm:ss,跑出来输出一模一样的字符串。他以为是时区问题,查了半小时缓存,其实是 Go 根本不认 y 和 d。

那串 2006-01-02 15:04:05 是模板不是示例

time.Format 用一个固定参考时间描述格式,参考时间是 2006 年 1 月 2 日下午 3 点 4 分 5 秒,时区写成 MST 加 -0700。你把模板里的数字换成想要的样子,它按位置替换。想要 2024/05/01 就写 2006/01/02,要带毫秒写成 2006-01-02 15:04:05.000。

容易记混的几个:15 是 24 小时制,03 是 12 小时制,01 补零月份,1 不补零,02 是补零日期,2 不补零。月份全写用 January,星期用 Monday,缩写是 Mon。

Parse 和 Format 不是一回事

time.Parse 拿同样的模板去读字符串,读出来默认是 UTC。国内业务数据必须用 ParseInLocation,第二个参数传 time.LoadLocation 拿到的 Asia/Shanghai,否则入库整体差 8 小时。Format 只决定输出长什么样,不改时间本身。

时间戳跟时区没关系

Unix 时间戳记的是秒数,同一个瞬间在哪台机器上都是同一个数字。用 t.Unix() 取秒,t.UnixMilli() 取毫秒。前端要 13 位就传毫秒,别自己乘 1000 再拼字符串。从时间戳还原,用 time.Unix 造出时间,再 In 到目标时区展示。

现成的常量先用着

time.RFC3339 就是 2006-01-02T15:04:05Z07:00,传给接口和写日志基本够用,省得自己拼。自定义格式建议定成包级常量,别在循环里反复拼模板字符串。格式化的开销不算大,但在几十万行的导出里也能量出来。

判断办法很朴素:写完打印一遍结果,看到 2024-01-02 这种正常输出,再往下写业务逻辑。模板里写 2024 也能编译通过,只是 2、0、4 会被当成占位符解释,出来是乱的。