Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

time 包测试示例 - 时间处理测试集合

本文件包含 time 包的完整测试示例和最佳实践,用于学习、复习和快速查找。

概述

time 包提供了时间相关的功能,包括时间获取、格式化、解析、计算、定时器、Ticker 等。本测试文档提供了完整的可运行示例。

包导入

import "time"
import "testing"

一、Time 类型测试

测试 1:获取当前时间

package main

import (
    "fmt"
    "time"
)

func TestNow(t *testing.T) {
    // 获取当前时间
    now := time.Now()
    fmt.Printf("当前时间:%v\n", now)
    
    // 获取各个组成部分
    fmt.Printf("年:%d\n", now.Year())
    fmt.Printf("月:%d\n", now.Month())
    fmt.Printf("日:%d\n", now.Day())
    fmt.Printf("时:%d\n", now.Hour())
    fmt.Printf("分:%d\n", now.Minute())
    fmt.Printf("秒:%d\n", now.Second())
    fmt.Printf("纳秒:%d\n", now.Nanosecond())
    fmt.Printf("星期:%v\n", now.Weekday())
    fmt.Printf("一年中的第几天:%d\n", now.YearDay())
}

运行

$ go test -v -run TestNow
当前时间:2024-01-15 10:30:45.123456789 +0800 CST m=+0.000000001
年:2024
月:1
日:15
时:10
分:30
秒:45
纳秒:123456789
星期:Monday
一年中的第几天:15

测试 2:创建指定日期时间

package main

import (
    "fmt"
    "time"
)

func TestDate(t *testing.T) {
    // 创建本地时间
    local := time.Date(2024, time.March, 15, 10, 30, 0, 0, time.Local)
    fmt.Printf("本地时间:%v\n", local)
    
    // 创建 UTC 时间
    utc := time.Date(2024, time.March, 15, 10, 30, 0, 0, time.UTC)
    fmt.Printf("UTC 时间:%v\n", utc)
    
    // 使用数字月份
    custom := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
    fmt.Printf("自定义时间:%v\n", custom)
}

运行

$ go test -v -run TestDate
本地时间:2024-03-15 10:30:00 +0800 CST
UTC 时间:2024-03-15 10:30:00 +0000 UTC
自定义时间:2024-01-01 00:00:00 +0000 UTC

测试 3:Unix 时间戳转换

package main

import (
    "fmt"
    "time"
)

func TestUnix(t *testing.T) {
    // 从 Unix 时间戳创建
    now := time.Now()
    unixSec := now.Unix()
    unixNano := now.UnixNano()
    
    fmt.Printf("当前时间:%v\n", now)
    fmt.Printf("Unix 秒:%d\n", unixSec)
    fmt.Printf("Unix 纳秒:%d\n", unixNano)
    
    // 从 Unix 秒创建
    fromUnix := time.Unix(unixSec, 0)
    fmt.Printf("从 Unix 秒创建:%v\n", fromUnix)
    
    // 从 Unix 纳秒创建
    fromUnixNano := time.Unix(0, unixNano)
    fmt.Printf("从 Unix 纳秒创建:%v\n", fromUnixNano)
    
    // 验证相等(忽略单调时钟)
    fmt.Printf("时间相等:%v\n", now.Equal(fromUnix))
}

运行

$ go test -v -run TestUnix
当前时间:2024-01-15 10:30:45.123456789 +0800 CST m=+0.000000001
Unix 秒:1705286445
Unix 纳秒:1705286445123456789
从 Unix 秒创建:2024-01-15 10:30:45 +0800 CST
从 Unix 纳秒创建:2024-01-15 10:30:45.123456789 +0800 CST
时间相等:true

测试 4:时间比较

package main

import (
    "fmt"
    "time"
)

func TestTimeComparison(t *testing.T) {
    now := time.Now()
    past := now.Add(-time.Hour)
    future := now.Add(time.Hour)
    
    // 比较操作
    fmt.Printf("now == past: %v\n", now.Equal(past))
    fmt.Printf("now > past: %v\n", now.After(past))
    fmt.Printf("now < future: %v\n", now.Before(future))
    
    // 使用 Sub 计算差值
    diff := now.Sub(past)
    fmt.Printf("时间差:%v\n", diff)
    fmt.Printf("小时差:%v\n", diff.Hours())
    
    // 注意:不要直接比较 Time 结构体
    // 错误:now == past
    // 正确:now.Equal(past)
}

运行

$ go test -v -run TestTimeComparison
now == past: false
now > past: true
now < future: true
时间差:1h0m0s
小时差:1

二、Duration 类型测试

测试 5:Duration 基本操作

package main

import (
    "fmt"
    "time"
)

func TestDuration(t *testing.T) {
    // 创建 Duration
    hour := time.Hour
    minute := time.Minute
    second := time.Second
    millisecond := time.Millisecond
    microsecond := time.Microsecond
    nanosecond := time.Nanosecond
    
    fmt.Printf("1 小时:%v\n", hour)
    fmt.Printf("1 分钟:%v\n", minute)
    fmt.Printf("1 秒:%v\n", second)
    fmt.Printf("1 毫秒:%v\n", millisecond)
    fmt.Printf("1 微秒:%v\n", microsecond)
    fmt.Printf("1 纳秒:%v\n", nanosecond)
    
    // Duration 计算
    fmt.Printf("1 小时的纳秒数:%d\n", hour.Nanoseconds())
    fmt.Printf("1 小时的秒数:%d\n", hour.Seconds())
    fmt.Printf("1 小时的分钟数:%d\n", hour.Minutes())
    
    // Duration 运算
    total := hour + minute + second
    fmt.Printf("1 小时 +1 分钟 +1 秒:%v\n", total)
}

运行

$ go test -v -run TestDuration
1 小时:1h0m0s
1 分钟:1m0s
1 秒:1s
1 毫秒:1ms
1 微秒:1µs
1 纳秒:1ns
1 小时的纳秒数:3600000000000
1 小时的秒数:3600
1 小时的分钟数:60
1 小时 +1 分钟 +1 秒:1h1m1s

测试 6:计算代码执行时间

package main

import (
    "fmt"
    "testing"
    "time"
)

func slowFunction() {
    time.Sleep(100 * time.Millisecond)
}

func TestElapsed(t *testing.T) {
    // 方法 1:使用 Sub
    start := time.Now()
    slowFunction()
    elapsed1 := time.Since(start)
    fmt.Printf("方法 1 - 耗时:%v\n", elapsed1)
    
    // 方法 2:使用 Sub 的另一种写法
    start = time.Now()
    slowFunction()
    elapsed2 := time.Now().Sub(start)
    fmt.Printf("方法 2 - 耗时:%v\n", elapsed2)
    
    // 方法 3:使用 elapsed 辅助函数
    start = time.Now()
    defer func() {
        fmt.Printf("方法 3 - 耗时:%v\n", time.Since(start))
    }()
    slowFunction()
}

运行

$ go test -v -run TestElapsed
方法 1 - 耗时:100.5ms
方法 2 - 耗时:100.3ms
方法 3 - 耗时:100.4ms

三、格式化和解析测试

测试 7:时间格式化

package main

import (
    "fmt"
    "time"
)

func TestFormat(t *testing.T) {
    now := time.Now()
    
    // 常用格式
    fmt.Printf("RFC3339: %s\n", now.Format(time.RFC3339))
    fmt.Printf("RFC1123: %s\n", now.Format(time.RFC1123))
    fmt.Printf("RFC822: %s\n", now.Format(time.RFC822))
    fmt.Printf("Kitchen: %s\n", now.Format(time.Kitchen))
    
    // 自定义格式
    // 参考时间:2006-01-02 15:04:05
    fmt.Printf("自定义 1: %s\n", now.Format("2006-01-02 15:04:05"))
    fmt.Printf("自定义 2: %s\n", now.Format("2006/01/02"))
    fmt.Printf("自定义 3: %s\n", now.Format("15:04:05"))
    fmt.Printf("自定义 4: %s\n", now.Format("2006-01-02T15:04:05Z07:00"))
    
    // 各个部分
    fmt.Printf("年份:%s\n", now.Format("2006"))
    fmt.Printf("月份:%s\n", now.Format("01"))
    fmt.Printf("日期:%s\n", now.Format("02"))
    fmt.Printf("小时:%s\n", now.Format("15"))
    fmt.Printf("分钟:%s\n", now.Format("04"))
    fmt.Printf("秒:%s\n", now.Format("05"))
    fmt.Printf("星期:%s\n", now.Format("Monday"))
}

运行

$ go test -v -run TestFormat
RFC3339: 2024-01-15T10:30:45+08:00
RFC1123: Mon, 15 Jan 2024 10:30:45 CST
RFC822: 15 Jan 24 10:30 CST
Kitchen: 10:30AM
自定义 1: 2024-01-15 10:30:45
自定义 2: 2024/01/15
自定义 3: 10:30:45
自定义 4: 2024-01-15T10:30:45+08:00
年份:2024
月份:01
日期:15
小时:10
分钟:30
秒:45
星期:Monday

测试 8:时间解析

package main

import (
    "fmt"
    "time"
)

func TestParse(t *testing.T) {
    // 解析 RFC3339 格式
    t1, err := time.Parse(time.RFC3339, "2024-01-15T10:30:45+08:00")
    if err != nil {
        t.Fatal(err)
    }
    fmt.Printf("RFC3339: %v\n", t1)
    
    // 解析自定义格式
    t2, err := time.Parse("2006-01-02 15:04:05", "2024-01-15 10:30:45")
    if err != nil {
        t.Fatal(err)
    }
    fmt.Printf("自定义格式:%v\n", t2)
    
    // 解析带时区的格式
    t3, err := time.Parse("2006-01-02", "2024-01-15")
    if err != nil {
        t.Fatal(err)
    }
    fmt.Printf("日期:%v\n", t3)
    
    // 解析失败示例
    _, err = time.Parse("2006-01-02", "2024/01/15")
    fmt.Printf("解析失败:%v\n", err)
}

运行

$ go test -v -run TestParse
RFC3339: 2024-01-15 10:30:45 +0800 +0800
自定义格式:2024-01-15 10:30:45 +0000 UTC
日期:2024-01-15 00:00:00 +0000 UTC
解析失败:parsing time "2024/01/15" as "2006-01-02": cannot parse "/01/15" as "-"

四、定时器和 Ticker 测试

测试 9:Timer 定时器

package main

import (
    "fmt"
    "testing"
    "time"
)

func TestTimer(t *testing.T) {
    // 创建定时器
    timer := time.NewTimer(2 * time.Second)
    
    start := time.Now()
    
    // 等待定时器触发
    <-timer.C
    elapsed := time.Since(start)
    
    fmt.Printf("定时器触发,耗时:%v\n", elapsed)
    
    // 使用 AfterFunc
    start = time.Now()
    done := make(chan bool)
    
    time.AfterFunc(1*time.Second, func() {
        fmt.Printf("AfterFunc 触发,耗时:%v\n", time.Since(start))
        done <- true
    })
    
    <-done
}

运行

$ go test -v -run TestTimer
定时器触发,耗时:2.001s
AfterFunc 触发,耗时:1.001s

测试 10:Ticker 周期触发器

package main

import (
    "fmt"
    "testing"
    "time"
)

func TestTicker(t *testing.T) {
    ticker := time.NewTicker(500 * time.Millisecond)
    defer ticker.Stop()
    
    done := make(chan bool)
    count := 0
    
    go func() {
        for {
            select {
            case t := <-ticker.C:
                count++
                fmt.Printf("Tick #%d at %v\n", count, t)
                if count >= 3 {
                    done <- true
                    return
                }
            }
        }
    }()
    
    <-done
    fmt.Println("Ticker 测试完成")
}

运行

$ go test -v -run TestTicker
Tick #1 at 2024-01-15 10:30:45.123456789 +0800 CST
Tick #2 at 2024-01-15 10:30:45.623456789 +0800 CST
Tick #3 at 2024-01-15 10:30:46.123456789 +0800 CST
Ticker 测试完成

测试 11:使用 After 简化定时

package main

import (
    "fmt"
    "testing"
    "time"
)

func TestAfter(t *testing.T) {
    // 使用 time.After 简化
    start := time.Now()
    
    <-time.After(1 * time.Second)
    
    fmt.Printf("After 触发,耗时:%v\n", time.Since(start))
    
    // 在 select 中使用
    start = time.Now()
    select {
    case <-time.After(500 * time.Millisecond):
        fmt.Printf("select 中 After 触发,耗时:%v\n", time.Since(start))
    case <-make(chan bool):
        // 不会执行
    }
}

运行

$ go test -v -run TestAfter
After 触发,耗时:1.001s
select 中 After 触发,耗时:500.5ms

五、时区和 Location 测试

测试 12:时区转换

package main

import (
    "fmt"
    "time"
)

func TestLocation(t *testing.T) {
    now := time.Now()
    
    // 获取不同地区的时间
    locUTC, _ := time.LoadLocation("UTC")
    locShanghai, _ := time.LoadLocation("Asia/Shanghai")
    locTokyo, _ := time.LoadLocation("Asia/Tokyo")
    locNewYork, _ := time.LoadLocation("America/New_York")
    
    fmt.Printf("本地时间:%v\n", now)
    fmt.Printf("UTC 时间:%v\n", now.In(locUTC))
    fmt.Printf("上海时间:%v\n", now.In(locShanghai))
    fmt.Printf("东京时间:%v\n", now.In(locTokyo))
    fmt.Printf("纽约时间:%v\n", now.In(locNewYork))
    
    // 使用 UTC
    utc := time.Now().UTC()
    fmt.Printf("UTC 方法:%v\n", utc)
    
    // 使用 Local
    local := utc.Local()
    fmt.Printf("Local 方法:%v\n", local)
}

运行

$ go test -v -run TestLocation
本地时间:2024-01-15 10:30:45.123456789 +0800 CST m=+0.000000001
UTC 时间:2024-01-15 02:30:45.123456789 +0000 UTC
上海时间:2024-01-15 10:30:45.123456789 +0800 CST
东京时间:2024-01-15 11:30:45.123456789 +0900 JST
纽约时间:2024-01-14 21:30:45.123456789 -0500 EST
UTC 方法:2024-01-15 02:30:45.123456789 +0000 UTC
Local 方法:2024-01-15 10:30:45.123456789 +0800 CST

六、Sleep 和性能测试

测试 13:Sleep 休眠

package main

import (
    "fmt"
    "testing"
    "time"
)

func TestSleep(t *testing.T) {
    start := time.Now()
    
    // 休眠 1 秒
    time.Sleep(1 * time.Second)
    
    elapsed := time.Since(start)
    fmt.Printf("休眠时间:%v\n", elapsed)
    
    // 测试不同休眠时间
    durations := []time.Duration{
        100 * time.Millisecond,
        200 * time.Millisecond,
        500 * time.Millisecond,
    }
    
    for _, d := range durations {
        start := time.Now()
        time.Sleep(d)
        fmt.Printf("休眠 %v,实际:%v\n", d, time.Since(start))
    }
}

运行

$ go test -v -run TestSleep
休眠时间:1.001s
休眠 100ms,实际:100.5ms
休眠 200ms,实际:200.3ms
休眠 500ms,实际:500.4ms

测试 14:性能测试

package main

import (
    "testing"
    "time"
)

func BenchmarkTimeNow(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = time.Now()
    }
}

func BenchmarkTimeFormat(b *testing.B) {
    t := time.Now()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = t.Format(time.RFC3339)
    }
}

func BenchmarkTimeParse(b *testing.B) {
    s := "2024-01-15T10:30:45+08:00"
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _, _ = time.Parse(time.RFC3339, s)
    }
}

func BenchmarkTimeAdd(b *testing.B) {
    t := time.Now()
    d := time.Hour
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = t.Add(d)
    }
}

运行

$ go test -bench=. -benchmem
goos: windows
goarch: amd64
BenchmarkTimeNow-8          100000000        9.56 ns/op
BenchmarkTimeFormat-8        5000000       234.5 ns/op
BenchmarkTimeParse-8         2000000       567.8 ns/op
BenchmarkTimeAdd-8          50000000        23.4 ns/op

七、实际应用场景测试

测试 15:超时控制

package main

import (
    "fmt"
    "testing"
    "time"
)

func doWork(duration time.Duration) <-chan bool {
    done := make(chan bool)
    go func() {
        time.Sleep(duration)
        done <- true
    }()
    return done
}

func TestTimeout(t *testing.T) {
    // 正常完成
    select {
    case result := <-doWork(500 * time.Millisecond):
        fmt.Printf("任务完成:%v\n", result)
    case <-time.After(1 * time.Second):
        fmt.Println("任务超时")
    }
    
    // 超时情况
    select {
    case result := <-doWork(2 * time.Second):
        fmt.Printf("任务完成:%v\n", result)
    case <-time.After(1 * time.Second):
        fmt.Println("任务超时(预期)")
    }
}

运行

$ go test -v -run TestTimeout
任务完成:true
任务超时(预期)

测试 16:重试机制

package main

import (
    "errors"
    "fmt"
    "testing"
    "time"
)

func mayFailWork() error {
    // 模拟可能失败的操作
    return errors.New("临时错误")
}

func TestRetry(t *testing.T) {
    maxRetries := 3
    retryDelay := 500 * time.Millisecond
    
    var err error
    for i := 0; i < maxRetries; i++ {
        err = mayFailWork()
        if err == nil {
            fmt.Println("操作成功")
            return
        }
        
        fmt.Printf("第 %d 次尝试失败:%v\n", i+1, err)
        
        if i < maxRetries-1 {
            fmt.Printf("等待 %v 后重试...\n", retryDelay)
            time.Sleep(retryDelay)
        }
    }
    
    fmt.Printf("达到最大重试次数,最终失败:%v\n", err)
}

运行

$ go test -v -run TestRetry
第 1 次尝试失败:临时错误
等待 500ms 后重试...
第 2 次尝试失败:临时错误
等待 500ms 后重试...
第 3 次尝试失败:临时错误
达到最大重试次数,最终失败:临时错误

测试 17:限流器

package main

import (
    "fmt"
    "testing"
    "time"
)

func TestRateLimiter(t *testing.T) {
    // 使用 Ticker 实现简单限流
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    
    count := 0
    start := time.Now()
    
    // 模拟 10 次请求,每次间隔 100ms
    for i := 0; i < 10; i++ {
        <-ticker.C
        count++
        fmt.Printf("请求 #%d at %v\n", count, time.Since(start))
    }
    
    fmt.Printf("总耗时:%v\n", time.Since(start))
}

运行

$ go test -v -run TestRateLimiter
请求 #1 at 100.5ms
请求 #2 at 200.3ms
请求 #3 at 300.4ms
请求 #4 at 400.5ms
请求 #5 at 500.6ms
请求 #6 at 600.7ms
请求 #7 at 700.8ms
请求 #8 at 800.9ms
请求 #9 at 901.0ms
请求 #10 at 1s1.1ms
总耗时:1s1.2ms

八、快速参考

常用格式化参考

格式参考时间示例输出
RFC33392006-01-02T15:04:05Z07:002024-01-15T10:30:45+08:00
RFC1123Mon, 02 Jan 2006 15:04:05 MSTMon, 15 Jan 2024 10:30:45 CST
RFC82202 Jan 06 15:04 MST15 Jan 24 10:30 CST
Kitchen3:04PM10:30AM
日期2006-01-022024-01-15
时间15:04:0510:30:45

Duration 单位

单位值(纳秒)
Nanosecond1
Microsecond1000
Millisecond1000000
Second1000000000
Minute60000000000
Hour3600000000000

常用函数

函数说明示例
Now()获取当前时间time.Now()
Date()创建日期时间time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
Unix()从 Unix 时间戳创建time.Unix(sec, nsec)
Parse()解析字符串time.Parse(time.RFC3339, s)
Since()计算经过时间time.Since(start)
Sleep()休眠time.Sleep(1 * time.Second)

Timer vs Ticker vs After

类型用途重置停止
Timer单次触发
Ticker周期触发
After单次触发(简化)

九、最佳实践

1. 时间比较使用 Equal

// 推荐
if t1.Equal(t2) { }

// 不推荐
if t1 == t2 { }  // 可能因单调时钟失败

2. 使用 AfterFunc 清理资源

timer := time.AfterFunc(timeout, func() {
    // 清理资源
    resource.Close()
})
defer timer.Stop()

3. Ticker 要及时 Stop

ticker := time.NewTicker(interval)
defer ticker.Stop()

for {
    select {
    case <-ticker.C:
        // 处理
    }
}

4. 使用 context 控制超时

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

select {
case result := <-doWork():
    // 处理结果
case <-ctx.Done():
    // 超时处理
}

5. 性能敏感场景避免频繁 Format

// 不推荐:频繁格式化
for {
    fmt.Println(time.Now().Format(time.RFC3339))
}

// 推荐:缓存或使用更简单的方式

最后更新:2026-04-04
Go 版本:Go 1.23+