Skip to content

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 格式