引入

json 包 解码数据时常遇到时间字段,日志记录、定时任务、性能测量也都离不开时间。Go 的 time 包提供时间的创建、格式化、运算和定时功能,是日常开发中用得最多的标准库之一。

正文

定义

定义

time 包提供时间处理功能,包括获取当前时间、创建时间点、格式化与解析、计算时间差、定时器和周期定时器。Go 的时间格式化使用固定的参考时间而非格式化字符串。

语法

import "time"
 
now := time.Now()                       // 当前时间
t := time.Date(2026, 7, 20, 15, 0, 0, 0, time.UTC) // 指定时间
duration := t.Sub(now)                  // 时间差
str := t.Format("2006-01-02 15:04:05") // 格式化

例子

获取当前时间

now := time.Now()
 
fmt.Println(now.Year())    // 2026
fmt.Println(now.Month())   // July
fmt.Println(now.Day())     // 20
fmt.Println(now.Hour())    // 15
fmt.Println(now.Minute())  // 30
fmt.Println(now.Second())  // 0

time.Now() 返回当前本地时间。通过方法可以取出年、月、日、时、分、秒各个分量。

创建指定时间

t := time.Date(2026, 7, 20, 15, 30, 0, 0, time.UTC)
fmt.Println(t)  // 2026-07-20 15:30:00 +0000 UTC
 
// 使用本地时区
t2 := time.Date(2026, 7, 20, 15, 30, 0, 0, time.Local)

time.Date 创建指定时间点,参数依次是年、月、日、时、分、秒、纳秒、时区。

格式化时间

t := time.Date(2026, 7, 20, 15, 30, 0, 0, time.UTC)
 
fmt.Println(t.Format("2006-01-02 15:04:05"))    // 2026-07-20 15:30:00
fmt.Println(t.Format("2006/01/02"))              // 2026/07/20
fmt.Println(t.Format("15:04:05"))                // 15:30:00
fmt.Println(t.Format(time.RFC3339))              // 2026-07-20T15:30:00Z

Go 的格式化不用 %Y%m%d 这种占位符,而是用固定参考时间 2006-01-02 15:04:05(即 Unix 时间 1136239445)。记住这个参考时间就掌握了格式化。

解析时间字符串

layout := "2006-01-02 15:04:05"
value := "2026-07-20 15:30:00"
 
t, err := time.Parse(layout, value)
if err != nil {
    log.Fatal(err)
}
fmt.Println(t.Year(), t.Month(), t.Day())  // 2026 July 20

time.Parse 用同样的参考时间布局把字符串解析为 time.Time

时间差

start := time.Now()
time.Sleep(2 * time.Second)
end := time.Now()
 
diff := end.Sub(start)
fmt.Println(diff)                          // 2.000...s
fmt.Println(diff.Seconds())                // 2.000...
fmt.Println(diff.Milliseconds())           // 2000...

Sub 返回 time.Duration 类型,表示两个时间点之间的间隔Duration 可以调用 Seconds()Milliseconds() 等方法转换单位。

时间加减

now := time.Now()
 
tomorrow := now.Add(24 * time.Hour)
lastWeek := now.AddDate(0, 0, -7)
 
fmt.Println(tomorrow)
fmt.Println(lastWeek)

Add 加上一个 DurationAddDate年月日加。Before()After() 比较时间先后。

定时器 Timer

timer := time.NewTimer(3 * time.Second)
 
<-timer.C
fmt.Println("3 秒到了")

NewTimer 创建一个一次性定时器,到时后往通道 C 发送当前时间。用 <-timer.C 阻塞等待。

周期定时器 Ticker

ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
 
for i := 0; i < 5; i++ {
    <-ticker.C
    fmt.Println("tick", i)
}

NewTicker 创建一个周期性定时器,每隔指定时间往通道发一次信号。用完要 Stop() 释放资源。

Sleep

fmt.Println("开始")
time.Sleep(2 * time.Second)
fmt.Println("2 秒后")

time.Sleep 让当前 goroutine 暂停指定时长。简单直接,但不适合精确计时。

常见写法

写法说明
time.Now()获取当前时间
time.Date(y, m, d, h, min, s, ns, loc)创建指定时间
t.Format("2006-01-02 15:04:05")格式化时间
time.Parse(layout, value)解析时间字符串
t.Sub(t2)计算时间差
t.Add(d)时间加一个 Duration
t.AddDate(y, m, d)时间加年月日
t.Before(t2) / t.After(t2)比较时间
time.Sleep(d)暂停指定时长
time.NewTimer(d)一次性定时器
time.NewTicker(d)周期性定时器
time.Since(t)t 到现在的时间差

特点

  • Go 格式化用固定参考时间 2006-01-02 15:04:05,不是 %Y 那种占位符
  • time.Duration 底层是 int64 纳秒,精度到纳秒
  • Timer 一次性触发,Ticker 周期性触发,底层都是通道通信
  • Ticker 用完必须 Stop(),否则** goroutine 泄漏**
  • time.Since(t) 等价于 time.Now().Sub(t)测量耗时最常用
  • 时区用 time.UTCtime.Local,生产环境建议统一用 UTC

理解

time 包的核心就三个东西:Time(时间点)、Duration(时间间隔)、格式化布局。格式化记住那个神奇的 2006-01-02 15:04:05 就行,所有格式都从这个参考时间变出来。TimerTicker 配合通道使用,在并发编程和定时任务里非常好使。time.Since 一行就能测耗时,写性能日志离不开它。

引出

time 包能记录时间点,但如果有一批时间数据要按先后排列,就需要排序能力。接下来看 Go sort 包,学习怎么对切片进行排序。