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