跳到主要内容

time 时间处理

问题

Go 的时间处理有哪些坑?为什么格式化用 2006-01-02?Timer 和 Ticker 怎么用?

答案

时间格式化

Go 的时间格式化不是 yyyy-MM-dd,而是使用参考时间

Mon Jan 2 15:04:05 MST 2006

2006年1月2日 3时4分5秒(1-2-3-4-5-6 的顺序):

now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05")) // 2025-01-15 14:30:00
fmt.Println(now.Format("2006/01/02")) // 2025/01/15
fmt.Println(now.Format(time.RFC3339)) // 2025-01-15T14:30:00Z
fmt.Println(now.Format("15:04")) // 14:30
// 解析时间字符串
t, err := time.Parse("2006-01-02", "2025-01-15")
t, err := time.ParseInLocation("2006-01-02 15:04:05", "2025-01-15 14:30:00", time.Local)

Timer 和 Ticker

// Timer:一次性定时器
timer := time.NewTimer(5 * time.Second)
<-timer.C // 5 秒后触发
timer.Stop() // 取消(如果还没触发)
timer.Reset(3 * time.Second) // 重置

// Ticker:周期性定时器
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() // 必须 Stop,否则泄漏

for range ticker.C {
fmt.Println("tick")
}
time.After 在循环中泄漏
// ❌ 每次循环创建新 Timer,旧的直到超时才释放
for {
select {
case <-time.After(5 * time.Second): // 内存泄漏!
}
}

// ✅ 复用 Timer
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
for {
timer.Reset(5 * time.Second)
select {
case <-timer.C:
}
}

时间比较和计算

t1 := time.Now()
t2 := t1.Add(2 * time.Hour)

duration := t2.Sub(t1) // 2h0m0s
elapsed := time.Since(t1) // 等同于 time.Now().Sub(t1)

t1.Before(t2) // true
t1.After(t2) // false
t1.Equal(t2) // false(用 Equal 而不是 ==)

常见面试问题

Q1: 为什么时间格式用 2006-01-02 而不是 yyyy-MM-dd?

答案:Go 创始人认为"用一个真实的日期做模板"比抽象的占位符更直观、不需要记忆。2006-01-02 15:04:05 按 1-2-3-4-5-6 排列,便于记忆。

Q2: time.Now() 在不同时区服务器上结果不同吗?

答案time.Now() 返回的是带时区信息的绝对时间.Unix() 时间戳全球一致,但 .Format() 输出依赖时区。服务端推荐用 UTC:

time.Now().UTC()

相关链接