1.14 时间处理
time.Time 结构体
go
package main
import (
"fmt"
"time"
)
func main() {
// 获取当前时间
now := time.Now()
fmt.Println("当前时间:", now)
// 创建特定时间
t := time.Date(2024, time.January, 15, 10, 30, 0, 0, time.UTC)
fmt.Println("特定时间:", t)
// 时间戳 (秒)
ts := t.Unix()
fmt.Println("Unix 时间戳:", ts)
// 时间戳 (纳秒)
tns := t.UnixNano()
fmt.Println("Unix 时间戳 (纳秒):", tns)
// 从时间戳创建
t2 := time.Unix(ts, 0)
fmt.Println("从时间戳创建:", t2)
// 从时间戳 (毫秒) 创建
t3 := time.UnixMilli(ts * 1000)
fmt.Println("从毫秒时间戳创建:", t3)
}时间格式化与解析
go
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")) // 2024-01-15
fmt.Println("时间:", now.Format("15:04:05")) // 10:30:00
fmt.Println("完整:", now.Format("2006-01-02 15:04:05")) // 2024-01-15 10:30:00
fmt.Println("RFC3339:", now.Format(time.RFC3339))
fmt.Println("RFC1123:", now.Format(time.RFC1123))
// 自定义格式
fmt.Println("年月日:", now.Format("2006 年 01 月 02 日"))
fmt.Println("时分:", now.Format("15 时 04 分"))
// 解析时间
t1, _ := time.Parse("2006-01-02", "2024-01-15")
fmt.Println("解析日期:", t1)
t2, _ := time.Parse(time.RFC3339, "2024-01-15T10:30:00Z")
fmt.Println("解析 RFC3339:", t2)
// 解析带时区
loc, _ := time.LoadLocation("Asia/Shanghai")
t3, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-01-15 10:30:00", loc)
fmt.Println("解析本地时间:", t3)
}常用时间布局常量
go
const (
Layout = "01/02 03:04:05PM '06 -0700"
ANSIC = time.ANSIC // "Mon Jan _2 15:04:05 2006"
UnixDate = time.UnixDate // "Mon Jan _2 15:04:05 MST 2006"
RubyDate = time.RubyDate // "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = time.RFC822 // "02 Jan 06 15:04 MST"
RFC822Z = time.RFC822Z // "02 Jan 06 15:04 -0700"
RFC850 = time.RFC850 // "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = time.RFC1123Z // "Mon, 02 Jan 2006 15:04:05 -0700"
RFC3339 = time.RFC3339 // "2006-01-02T15:04:05Z07:00"
RFC3339Nano = time.RFC3339Nano // "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = time.Kitchen // "3:04PM"
)时间运算
go
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
t := time.Date(2024, time.January, 15, 10, 30, 0, 0, time.UTC)
// Add: 增加时间
tomorrow := now.Add(24 * time.Hour)
nextWeek := now.Add(7 * 24 * time.Hour)
nextMonth := now.AddDate(0, 1, 0) // 增加一个月
nextYear := now.AddDate(1, 0, 0) // 增加一年
fmt.Println("明天:", tomorrow.Format("2006-01-02"))
fmt.Println("下周:", nextWeek.Format("2006-01-02"))
fmt.Println("下月:", nextMonth.Format("2006-01-02"))
fmt.Println("明年:", nextYear.Format("2006-01-02"))
// Sub: 计算时间差
diff := now.Sub(t)
fmt.Println("时间差:", diff)
fmt.Println("天数:", int(diff.Hours()/24))
// After: 判断是否在之后
if now.After(t) {
fmt.Println("现在在目标时间之后")
}
// Before: 判断是否在之前
if now.Before(tomorrow) {
fmt.Println("现在在明天之前")
}
// Equal: 判断两个时间是否相等 (考虑时区)
t1 := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
t2 := time.Date(2024, 1, 15, 18, 0, 0, 0, time.FixedZone("CST", 8*3600))
fmt.Println("时间相等:", t1.Equal(t2)) // true
// Compare: 比较时间
// -1: t < other, 0: t == other, 1: t > other
fmt.Println("比较结果:", t.Compare(now))
}时间单位常量
go
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
// 使用示例
time.Sleep(100 * time.Millisecond)
ticker := time.NewTicker(5 * time.Second)定时器 (Timer, Ticker)
Timer (一次性定时器)
go
package main
import (
"fmt"
"time"
)
func main() {
// 创建定时器
timer := time.NewTimer(2 * time.Second)
// 等待定时器
<-timer.C
fmt.Println("2 秒后触发")
// 带超时的 select
timer2 := time.NewTimer(1 * time.Second)
select {
case <-timer2.C:
fmt.Println("定时器触发")
case <-time.After(500 * time.Millisecond):
fmt.Println("超时了")
}
// 停止定时器
timer3 := time.NewTimer(time.Hour)
if timer3.Stop() {
fmt.Println("定时器已停止")
}
}Ticker (周期性定时器)
go
package main
import (
"fmt"
"time"
)
func main() {
// 创建周期定时器
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
done := make(chan bool)
go func() {
for {
select {
case t := <-ticker.C:
fmt.Println("触发:", t)
case <-done:
fmt.Println("停止 ticker")
return
}
}
}()
// 2 秒后停止
time.Sleep(2 * time.Second)
done <- true
// 使用 time.Tick (简单场景,无法停止)
for t := range time.Tick(time.Second) {
fmt.Println("每秒触发:", t)
// 注意:无法停止,适用于程序生命周期
}
}time.After (延迟触发)
go
// 常用在 select 中设置超时
select {
case result := <-doSomething():
fmt.Println("结果:", result)
case <-time.After(3 * time.Second):
fmt.Println("超时了!")
}时区处理
go
package main
import (
"fmt"
"time"
)
func main() {
// 获取本地时区
loc := time.Local
fmt.Println("本地时区:", loc)
// 获取 UTC
utc := time.UTC
fmt.Println("UTC 时区:", utc)
// 加载特定时区
shanghai, _ := time.LoadLocation("Asia/Shanghai")
tokyo, _ := time.LoadLocation("Asia/Tokyo")
newYork, _ := time.LoadLocation("America/New_York")
// 时区转换
now := time.Now()
fmt.Println("本地时间:", now)
fmt.Println("上海时间:", now.In(shanghai))
fmt.Println("东京时间:", now.In(tokyo))
fmt.Println("纽约时间:", now.In(newYork))
fmt.Println("UTC 时间:", now.In(utc))
// 在特定时区创建时间
t := time.Date(2024, 1, 15, 10, 30, 0, 0, shanghai)
fmt.Println("上海时间:", t)
fmt.Println("对应 UTC:", t.UTC())
// 固定偏移时区
cst := time.FixedZone("CST", 8*3600) // 东八区 (秒)
fmt.Println("CST 时区:", cst)
}常见时区名称
go
// 亚洲
"Asia/Shanghai" // 中国
"Asia/Tokyo" // 日本
"Asia/Seoul" // 韩国
"Asia/Singapore" // 新加坡
"Asia/Bangkok" // 泰国
"Asia/Ho_Chi_Minh" // 越南
"Asia/Jakarta" // 印尼
// 美洲
"America/New_York" // 美国东部
"America/Los_Angeles" // 美国西部
"America/Chicago" // 美国中部
"America/Toronto" // 加拿大
"America/Sao_Paulo" // 巴西
// 欧洲
"Europe/London" // 英国
"Europe/Paris" // 法国
"Europe/Berlin" // 德国
"Europe/Moscow" // 俄罗斯时间戳转换
go
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// 转秒级时间戳
ts := now.Unix()
fmt.Println("秒级时间戳:", ts)
// 转毫秒级时间戳
tsMs := now.UnixMilli()
fmt.Println("毫秒级时间戳:", tsMs)
// 转纳秒级时间戳
tsNs := now.UnixNano()
fmt.Println("纳秒级时间戳:", tsNs)
// 从秒级时间戳创建
t1 := time.Unix(ts, 0)
fmt.Println("从秒创建:", t1)
// 从毫秒级时间戳创建
t2 := time.UnixMilli(tsMs)
fmt.Println("从毫秒创建:", t2)
// 从纳秒级时间戳创建
t3 := time.Unix(0, tsNs)
fmt.Println("从纳秒创建:", t3)
}实用转换函数
go
// 格式化时间戳为可读字符串
func FormatTimestamp(ts int64) string {
return time.Unix(ts, 0).Format("2006-01-02 15:04:05")
}
// 解析日期字符串为时间戳
func ParseToTimestamp(dateStr string) (int64, error) {
t, err := time.Parse("2006-01-02", dateStr)
if err != nil {
return 0, err
}
return t.Unix(), nil
}
// 获取当天的开始时间
func StartOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
// 获取当天的结束时间
func EndOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 999999999, t.Location())
}耗时计算 (Since, Until)
go
package main
import (
"fmt"
"time"
)
func main() {
// Since: 从指定时间到现在
start := time.Now()
time.Sleep(100 * time.Millisecond)
elapsed := time.Since(start)
fmt.Println("耗时:", elapsed)
fmt.Println("耗时 (毫秒):", elapsed.Milliseconds())
// Until: 从当前时间到指定时间
deadline := time.Now().Add(5 * time.Second)
remaining := time.Until(deadline)
fmt.Println("剩余时间:", remaining)
// 手动计算
diff := deadline.Sub(start)
fmt.Println("时间差:", diff)
}性能测试用耗时计算
go
// 使用 time.Now().Sub()
start := time.Now()
doSomething()
elapsed := time.Since(start)
fmt.Printf("耗时:%v\n", elapsed)
// 或使用 time.Now().UnixNano()
startNs := time.Now().UnixNano()
doSomething()
elapsedNs := time.Now().UnixNano() - startNs
fmt.Printf("耗时:%d 纳秒\n", elapsedNs)实用时间工具函数
go
package utils
import (
"time"
)
// 获取当前时间戳 (秒)
func Now() int64 {
return time.Now().Unix()
}
// 获取当前时间戳 (毫秒)
func NowMilli() int64 {
return time.Now().UnixMilli()
}
// 格式化时间
func Format(t time.Time, layout string) string {
if layout == "" {
layout = "2006-01-02 15:04:05"
}
return t.Format(layout)
}
// 解析时间
func Parse(dateStr string, layout string) (time.Time, error) {
if layout == "" {
layout = "2006-01-02 15:04:05"
}
return time.Parse(layout, dateStr)
}
// 是否为今天
func IsToday(t time.Time) bool {
now := time.Now()
return t.Year() == now.Year() &&
t.YearDay() == now.YearDay()
}
// 是否为昨天
func IsYesterday(t time.Time) bool {
yesterday := time.Now().AddDate(0, 0, -1)
return t.Year() == yesterday.Year() &&
t.YearDay() == yesterday.YearDay()
}
// 获取本周开始时间 (周一)
func StartOfWeek(t time.Time) time.Time {
weekday := int(t.Weekday())
if weekday == 0 {
weekday = 7
}
return StartOfDay(t.AddDate(0, 0, -(weekday - 1)))
}
// 获取本周结束时间 (周日)
func EndOfWeek(t time.Time) time.Time {
return EndOfDay(StartOfWeek(t).AddDate(0, 0, 6))
}
// 获取本月开始时间
func StartOfMonth(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location())
}
// 获取本月结束时间
func EndOfMonth(t time.Time) time.Time {
return StartOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond)
}
// 天数差
func DaysBetween(t1, t2 time.Time) int {
return int(t2.Sub(t1).Hours() / 24)
}
// 工作日后 n 天
func AddBusinessDays(t time.Time, days int) time.Time {
result := t
for days > 0 {
result = result.AddDate(0, 0, 1)
if result.Weekday() != time.Saturday && result.Weekday() != time.Sunday {
days--
}
}
return result
}时间处理最佳实践
go
// 1. 存储时间使用 UTC
func saveTime(t time.Time) {
utc := t.UTC()
// 存储到数据库
}
// 2. 显示时间转换为本地时区
func displayTime(utc time.Time, location string) string {
loc, _ := time.LoadLocation(location)
return utc.In(loc).Format("2006-01-02 15:04:05")
}
// 3. 使用 context 设置超时
func doWithTimeout(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return doSomething(ctx)
}
// 4. 使用 Ticker 注意清理
func useTicker() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop() // 重要:停止 ticker
for range ticker.C {
// 处理逻辑
}
}
// 5. 避免使用 time.Tick (无法停止)
// 不好
func bad() {
for range time.Tick(time.Second) {
// 无法停止
}
}
// 好
func good() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for range ticker.C {
// 可以停止
}
}时间处理检查清单
[ ] 存储时间使用 UTC
[ ] 显示时间转换为本地时区
[ ] 使用正确的时区名称 (如 Asia/Shanghai)
[ ] Ticker 使用后立即 defer Stop()
[ ] 避免使用 time.Tick (无法停止)
[ ] 超时控制使用 context 或 time.After
[ ] 解析时间时处理错误
[ ] 比较时间使用 Equal/Before/After
[ ] 时间戳转换注意单位 (秒/毫秒/纳秒)
[ ] 跨时区应用统一使用 RFC3339 格式