使用Timer或Ticker实现在指定时间执行任务

package main

import (
   "fmt"
   "strconv"
   "strings"
   "time"
)

var location, _ = time.LoadLocation("Asia/Shanghai")
var hms = "04:00:00" // 在每天的这个时间(时:分:秒)执行任务

// GetNextDuration 获取到下一次执行任务的剩余时间
func GetNextDuration() time.Duration {
   now := time.Now()    // 当前时间
   year := now.Year()   // 年
   month := now.Month() // 月
   day := now.Day()     // 日

   slice := strings.Split(hms, ":")
   hour, _ := strconv.Atoi(slice[0]) // 时
   min, _ := strconv.Atoi(slice[1])  // 分
   sec, _ := strconv.Atoi(slice[2])  // 秒

   // 获取当前时间和下一次执行任务时间
   nowTime := time.Now().In(location)
   nextTime := time.Date(year, month, day, hour, min, sec, 0, location).In(location)

   // 今天已经错过了执行任务时间,把下一次执行任务时间改为明天
   if nextTime.Sub(nowTime) < 0 {
      nextTime = time.Date(year, month, day+1, hour, min, sec, 0, location).In(location)
   }

   return nextTime.Sub(nowTime) // 这里是计算时间差,可以理解为“nextTime - nowTime”
}

func main() {
   index := 0 // 任务序号

   for {
      index++

      dateTime := time.Unix(time.Now().In(location).Unix(), 0).In(location).Format(time.DateTime) // 当前日期时间
      duration := GetNextDuration()
      cron := time.NewTimer(duration) // 创建任务,这里也可以用time.NewTicker(),效果是一样的
      fmt.Printf("[%s] 创建定时任务(%d)成功,距离执行任务剩余时间:%s \n", dateTime, index, duration)

      c := <-cron.C // 重要说明:这里会发生阻塞,直到执行任务时间
      fmt.Printf("[%s] 开始执行定时任务(%d) \n", c.In(location).Format(time.DateTime), index)
      fmt.Println("")

      // 这里写定时任务的逻辑
      // TODO...

      cron.Stop() // 终止任务,进入下一轮循环后再重新创建任务
   }
}

//========== 总结 ==========//
// 1、time.NewTimer()和time.NewTicker()本身并不支持接收一个具体时间点,然后在到达该时间点后执行任务,但是它们都支持接收一个
//    时间差,在经过这段时间后就会执行任务。举个具体例子,假设要在每天凌晨4点执行任务,那么就可以算出当前时间距离凌晨4点的剩
//    余时间,将这个剩余时间作为time.NewTimer()或time.NewTicker()的参数就可以触发在凌晨4点执行任务,这就是执行一次任务的完整
//    流程,将这个流程放入死循环中就可以实现每天执行任务。

Copyright © 2024 码农人生. All Rights Reserved