Skip to content
鼓励作者:欢迎打赏犒劳

go正则表达式

🔍 Cron 表达式格式说明(6位或5位)

字段数含义
5 位分 时 日 月 星期
6 位秒 分 时 日 月 星期

使用 cron.NewParser(cron.SecondOptional) 可以兼容 5 位和 6 位表达式。

✅ 支持的 Cron 格式示例

  • 0 8 * * * → 每天 8:00
  • */15 * * * * → 每15分钟一次
  • 0 0 1 * * → 每月1号 0:00
  • 0 0 12 * * ? → 支持 Quartz 风格(需开启对应 parser)

列出未来 N 个匹配的时间点

go
package main

import (
	"fmt"
	"time"

	"github.com/robfig/cron/v3"
)

// GetNextCronTimes 解析 Cron 表达式,并返回从 start 开始的接下来 count 个触发时间
// expr: Cron 表达式(支持 5 位:分 时 日 月 星期)
// start: 起始时间(通常为 time.Now())
// count: 需要返回的时间点数量
// timezone: 时区,例如 time.Local 或 time.UTC
// 返回值:匹配的时间点切片,错误信息
func GetNextCronTimes(expr string, start time.Time, count int, loc *time.Location) ([]time.Time, error) {
	// 创建支持 5 位 Cron 的解析器(秒为可选)
	parser := cron.NewParser(
		cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
	)

	schedule, err := parser.Parse(expr)
	if err != nil {
		return nil, fmt.Errorf("解析 Cron 表达式失败: %w", err)
	}

	// 确保时间使用指定时区
	start = start.In(loc)

	var times []time.Time
	current := start

	for i := 0; i < count; i++ {
		next := schedule.Next(current)
		// 防止无限循环(比如表达式无匹配)
		if next.IsZero() {
			break
		}
		times = append(times, next)
		current = next
	}

	return times, nil
}

func main() {
	expr := "0 8 * * 1" // 每周一早上 8:00
	start := time.Now()
	count := 5
	loc := time.Local // 使用本地时区,也可用 time.UTC

	times, err := GetNextCronTimes(expr, start, count, loc)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Cron 表达式: %s\n", expr)
	fmt.Printf("起始时间: %s\n", start.Format("2006-01-02 15:04:05"))
	fmt.Println("接下来的触发时间:")
	for i, t := range times {
		fmt.Printf("%d: %s\n", i+1, t.Format("2006-01-02 15:04:05"))
	}
}

结果:

假设今天是 2025-10-14(周二),Cron 表达式为 "0 8 * * 1"(每周一 8:00)

text
Cron 表达式: 0 8 * * 1
起始时间: 2025-10-14 22:40:01
接下来的触发时间:
1: 2025-10-20 08:00:00
2: 2025-10-27 08:00:00
3: 2025-11-03 08:00:00
4: 2025-11-10 08:00:00
5: 2025-11-17 08:00:00

校验cron是否合法

go
package main

import (
	"fmt"

	"github.com/robfig/cron/v3"
)

func main() {
	// 定义你的 CRON 表达式字符串
	cronExpression := "*/5 * * * *" // 每五分钟执行一次的例子
	//cronExpression := "* */5 * * * *" // 每五分钟执行一次的例子  正常的cron  需要6位

	// 创建一个新的解析器,包含秒、分、时、日、月、星期几以及描述符支持
	parser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)

	// 尝试解析 CRON 表达式
	_, err := parser.Parse(cronExpression)

	if err != nil {
		// 如果有错误,则表达式无效
		fmt.Println("CRON 表达式无效:", err)
	} else {
		// 如果没有错误,则表达式有效
		fmt.Println("CRON 表达式有效")
	}
}

这些常量定义了 CRON 表达式中 每个位置代表什么时间单位。标准 CRON 通常是 5 位或 6 位,而这个库支持扩展到 6 或 7 位(含秒、描述符等)

常量含义对应字段位置(从左到右)示例值
cron.Second第 1 位(可选)0, */5, 30
cron.Minute分钟第 2 位(或第 1 位,若无秒)0-59
cron.Hour小时第 3 位(或第 2 位)0-23
cron.DomDay of Month(每月几号)第 4 位1-31
cron.Month月份第 5 位1-12JAN-DEC
cron.DowDay of Week(星期几)第 6 位0-7(0 和 7 都是周日)或 SUN-SAT
cron.Descriptor描述符支持(非标准字段)——允许使用 @daily, @hourly

🔔 注意:cron.Descriptor 不是一个“字段位置”,而是启用对 预定义别名(如 @every 5s@hourly)的支持。

匹配所有的时间点

go
package main

import (
	"fmt"
	"time"

	"github.com/robfig/cron/v3"
)

// GetCronTimesInRange 解析 Cron 表达式,并返回在 [start, end) 时间区间内所有匹配的时间点
// expr: Cron 表达式(支持 5 位:分 时 日 月 星期)
// start: 区间起始时间(包含)
// end: 区间结束时间(不包含)
// loc: 时区(如 time.Local, time.UTC 或自定义)
// 返回值:该区间内所有触发时间的切片,按时间升序排列
func GetCronTimesInRange(expr string, start, end time.Time, loc *time.Location) ([]time.Time, error) {
	// 确保输入时间使用指定时区
	start = start.In(loc)
	end = end.In(loc)

	if end.Before(start) {
		return nil, fmt.Errorf("结束时间不能早于开始时间")
	}

	// 创建支持 5 位 Cron 的解析器(秒可选)
	parser := cron.NewParser(
		cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
	)

	schedule, err := parser.Parse(expr)
	if err != nil {
		return nil, fmt.Errorf("解析 Cron 表达式失败: %w", err)
	}

	var times []time.Time
	current := start

	for {
		next := schedule.Next(current)
		if next.IsZero() || !next.Before(end) { // next >= end 或为零值则停止
			break
		}
		times = append(times, next)
		current = next
	}

	return times, nil
}
func main() {
	expr := "0 8 * * 1"                                          // 每周一早上 8:00
	startTime := time.Date(2025, 10, 14, 0, 0, 0, 0, time.Local) // 2025-10-14(周二)
	endTime := time.Date(2025, 11, 15, 0, 0, 0, 0, time.Local)   // 2025-11-15

	times, err := GetCronTimesInRange(expr, startTime, endTime, time.Local)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Cron 表达式: %s\n", expr)
	fmt.Printf("时间范围: %s%s\n", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"))
	fmt.Println("匹配的时间点:")
	for i, t := range times {
		fmt.Printf("%d: %s\n", i+1, t.Format("2006-01-02 15:04:05"))
	}
}

结果

text
Cron 表达式: 0 8 * * 1
时间范围: 2025-10-14 到 2025-11-15
匹配的时间点:
1: 2025-10-20 08:00:00
2: 2025-10-27 08:00:00
3: 2025-11-03 08:00:00
4: 2025-11-10 08:00:00

通过cron表达式执行函数

这样就很方便。但是需要注意的是,必须要显示的指定是否开启秒级支持,如果开启,则必须传6位cron表达式,如果没开启,则必须传5位cron表达式

在 robfig/cron/v3 中,你必须显式指定是否启用秒级(如 cron.WithSeconds()),它不会根据 cron 表达式的位数自动判断。

这意味着如果你写一个 6 位的表达式(比如 "0 0 9 * * *"),但没有启用秒字段,就会报错

go
package main

import (
	"fmt"
	"time"

	"github.com/robfig/cron/v3"
)

func main() {
	// 设置时区为 Asia/Shanghai(北京时间)
	location, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		panic(err) // 建议处理 LoadLocation 的错误
	}

	// 创建 cron 调度器,使用上海时区,并启用秒级精度
	cronJob := cron.New(cron.WithLocation(location), cron.WithSeconds())

	// 添加每 3 秒执行一次的任务
	entryId, err := cronJob.AddFunc("*/3 * * * * *", func() {
		fmt.Println(time.Now(), "Good morning!")
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("entryId = %d \n", entryId)

	// 添加每 4 秒执行一次的任务
	entryId2, err := cronJob.AddFunc("*/4 * * * * *", func() {
		fmt.Println(time.Now(), "ffffff")
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("entryId2 = %d \n", entryId2)
	// 启动调度器
	cronJob.Start()

	time.Sleep(10 * time.Second)
	//移除指定的cron
	fmt.Printf("10s之后移除entryId job\n")
	cronJob.Remove(entryId)
	// 阻塞主进程,防止程序退出
	select {}
}

如有转载或 CV 的请标注本站原文地址