Go标准库-Time

Published on in Technology with 0 views and 0 comments
  • Go Time 包完全指南

  • 目录

    1. 核心类型概览
    2. 时间点操作 (Time)
    3. 时间间隔 (Duration)
    4. 定时器 (Timer)
    5. 周期触发器 (Ticker)
    6. 时间格式化
    7. 实战场景
    8. 常见陷阱
  • 1. 核心类型概览

    • time 包的核心是 4 个类型
    • go
      // Time - 表示一个时间点
      var t time.Time = time.Now()
      
      // Duration - 表示时间间隔(纳秒精度)
      var d time.Duration = 5 * time.Second
      
      // Timer - 一次性定时器
      var timer *time.Timer = time.NewTimer(5 * time.Second)
      
      // Ticker - 周期性触发器
      var ticker *time.Ticker = time.NewTicker(1 * time.Second)
      
    • 关系图:
    • plain text
      Time (时间点)
        ├── Now() - 获取当前时间
        ├── Add(Duration) - 加上时间间隔
        └── Sub(Time) - 两个时间点相减 → Duration
      
      Duration (时间间隔)
        ├── Second, Minute, Hour - 常量
        └── 用于定时器和时间计算
      
      Timer (一次性)
        └── 在指定 Duration 后触发一次
      
      Ticker (周期性)
        └── 每隔指定 Duration 触发一次
      
  • 2. 时间点操作 (Time)

  • 2.1 获取时间

    • go
      // 获取当前时间(本地时区)
      now := time.Now()
      fmt.Println(now) // 2025-11-24 10:30:45.123456 +0800 CST
      
      // 获取 UTC 时间
      utc := time.Now().UTC()
      
      // 获取 Unix 时间戳
      timestamp := time.Now().Unix()        // 秒级
      timestampMilli := time.Now().UnixMilli() // 毫秒级
      timestampNano := time.Now().UnixNano()   // 纳秒级
      
      // 从时间戳创建 Time
      t := time.Unix(1700000000, 0)         // 秒级时间戳
      t := time.UnixMilli(1700000000000)    // 毫秒级时间戳
      
  • 2.2 时间计算

    • go
      now := time.Now()
      
      // 加上时间间隔
      future := now.Add(2 * time.Hour)              // 2小时后
      past := now.Add(-30 * time.Minute)            // 30分钟前
      
      // 添加日期(年月日)
      nextYear := now.AddDate(1, 0, 0)              // 1年后
      nextMonth := now.AddDate(0, 1, 0)             // 1个月后
      tomorrow := now.AddDate(0, 0, 1)              // 明天
      
      // 两个时间点相减
      start := time.Now()
      // ... 执行某些操作
      end := time.Now()
      elapsed := end.Sub(start)                     // 返回 Duration
      fmt.Printf("耗时: %v\n", elapsed)
      
  • 2.3 时间比较

    • go
      t1 := time.Now()
      time.Sleep(100 * time.Millisecond)
      t2 := time.Now()
      
      // 比较大小
      t1.Before(t2)  // true - t1 在 t2 之前
      t1.After(t2)   // false - t1 在 t2 之后
      t1.Equal(t2)   // false - 完全相等(考虑时区)
      
      // 判断是否为零值
      var t time.Time
      t.IsZero()     // true
      
  • 2.4 时间组成部分

    • go
      now := time.Now()
      
      // 获取各个部分
      year := now.Year()        // 2025
      month := now.Month()      // November (类型是 time.Month)
      day := now.Day()          // 24
      hour := now.Hour()        // 10
      minute := now.Minute()    // 30
      second := now.Second()    // 45
      weekday := now.Weekday()  // Monday (类型是 time.Weekday)
      
      // 获取年月日
      y, m, d := now.Date()
      
      // 获取时分秒
      h, min, s := now.Clock()
      
      // 年中的第几天
      yearDay := now.YearDay()  // 328
      
  • 3. 时间间隔 (Duration)

  • 3.1 Duration 常量

    • go
      // time 包预定义的常量
      time.Nanosecond   // 1 纳秒
      time.Microsecond  // 1000 纳秒
      time.Millisecond  // 1000000 纳秒
      time.Second       // 1000000000 纳秒
      time.Minute       // 60 秒
      time.Hour         // 60 分钟
      
      // 使用示例
      d := 5 * time.Second              // 5秒
      d := 100 * time.Millisecond       // 100毫秒
      d := 2*time.Hour + 30*time.Minute // 2小时30分钟
      
  • 3.2 Duration 操作

    • go
      d := 90 * time.Second
      
      // 转换为不同单位
      d.Seconds()      // 90.0 (float64)
      d.Milliseconds() // 90000 (int64)
      d.Microseconds() // 90000000 (int64)
      d.Nanoseconds()  // 90000000000 (int64)
      
      // Duration 运算
      d1 := 1 * time.Hour
      d2 := 30 * time.Minute
      sum := d1 + d2        // 1小时30分钟
      diff := d1 - d2       // 30分钟
      doubled := d1 * 2     // 2小时
      half := d1 / 2        // 30分钟
      
  • 3.3 解析 Duration 字符串

    • go
      // 解析字符串为 Duration
      d, err := time.ParseDuration("1h30m")
      // 支持的单位: ns, us(μs), ms, s, m, h
      
      // 常见格式
      time.ParseDuration("300ms")    // 300毫秒
      time.ParseDuration("1.5h")     // 1.5小时
      time.ParseDuration("2h45m")    // 2小时45分钟
      time.ParseDuration("1h30m20s") // 1小时30分20秒
      
      // Duration 转字符串
      d := 90 * time.Second
      s := d.String() // "1m30s"
      
  • 4. 定时器 (Timer)

  • 4.1 基本使用

    • go
      // 创建一次性定时器
      timer := time.NewTimer(2 * time.Second)
      
      // 等待定时器触发
      <-timer.C  // 阻塞,直到 2 秒后
      fmt.Println("Timer fired!")
      
      // 简化写法(不需要手动管理 Timer)
      <-time.After(2 * time.Second)
      fmt.Println("2 seconds passed")
      
    • ⚠️ 注意: 会创建 Timer 但不能取消,慎用!
  • 4.2 停止 Timer

    • go
      timer := time.NewTimer(5 * time.Second)
      
      // 启动 goroutine 等待
      go func() {
          <-timer.C
          fmt.Println("Timer fired!")
      }()
      
      // 2秒后取消定时器
      time.Sleep(2 * time.Second)
      if timer.Stop() {
          fmt.Println("Timer stopped before firing")
      } else {
          fmt.Println("Timer already fired")
      }
      
    • Stop() 返回值:
      • : 成功停止,定时器未触发
      • : 定时器已经触发或已被停止
  • 4.3 重置 Timer

    • go
      timer := time.NewTimer(1 * time.Second)
      
      // 首次触发
      <-timer.C
      fmt.Println("First fire")
      
      // 重置定时器为 2 秒
      timer.Reset(2 * time.Second)
      
      // 再次触发
      <-timer.C
      fmt.Println("Second fire")
      
    • ⚠️ 重要:Reset 的陷阱
    • go
      // ❌ 错误用法
      timer := time.NewTimer(1 * time.Second)
      timer.Reset(2 * time.Second)  // 可能有问题
      
      // ✅ 正确用法
      timer := time.NewTimer(1 * time.Second)
      if !timer.Stop() {
          <-timer.C  // 先排空 channel
      }
      timer.Reset(2 * time.Second)
      
  • 4.4 Timer 实战场景

    • 场景 1:超时控制
    • go
      func doWithTimeout(timeout time.Duration) error {
          timer := time.NewTimer(timeout)
          defer timer.Stop()
      
          resultCh := make(chan string)
      
          go func() {
              // 执行耗时操作
              time.Sleep(3 * time.Second)
              resultCh <- "done"
          }()
      
          select {
          case result := <-resultCh:
              fmt.Println("Success:", result)
              return nil
          case <-timer.C:
              return fmt.Errorf("timeout after %v", timeout)
          }
      }
      
    • 场景 2:延迟执行
    • go
      func delayedAction(delay time.Duration, action func()) {
          timer := time.AfterFunc(delay, action)
      
          // 可以取消
          // timer.Stop()
      }
      
      // 使用
      delayedAction(5*time.Second, func() {
          fmt.Println("5 seconds later")
      })
      
  • 5. 周期触发器 (Ticker)

  • 5.1 基本使用

    • go
      // 创建每秒触发一次的 Ticker
      ticker := time.NewTicker(1 * time.Second)
      defer ticker.Stop()
      
      // 方式 1:循环接收
      for i := 0; i < 5; i++ {
          t := <-ticker.C
          fmt.Println("Tick at", t)
      }
      
      // 方式 2:用 range
      ticker2 := time.NewTicker(500 * time.Millisecond)
      defer ticker2.Stop()
      
      for t := range ticker2.C {
          fmt.Println("Tick at", t)
          // 需要手动 break 退出
      }
      
  • 5.2 停止 Ticker

    • go
      ticker := time.NewTicker(1 * time.Second)
      
      go func() {
          for range ticker.C {
              fmt.Println("Tick")
          }
      }()
      
      // 10 秒后停止
      time.Sleep(10 * time.Second)
      ticker.Stop()
      
    • ⚠️ 重要:必须调用 Stop()
    • go
      // ❌ 错误:忘记 Stop,导致 goroutine 泄漏
      func bad() {
          ticker := time.NewTicker(1 * time.Second)
          for range ticker.C {
              // ...
              break  // 退出循环,但 ticker 没停止
          }
          // ticker 仍在运行,永久泄漏
      }
      
      // ✅ 正确
      func good() {
          ticker := time.NewTicker(1 * time.Second)
          defer ticker.Stop()  // 确保停止
      
          for range ticker.C {
              // ...
              break
          }
      }
      
  • 5.3 Ticker 实战场景

    • 场景 1:心跳检测
    • go
      func heartbeat(interval time.Duration, stopCh <-chan struct{}) {
          ticker := time.NewTicker(interval)
          defer ticker.Stop()
      
          for {
              select {
              case <-ticker.C:
                  fmt.Println("Heartbeat:", time.Now())
                  // 发送心跳包
      
              case <-stopCh:
                  fmt.Println("Heartbeat stopped")
                  return
              }
          }
      }
      
      // 使用
      stopCh := make(chan struct{})
      go heartbeat(5*time.Second, stopCh)
      
      // 30秒后停止
      time.Sleep(30 * time.Second)
      close(stopCh)
      
    • 场景 2:定时任务
    • go
      func periodicTask() {
          ticker := time.NewTicker(1 * time.Minute)
          defer ticker.Stop()
      
          for range ticker.C {
              // 每分钟执行一次
              cleanupOldFiles()
              collectMetrics()
          }
      }
      
    • 场景 3:限流
    • go
      type RateLimiter struct {
          ticker *time.Ticker
      }
      
      func NewRateLimiter(requestsPerSecond int) *RateLimiter {
          interval := time.Second / time.Duration(requestsPerSecond)
          return &RateLimiter{
              ticker: time.NewTicker(interval),
          }
      }
      
      func (rl *RateLimiter) Wait() {
          <-rl.ticker.C
      }
      
      func (rl *RateLimiter) Stop() {
          rl.ticker.Stop()
      }
      
      // 使用:限制为每秒 10 个请求
      limiter := NewRateLimiter(10)
      defer limiter.Stop()
      
      for i := 0; i < 100; i++ {
          limiter.Wait()  // 控制速率
          makeRequest(i)
      }
      
  • 6. 时间格式化

  • 6.1 格式化时间

    • Go 使用参考时间来定义格式:
    • go
      now := time.Now()
      
      // 常用格式
      now.Format("2006-01-02")                // 2025-11-24
      now.Format("2006-01-02 15:04:05")       // 2025-11-24 10:30:45
      now.Format("2006/01/02 15:04:05")       // 2025/11/24 10:30:45
      now.Format("15:04:05")                  // 10:30:45
      now.Format("3:04 PM")                   // 10:30 AM
      now.Format("Jan 2, 2006")               // Nov 24, 2025
      now.Format("Monday, 02-Jan-06")         // Monday, 24-Nov-25
      
      // 预定义格式常量
      now.Format(time.RFC3339)      // 2025-11-24T10:30:45+08:00
      now.Format(time.RFC822)       // 24 Nov 25 10:30 CST
      now.Format(time.Kitchen)      // 10:30AM
      now.Format(time.Stamp)        // Nov 24 10:30:45
      now.Format(time.DateTime)     // 2025-11-24 10:30:45
      now.Format(time.DateOnly)     // 2025-11-24
      now.Format(time.TimeOnly)     // 10:30:45
      
    • 为什么用这个奇怪的时间?
    • plain text
      Mon Jan 2 15:04:05 MST 2006
      → 1   2   3  4  5   6    7
      
      记忆法:1234567
      
  • 6.2 解析时间字符串

    • go
      // Parse:使用 UTC 时区
      t, err := time.Parse("2006-01-02", "2025-11-24")
      
      // ParseInLocation:指定时区
      loc, _ := time.LoadLocation("Asia/Shanghai")
      t, err := time.ParseInLocation("2006-01-02 15:04:05", 
          "2025-11-24 10:30:45", loc)
      
      // 常见格式解析
      time.Parse(time.RFC3339, "2025-11-24T10:30:45+08:00")
      time.Parse("2006-01-02", "2025-11-24")
      time.Parse("01/02/2006", "11/24/2025")
      
  • 7. 实战场景

  • 7.1 测量代码执行时间

    • go
      // 方式 1:手动计算
      start := time.Now()
      // ... 执行代码
      elapsed := time.Since(start)
      fmt.Printf("耗时: %v\n", elapsed)
      
      // 方式 2:使用 defer
      func timeTrack(start time.Time, name string) {
          elapsed := time.Since(start)
          fmt.Printf("%s 耗时: %v\n", name, elapsed)
      }
      
      func slowFunction() {
          defer timeTrack(time.Now(), "slowFunction")
      
          // ... 函数逻辑
          time.Sleep(2 * time.Second)
      }
      
  • 7.2 超时控制(结合 Context)

    • go
      func fetchWithTimeout(url string, timeout time.Duration) (string, error) {
          ctx, cancel := context.WithTimeout(context.Background(), timeout)
          defer cancel()
      
          resultCh := make(chan string, 1)
          errCh := make(chan error, 1)
      
          go func() {
              // 模拟 HTTP 请求
              time.Sleep(2 * time.Second)
              resultCh <- "response data"
          }()
      
          select {
          case result := <-resultCh:
              return result, nil
          case err := <-errCh:
              return "", err
          case <-ctx.Done():
              return "", ctx.Err() // context.DeadlineExceeded
          }
      }
      
  • 7.3 重试机制

    • go
      func retryWithBackoff(maxRetries int, operation func() error) error {
          backoff := 1 * time.Second
      
          for i := 0; i < maxRetries; i++ {
              err := operation()
              if err == nil {
                  return nil
              }
      
              if i < maxRetries-1 {
                  fmt.Printf("Retry %d failed, waiting %v\n", i+1, backoff)
                  time.Sleep(backoff)
                  backoff *= 2  // 指数退避
              }
          }
      
          return fmt.Errorf("failed after %d retries", maxRetries)
      }
      
  • 7.4 定时任务调度

    • go
      func scheduleDaily(hour, minute int, task func()) {
          ticker := time.NewTicker(1 * time.Minute)
          defer ticker.Stop()
      
          for range ticker.C {
              now := time.Now()
              if now.Hour() == hour && now.Minute() == minute {
                  go task()  // 在 goroutine 中执行,避免阻塞
              }
          }
      }
      
      // 每天凌晨 2 点执行备份
      go scheduleDaily(2, 0, func() {
          fmt.Println("Running daily backup...")
          // 备份逻辑
      })
      
  • 8. 常见陷阱

  • 8.1 time.After 内存泄漏

    • go
      // ❌ 错误:在循环中使用 time.After
      for {
          select {
          case <-time.After(1 * time.Second):  // 每次循环创建新 Timer
              // 旧的 Timer 无法被GC,造成内存泄漏
              process()
          }
      }
      
      // ✅ 正确:使用 Ticker
      ticker := time.NewTicker(1 * time.Second)
      defer ticker.Stop()
      
      for range ticker.C {
          process()
      }
      
  • 8.2 忘记 Stop Timer/Ticker

    • go
      // ❌ 错误:Timer/Ticker 没有停止
      func bad() {
          timer := time.NewTimer(1 * time.Hour)
          // ... 使用 timer
          return  // timer 还在运行,资源泄漏
      }
      
      // ✅ 正确:使用 defer
      func good() {
          timer := time.NewTimer(1 * time.Hour)
          defer timer.Stop()
          // ... 使用 timer
      }
      
  • 8.3 Reset 的正确姿势

    • go
      // ❌ 错误:直接 Reset 可能 channel 中还有数据
      timer := time.NewTimer(1 * time.Second)
      // ... timer 触发了
      timer.Reset(2 * time.Second)  // 可能无效
      
      // ✅ 正确:Reset 前先排空
      timer := time.NewTimer(1 * time.Second)
      if !timer.Stop() {
          <-timer.C  // 排空 channel
      }
      timer.Reset(2 * time.Second)
      
  • 8.4 时区问题

    • go
      // ❌ 错误:忽略时区
      t, _ := time.Parse("2006-01-02 15:04:05", "2025-11-24 10:30:45")
      // t 是 UTC 时区!
      
      // ✅ 正确:指定时区
      loc, _ := time.LoadLocation("Asia/Shanghai")
      t, _ := time.ParseInLocation("2006-01-02 15:04:05", 
          "2025-11-24 10:30:45", loc)
      
  • 8.5 Timer.C 只能读取一次

    • go
      timer := time.NewTimer(1 * time.Second)
      
      // ❌ 错误:多次读取
      <-timer.C  // 第一次读取,OK
      <-timer.C  // 永远阻塞!因为 channel 已经空了
      
      // ✅ 正确:Timer 是一次性的,需要 Reset
      timer := time.NewTimer(1 * time.Second)
      <-timer.C
      timer.Reset(1 * time.Second)
      <-timer.C
      
  • 9. Time 包函数速查表

  • 9.1 创建时间

    • {{table}}
      • 函数
        • 说明
          • 示例
        • 当前时间
          *
        • 从 Unix 时间戳创建
          *
        • 指定年月日时分秒创建
          *
        • 解析字符串
          *
  • 9.2 时间计算

    • {{table}}
      • 方法
        • 说明
          • 示例
        • 加上时间间隔
          *
        • 两个时间相减
          *
        • 加上年月日
          *
        • 从 t 到现在的间隔
          *
        • 从现在到 t 的间隔
          *
  • 9.3 定时器

    • {{table}}
      • 函数/方法
        • 说明
          • 示例
        • 睡眠指定时间
          *
        • 返回 channel,d 后发送时间
          *
        • 创建一次性定时器
          *
        • 停止定时器
          *
        • 重置定时器
          *
        • d 后执行函数 f
          *
  • 9.4 周期触发器

    • {{table}}
      • 函数/方法
        • 说明
          • 示例
        • 创建周期触发器
          *
        • 简化版Ticker(不能Stop)
          *
        • 停止触发器
          *

标题:Go标准库-Time
作者:wangzhaoo
地址:http://www.bangnimang.top/go-time