time 包 - 时间处理详解
time 包提供了时间的显示、测量、格式化等功能。掌握 time 包是开发定时任务、日志记录、性能监控等场景的基础。
📌 核心概念
⏰
Time
时间点
time.Now()
⏱️
Duration
时间间隔
time.Second
⏲️
Timer
定时器
time.NewTimer
🔄
Ticker
周期触发器
time.NewTicker
Time 时间点
📝 获取和操作时间
package main
import (
"fmt"
"time"
)
func main() {
// 当前时间
now := time.Now()
fmt.Printf("Now: %v\n", now)
// 时间分量
fmt.Printf("Year: %d\n", now.Year())
fmt.Printf("Month: %d\n", now.Month())
fmt.Printf("Day: %d\n", now.Day())
fmt.Printf("Hour: %d\n", now.Hour())
fmt.Printf("Minute: %d\n", now.Minute())
fmt.Printf("Second: %d\n", now.Second())
fmt.Printf("Weekday: %v\n", now.Weekday())
// 时间计算
tomorrow := now.Add(24 * time.Hour)
fmt.Printf("Tomorrow: %v\n", tomorrow)
yesterday := now.AddDate(0, 0, -1)
fmt.Printf("Yesterday: %v\n", yesterday)
// 时间比较
fmt.Printf("Before: %v\n", now.Before(tomorrow))
fmt.Printf("After: %v\n", now.After(tomorrow))
fmt.Printf("Equal: %v\n", now.Equal(now))
// 时间差
diff := tomorrow.Sub(now)
fmt.Printf("Diff: %v hours\n", diff.Hours())
}
Duration 时间间隔
📝 Duration 操作
package main
import (
"fmt"
"time"
)
func main() {
// 内置常量
fmt.Println(time.Nanosecond) // 1ns
fmt.Println(time.Microsecond) // 1µs
fmt.Println(time.Millisecond) // 1ms
fmt.Println(time.Second) // 1s
fmt.Println(time.Minute) // 1m0s
fmt.Println(time.Hour) // 1h0m0s
// 创建 Duration
d1 := 5 * time.Second
d2 := 2 * time.Minute
d3 := time.Hour + 30 * time.Minute
// Duration 计算
fmt.Printf("d1 + d2: %v\n", d1+d2)
fmt.Printf("d2 - d1: %v\n", d2-d1)
fmt.Printf("d1 * 2: %v\n", d1*2)
fmt.Printf("d2 / 2: %v\n", d2/2)
// 转换为不同单位
fmt.Printf("d1 Nanoseconds: %d\n", d1.Nanoseconds())
fmt.Printf("d1 Microseconds: %d\n", d1.Microseconds())
fmt.Printf("d1 Milliseconds: %d\n", d1.Milliseconds())
fmt.Printf("d1 Seconds: %f\n", d1.Seconds())
fmt.Printf("d1 Minutes: %f\n", d1.Minutes())
fmt.Printf("d1 Hours: %f\n", d1.Hours())
// 解析 Duration 字符串
d4, _ := time.ParseDuration("1h30m45s")
fmt.Printf("Parsed: %v\n", d4)
}
时间格式化
📝 Format 和 Parse
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// Go 的参考时间:2006-01-02 15:04:05
// 记忆口诀:1 2 3 4 5 6 (美国格式)
// 常用格式
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format("2006/01/02"))
fmt.Println(now.Format("15:04:05"))
fmt.Println(now.Format("2006-01-02T15:04:05Z07:00")) // RFC3339
// 自定义格式
fmt.Println(now.Format("Jan 2, 2006 at 3:04pm"))
fmt.Println(now.Format("Monday, January 2, 2006"))
// 解析时间字符串
t1, _ := time.Parse("2006-01-02", "2024-03-15")
fmt.Printf("Parsed: %v\n", t1)
t2, _ := time.Parse(time.RFC3339, "2024-03-15T10:30:00Z")
fmt.Printf("RFC3339: %v\n", t2)
// 常用布局常量
fmt.Println(now.Format(time.RFC822))
fmt.Println(now.Format(time.RFC1123))
fmt.Println(now.Format(time.DateTime))
}
💡 格式化要点
- 参考时间: Go 使用
2006-01-02 15:04:05作为参考格式 - 记忆方法: 1 2 3 4 5 6 (美国日期格式)
- 时区: 使用
Z07:00或MST表示时区 - 布局常量: time.RFC3339、time.RFC822 等
Timer 和 Ticker
Timer 一次性定时器
📝 Timer 使用
package main
import (
"fmt"
"time"
)
func main() {
// 创建 Timer
timer := time.NewTimer(time.Second * 2)
// 等待 Timer
<-timer.C
fmt.Println("2 seconds elapsed")
// 使用 AfterFunc
time.AfterFunc(time.Second, func() {
fmt.Println("1 second later (async)")
})
// 简单等待
<-time.After(time.Millisecond * 500)
fmt.Println("500ms elapsed")
// 停止 Timer
timer2 := time.NewTimer(time.Minute)
if timer2.Stop() {
fmt.Println("Timer stopped before firing")
}
time.Sleep(time.Millisecond * 100)
}
Ticker 周期触发器
📝 Ticker 使用
package main
import (
"fmt"
"time"
)
func main() {
// 创建 Ticker
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
// 周期执行
done := make(chan bool)
go func() {
count := 0
for {
select {
case <-ticker.C:
count++
fmt.Printf("Tick %d\n", count)
if count >= 5 {
done <- true
return
}
}
}
}()
<-done
fmt.Println("Ticker stopped")
}
性能分析
📊 使用 time 进行性能测试
package main
import (
"fmt"
"time"
)
func main() {
// 方法 1: 使用 Sub
start := time.Now()
doWork()
elapsed := time.Since(start)
fmt.Printf("Elapsed: %v\n", elapsed)
// 方法 2: 使用 defer
timedFunction()
// 方法 3: 使用 AfterFunc 超时
done := make(chan bool)
timeout := time.AfterFunc(time.Second, func() {
fmt.Println("Timeout!")
})
go func() {
doWork()
done <- true
}()
<-done
timeout.Stop()
}
func doWork() {
time.Sleep(time.Millisecond * 100)
}
func timedFunction() {
defer func(start time.Time) {
fmt.Printf("Function took: %v\n", time.Since(start))
}(time.Now())
doWork()
}
时区处理
📝 时区转换
package main
import (
"fmt"
"time"
)
func main() {
// 本地时间
local := time.Now()
fmt.Printf("Local: %v (%v)\n", local, local.Location())
// UTC 时间
utc := local.UTC()
fmt.Printf("UTC: %v\n", utc)
// 加载时区
loc, _ := time.LoadLocation("Asia/Shanghai")
shanghai := local.In(loc)
fmt.Printf("Shanghai: %v\n", shanghai)
loc2, _ := time.LoadLocation("America/New_York")
ny := local.In(loc2)
fmt.Printf("New York: %v\n", ny)
// 固定偏移时区
fixedZone := time.FixedZone("CST", 8*3600)
fmt.Printf("Fixed: %v\n", local.In(fixedZone))
}
最佳实践
✅ Time 使用建议
- 存储 UTC: 数据库中存储 UTC 时间
- 显示本地化: 展示时转换为本地时间
- 使用常量: time.Second、time.Minute 等
- 停止 Ticker: 使用 defer ticker.Stop()
- 超时控制: 使用 time.After 或 context
- 避免泄漏: Timer/Ticker 用后及时停止
🚨 常见陷阱
- Timer 泄漏: 不调用 Stop() 会导致资源泄漏
- Ticker 泄漏: 必须调用 Stop() 停止
- 时区问题: 跨时区系统应统一使用 UTC
- 时间比较: 使用 Equal() 而非 ==
- 格式化错误: 记住参考时间是 2006 不是 1970
总结
✅ 核心要点
- Time: 时间点,使用 time.Now() 获取
- Duration: 时间间隔,纳秒为单位
- Format/Parse: 使用参考时间 2006-01-02 15:04:05
- Timer: 一次性定时器,用后 Stop()
- Ticker: 周期触发器,用后 Stop()
- 时区: 存储 UTC,显示本地化