feat: 新增 chrono 包,用于替代原本的 timer 及 times 包
This commit is contained in:
parent
92d6c5680d
commit
e608e9257e
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit bb9c3f74620c19b139d13c20793f9ff016d302c0
|
|
@ -0,0 +1,38 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// NewAdjuster 创建一个时间调节器
|
||||||
|
func NewAdjuster(adjust time.Duration) *Adjuster {
|
||||||
|
return &Adjuster{adjust: adjust}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjuster 时间调节器是一个用于获取偏移时间的工具
|
||||||
|
type Adjuster struct {
|
||||||
|
adjust time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust 获取偏移调整的时间量
|
||||||
|
func (a *Adjuster) Adjust() time.Duration {
|
||||||
|
return a.adjust
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdjust 设置偏移调整的时间量
|
||||||
|
func (a *Adjuster) SetAdjust(adjust time.Duration) {
|
||||||
|
a.adjust = adjust
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAdjust 增加偏移调整的时间量
|
||||||
|
func (a *Adjuster) AddAdjust(adjust time.Duration) {
|
||||||
|
a.adjust += adjust
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now 获取经过偏移调整的当前时间
|
||||||
|
func (a *Adjuster) Now() time.Time {
|
||||||
|
return time.Now().Add(a.adjust)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since 获取经过偏移调整的时间间隔
|
||||||
|
func (a *Adjuster) Since(t time.Time) time.Duration {
|
||||||
|
return a.Now().Sub(t)
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
var (
|
||||||
|
builtInAdjuster *Adjuster
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
builtInAdjuster = NewAdjuster(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuiltInAdjuster 获取内置的由 NewAdjuster(0) 函数创建的时间调节器
|
||||||
|
func BuiltInAdjuster() *Adjuster {
|
||||||
|
return builtInAdjuster
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now 调用内置时间调节器 BuiltInAdjuster 的 Adjuster.Now 函数
|
||||||
|
func Now() time.Time {
|
||||||
|
return BuiltInAdjuster().Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since 调用内置时间调节器 BuiltInAdjuster 的 Adjuster.Since 函数
|
||||||
|
func Since(t time.Time) time.Duration {
|
||||||
|
return BuiltInAdjuster().Since(t)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const (
|
||||||
|
Nanosecond = time.Nanosecond
|
||||||
|
Microsecond = time.Microsecond
|
||||||
|
Millisecond = time.Millisecond
|
||||||
|
Second = time.Second
|
||||||
|
Minute = time.Minute
|
||||||
|
Hour = time.Hour
|
||||||
|
Day = Hour * 24
|
||||||
|
Week = Day * 7
|
||||||
|
)
|
|
@ -0,0 +1,350 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var zero = time.Time{}
|
||||||
|
|
||||||
|
// IsMomentReached 检查指定时刻是否已到达且未发生过
|
||||||
|
// - now: 当前时间
|
||||||
|
// - last: 上次发生的时间
|
||||||
|
// - hour: 要检查的时刻的小时数
|
||||||
|
// - min: 要检查的时刻的分钟数
|
||||||
|
// - sec: 要检查的时刻的秒数
|
||||||
|
func IsMomentReached(now time.Time, last time.Time, hour, min, sec int) bool {
|
||||||
|
moment := time.Date(last.Year(), last.Month(), last.Day(), hour, min, sec, 0, time.Local)
|
||||||
|
if !moment.Before(now) {
|
||||||
|
return false
|
||||||
|
} else if moment.After(last) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果要检查的时刻在上次发生的时间和当前时间之间,并且已经过了一天,说明已经发生
|
||||||
|
nextDayMoment := moment.AddDate(0, 0, 1)
|
||||||
|
return !nextDayMoment.After(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextMoment 获取下一个指定时刻发生的时间。
|
||||||
|
func GetNextMoment(now time.Time, hour, min, sec int) time.Time {
|
||||||
|
moment := time.Date(now.Year(), now.Month(), now.Day(), hour, min, sec, 0, time.Local)
|
||||||
|
// 如果要检查的时刻已经过了,则返回明天的这个时刻
|
||||||
|
if moment.Before(now) {
|
||||||
|
moment = moment.AddDate(0, 0, 1)
|
||||||
|
}
|
||||||
|
return moment
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMomentInDays 检查指定时刻是否在给定的天数内发生。
|
||||||
|
// - now: 当前时间
|
||||||
|
// - hour: 要检查的时刻的小时数
|
||||||
|
// - min: 要检查的时刻的分钟数
|
||||||
|
// - sec: 要检查的时刻的秒数
|
||||||
|
// - days: 表示要偏移的天数。正数表示未来,负数表示过去,0 即今天
|
||||||
|
func IsMomentInDays(now time.Time, hour, min, sec, days int) bool {
|
||||||
|
offsetTime := now.AddDate(0, 0, days)
|
||||||
|
moment := time.Date(offsetTime.Year(), offsetTime.Month(), offsetTime.Day(), hour, min, sec, 0, time.Local)
|
||||||
|
return now.Before(moment.AddDate(0, 0, 1)) && now.After(moment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMomentYesterday 检查指定时刻是否在昨天发生
|
||||||
|
func IsMomentYesterday(now time.Time, hour, min, sec int) bool {
|
||||||
|
return IsMomentInDays(now, hour, min, sec, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMomentToday 检查指定时刻是否在今天发生
|
||||||
|
func IsMomentToday(now time.Time, hour, min, sec int) bool {
|
||||||
|
return IsMomentInDays(now, hour, min, sec, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMomentTomorrow 检查指定时刻是否在明天发生
|
||||||
|
func IsMomentTomorrow(now time.Time, hour, min, sec int) bool {
|
||||||
|
return IsMomentInDays(now, hour, min, sec, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMomentPassed 检查指定时刻是否已经过去
|
||||||
|
func IsMomentPassed(now time.Time, hour, min, sec int) bool {
|
||||||
|
// 构建要检查的时刻
|
||||||
|
moment := time.Date(now.Year(), now.Month(), now.Day(), hour, min, sec, 0, time.Local)
|
||||||
|
return now.After(moment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMomentFuture 检查指定时刻是否在未来
|
||||||
|
func IsMomentFuture(now time.Time, hour, min, sec int) bool {
|
||||||
|
// 构建要检查的时刻
|
||||||
|
moment := time.Date(now.Year(), now.Month(), now.Day(), hour, min, sec, 0, time.Local)
|
||||||
|
return now.Before(moment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStartOfDay 获取指定时间的当天第一刻,即 00:00:00
|
||||||
|
func GetStartOfDay(t time.Time) time.Time {
|
||||||
|
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEndOfDay 获取指定时间的当天最后一刻,即 23:59:59
|
||||||
|
func GetEndOfDay(t time.Time) time.Time {
|
||||||
|
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelativeStartOfDay 获取相对于指定时间减去或加上指定天数后的当天开始时间
|
||||||
|
// - offsetDays: 要偏移的天数,负数表示过去的某一天,正数表示未来的某一天
|
||||||
|
func GetRelativeStartOfDay(t time.Time, offsetDays int) time.Time {
|
||||||
|
return GetStartOfDay(GetStartOfDay(t.AddDate(0, 0, offsetDays)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelativeEndOfDay 获取相对于指定时间减去或加上指定天数后的当天结束时间
|
||||||
|
// - offsetDays: 要偏移的天数,负数表示过去的某一天,正数表示未来的某一天
|
||||||
|
func GetRelativeEndOfDay(t time.Time, offsetDays int) time.Time {
|
||||||
|
return GetEndOfDay(GetEndOfDay(t.AddDate(0, 0, offsetDays)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStartOfWeek 获取指定时间所在周的特定周的开始时刻,即 00:00:00
|
||||||
|
func GetStartOfWeek(t time.Time, weekday time.Weekday) time.Time {
|
||||||
|
t = GetStartOfDay(t)
|
||||||
|
tw := t.Weekday()
|
||||||
|
if tw == 0 {
|
||||||
|
tw = 7
|
||||||
|
}
|
||||||
|
d := 1 - int(tw)
|
||||||
|
switch weekday {
|
||||||
|
case time.Sunday:
|
||||||
|
d += 6
|
||||||
|
default:
|
||||||
|
d += int(weekday) - 1
|
||||||
|
}
|
||||||
|
return t.AddDate(0, 0, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEndOfWeek 获取指定时间所在周的特定周的最后时刻,即 23:59:59
|
||||||
|
func GetEndOfWeek(t time.Time, weekday time.Weekday) time.Time {
|
||||||
|
return GetEndOfDay(GetStartOfWeek(t, weekday))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelativeStartOfWeek 获取相对于当前时间的本周开始时间,以指定的星期作为一周的开始,并根据需要进行周数的偏移
|
||||||
|
// - now:当前时间
|
||||||
|
// - week:以哪一天作为一周的开始
|
||||||
|
// - offsetWeeks:要偏移的周数,正数表示向未来偏移,负数表示向过去偏移
|
||||||
|
//
|
||||||
|
// 该函数返回以指定星期作为一周的开始时间,然后根据偏移量进行周数偏移,得到相对于当前时间的周的开始时间
|
||||||
|
//
|
||||||
|
// 假设 week 为 time.Saturday 且 offsetWeeks 为 -1,则表示获取上周六的开始时间,一下情况中第一个时间为 now,第二个时间为函数返回值
|
||||||
|
// - 2024-03-01 00:00:00 --相对时间-> 2024-02-24 00:00:00 --偏移时间--> 2024-02-17 00:00:00
|
||||||
|
// - 2024-03-02 00:00:00 --相对时间-> 2024-02-24 00:00:00 --偏移时间--> 2024-02-17 00:00:00
|
||||||
|
// - 2024-03-03 00:00:00 --相对时间-> 2024-03-02 00:00:00 --偏移时间--> 2024-02-24 00:00:00
|
||||||
|
func GetRelativeStartOfWeek(now time.Time, week time.Weekday, offsetWeeks int) time.Time {
|
||||||
|
nowWeekday, weekday := int(now.Weekday()), int(week)
|
||||||
|
if nowWeekday == 0 {
|
||||||
|
nowWeekday = 7
|
||||||
|
}
|
||||||
|
if weekday == 0 {
|
||||||
|
weekday = 7
|
||||||
|
}
|
||||||
|
if nowWeekday < weekday {
|
||||||
|
now = now.Add(-Week)
|
||||||
|
}
|
||||||
|
moment := GetStartOfWeek(now, week)
|
||||||
|
return moment.Add(Week * time.Duration(offsetWeeks))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelativeEndOfWeek 获取相对于当前时间的本周结束时间,以指定的星期作为一周的开始,并根据需要进行周数的偏移
|
||||||
|
// - 该函数详细解释参考 GetRelativeEndOfWeek 函数,其中不同的是,该函数返回的是这一天最后一刻的时间,即 23:59:59
|
||||||
|
func GetRelativeEndOfWeek(now time.Time, week time.Weekday, offsetWeeks int) time.Time {
|
||||||
|
return GetEndOfDay(GetRelativeStartOfWeek(now, week, offsetWeeks))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelativeTimeOfWeek 获取相对于当前时间的本周指定星期的指定时刻,以指定的星期作为一周的开始,并根据需要进行周数的偏移
|
||||||
|
// - 该函数详细解释参考 GetRelativeStartOfWeek 函数,其中不同的是,该函数返回的是这一天对应 now 的时间
|
||||||
|
func GetRelativeTimeOfWeek(now time.Time, week time.Weekday, offsetWeeks int) time.Time {
|
||||||
|
moment := GetRelativeStartOfWeek(now, week, offsetWeeks)
|
||||||
|
return time.Date(moment.Year(), moment.Month(), moment.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond(), now.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero 获取一个零值的时间
|
||||||
|
func Zero() time.Time {
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero 检查一个时间是否为零值
|
||||||
|
func IsZero(t time.Time) bool {
|
||||||
|
return t.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max 获取两个时间中的最大值
|
||||||
|
func Max(t1, t2 time.Time) time.Time {
|
||||||
|
if t1.After(t2) {
|
||||||
|
return t1
|
||||||
|
}
|
||||||
|
return t2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min 获取两个时间中的最小值
|
||||||
|
func Min(t1, t2 time.Time) time.Time {
|
||||||
|
if t1.Before(t2) {
|
||||||
|
return t1
|
||||||
|
}
|
||||||
|
return t2
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmallerFirst 将两个时间按照从小到大的顺序排列
|
||||||
|
func SmallerFirst(t1, t2 time.Time) (time.Time, time.Time) {
|
||||||
|
if t1.Before(t2) {
|
||||||
|
return t1, t2
|
||||||
|
}
|
||||||
|
return t2, t1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmallerLast 将两个时间按照从大到小的顺序排列
|
||||||
|
func SmallerLast(t1, t2 time.Time) (time.Time, time.Time) {
|
||||||
|
if t1.Before(t2) {
|
||||||
|
return t2, t1
|
||||||
|
}
|
||||||
|
return t1, t2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delta 获取两个时间之间的时间差
|
||||||
|
func Delta(t1, t2 time.Time) time.Duration {
|
||||||
|
if t1.Before(t2) {
|
||||||
|
return t2.Sub(t1)
|
||||||
|
}
|
||||||
|
return t1.Sub(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloorDeltaDays 计算两个时间之间的天数差异,并向下取整
|
||||||
|
func FloorDeltaDays(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Day)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CeilDeltaDays 计算两个时间之间的天数差异,并向上取整
|
||||||
|
func CeilDeltaDays(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(math.Ceil(float64(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Day)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundDeltaDays 计算两个时间之间的天数差异,并四舍五入
|
||||||
|
func RoundDeltaDays(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(math.Round(float64(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Day)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloorDeltaHours 计算两个时间之间的小时数差异,并向下取整
|
||||||
|
func FloorDeltaHours(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CeilDeltaHours 计算两个时间之间的小时数差异,并向上取整
|
||||||
|
func CeilDeltaHours(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(math.Ceil(float64(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Hour)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundDeltaHours 计算两个时间之间的小时数差异,并四舍五入
|
||||||
|
func RoundDeltaHours(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(math.Round(float64(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Hour)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloorDeltaMinutes 计算两个时间之间的分钟数差异,并向下取整
|
||||||
|
func FloorDeltaMinutes(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CeilDeltaMinutes 计算两个时间之间的分钟数差异,并向上取整
|
||||||
|
func CeilDeltaMinutes(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(math.Ceil(float64(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Minute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundDeltaMinutes 计算两个时间之间的分钟数差异,并四舍五入
|
||||||
|
func RoundDeltaMinutes(t1, t2 time.Time) int {
|
||||||
|
t1, t2 = SmallerFirst(t1, t2)
|
||||||
|
return int(math.Round(float64(GetStartOfDay(t2).Sub(GetStartOfDay(t1)) / Minute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameSecond 检查两个时间是否在同一秒
|
||||||
|
func IsSameSecond(t1, t2 time.Time) bool {
|
||||||
|
return t1.Unix() == t2.Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameMinute 检查两个时间是否在同一分钟
|
||||||
|
func IsSameMinute(t1, t2 time.Time) bool {
|
||||||
|
return t1.Minute() == t2.Minute() && IsSameHour(t1, t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameHour 检查两个时间是否在同一小时
|
||||||
|
func IsSameHour(t1, t2 time.Time) bool {
|
||||||
|
return t1.Hour() == t2.Hour() && IsSameDay(t1, t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameDay 检查两个时间是否在同一天
|
||||||
|
func IsSameDay(t1, t2 time.Time) bool {
|
||||||
|
return GetStartOfDay(t1).Equal(GetStartOfDay(t2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameWeek 检查两个时间是否在同一周
|
||||||
|
func IsSameWeek(t1, t2 time.Time) bool {
|
||||||
|
return GetStartOfWeek(t1, time.Monday).Equal(GetStartOfWeek(t2, time.Monday))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameMonth 检查两个时间是否在同一月
|
||||||
|
func IsSameMonth(t1, t2 time.Time) bool {
|
||||||
|
return t1.Month() == t2.Month() && t1.Year() == t2.Year()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameYear 检查两个时间是否在同一年
|
||||||
|
func IsSameYear(t1, t2 time.Time) bool {
|
||||||
|
return t1.Year() == t2.Year()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMonthDays 获取指定时间所在月的天数
|
||||||
|
func GetMonthDays(t time.Time) int {
|
||||||
|
year, month, _ := t.Date()
|
||||||
|
if month != 2 {
|
||||||
|
if month == 4 || month == 6 || month == 9 || month == 11 {
|
||||||
|
return 30
|
||||||
|
}
|
||||||
|
return 31
|
||||||
|
}
|
||||||
|
if ((year%4 == 0) && (year%100 != 0)) || year%400 == 0 {
|
||||||
|
return 29
|
||||||
|
}
|
||||||
|
return 28
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDuration 将一个数值转换为 time.Duration 类型,当 unit 为空时,默认为纳秒单位
|
||||||
|
func ToDuration[V generic.Number](v V, unit ...time.Duration) time.Duration {
|
||||||
|
var u = Nanosecond
|
||||||
|
if len(unit) > 0 {
|
||||||
|
u = unit[0]
|
||||||
|
}
|
||||||
|
return time.Duration(v) * u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDurationSecond 将一个数值转换为秒的 time.Duration 类型
|
||||||
|
func ToDurationSecond[V generic.Number](v V) time.Duration {
|
||||||
|
return ToDuration(v, Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDurationMinute 将一个数值转换为分钟的 time.Duration 类型
|
||||||
|
func ToDurationMinute[V generic.Number](v V) time.Duration {
|
||||||
|
return ToDuration(v, Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDurationHour 将一个数值转换为小时的 time.Duration 类型
|
||||||
|
func ToDurationHour[V generic.Number](v V) time.Duration {
|
||||||
|
return ToDuration(v, Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDurationDay 将一个数值转换为天的 time.Duration 类型
|
||||||
|
func ToDurationDay[V generic.Number](v V) time.Duration {
|
||||||
|
return ToDuration(v, Day)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDurationWeek 将一个数值转换为周的 time.Duration 类型
|
||||||
|
func ToDurationWeek[V generic.Number](v V) time.Duration {
|
||||||
|
return ToDuration(v, Week)
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPeriod 创建一个时间段
|
||||||
|
// - 如果 start 比 end 晚,则会自动交换两个时间
|
||||||
|
func NewPeriod(start, end time.Time) Period {
|
||||||
|
if start.After(end) {
|
||||||
|
start, end = end, start
|
||||||
|
}
|
||||||
|
return Period{start, end}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWindow 创建一个特定长度的时间窗口
|
||||||
|
func NewPeriodWindow(t time.Time, size time.Duration) Period {
|
||||||
|
start := t.Truncate(size)
|
||||||
|
end := start.Add(size)
|
||||||
|
return Period{start, end}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWindowWeek 创建一周长度的时间窗口,从周一零点开始至周日 23:59:59 结束
|
||||||
|
func NewPeriodWindowWeek(t time.Time) Period {
|
||||||
|
var start = GetStartOfWeek(t, time.Monday)
|
||||||
|
end := start.Add(Week)
|
||||||
|
return Period{start, end}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithTimeArray 创建一个时间段
|
||||||
|
func NewPeriodWithTimeArray(times [2]time.Time) Period {
|
||||||
|
return NewPeriod(times[0], times[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithDayZero 创建一个时间段,从 t 开始,持续到 day 天后的 0 点
|
||||||
|
func NewPeriodWithDayZero(t time.Time, day int) Period {
|
||||||
|
return NewPeriod(t, GetStartOfDay(t.AddDate(0, 0, day)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithDay 创建一个时间段,从 t 开始,持续 day 天
|
||||||
|
func NewPeriodWithDay(t time.Time, day int) Period {
|
||||||
|
return NewPeriod(t, t.AddDate(0, 0, day))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithHour 创建一个时间段,从 t 开始,持续 hour 小时
|
||||||
|
func NewPeriodWithHour(t time.Time, hour int) Period {
|
||||||
|
return NewPeriod(t, t.Add(time.Duration(hour)*time.Hour))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithMinute 创建一个时间段,从 t 开始,持续 minute 分钟
|
||||||
|
func NewPeriodWithMinute(t time.Time, minute int) Period {
|
||||||
|
return NewPeriod(t, t.Add(time.Duration(minute)*time.Minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithSecond 创建一个时间段,从 t 开始,持续 second 秒
|
||||||
|
func NewPeriodWithSecond(t time.Time, second int) Period {
|
||||||
|
return NewPeriod(t, t.Add(time.Duration(second)*time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithMillisecond 创建一个时间段,从 t 开始,持续 millisecond 毫秒
|
||||||
|
func NewPeriodWithMillisecond(t time.Time, millisecond int) Period {
|
||||||
|
return NewPeriod(t, t.Add(time.Duration(millisecond)*time.Millisecond))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithMicrosecond 创建一个时间段,从 t 开始,持续 microsecond 微秒
|
||||||
|
func NewPeriodWithMicrosecond(t time.Time, microsecond int) Period {
|
||||||
|
return NewPeriod(t, t.Add(time.Duration(microsecond)*time.Microsecond))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriodWithNanosecond 创建一个时间段,从 t 开始,持续 nanosecond 纳秒
|
||||||
|
func NewPeriodWithNanosecond(t time.Time, nanosecond int) Period {
|
||||||
|
return NewPeriod(t, t.Add(time.Duration(nanosecond)*time.Nanosecond))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Period 表示一个时间段
|
||||||
|
type Period [2]time.Time
|
||||||
|
|
||||||
|
// Start 返回时间段的开始时间
|
||||||
|
func (p Period) Start() time.Time {
|
||||||
|
return p[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// End 返回时间段的结束时间
|
||||||
|
func (p Period) End() time.Time {
|
||||||
|
return p[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration 返回时间段的持续时间
|
||||||
|
func (p Period) Duration() time.Duration {
|
||||||
|
return p[1].Sub(p[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Days 返回时间段的持续天数
|
||||||
|
func (p Period) Days() int {
|
||||||
|
return int(p.Duration().Hours() / 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hours 返回时间段的持续小时数
|
||||||
|
func (p Period) Hours() int {
|
||||||
|
return int(p.Duration().Hours())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minutes 返回时间段的持续分钟数
|
||||||
|
func (p Period) Minutes() int {
|
||||||
|
return int(p.Duration().Minutes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seconds 返回时间段的持续秒数
|
||||||
|
func (p Period) Seconds() int {
|
||||||
|
return int(p.Duration().Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Milliseconds 返回时间段的持续毫秒数
|
||||||
|
func (p Period) Milliseconds() int {
|
||||||
|
return int(p.Duration().Milliseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Microseconds 返回时间段的持续微秒数
|
||||||
|
func (p Period) Microseconds() int {
|
||||||
|
return int(p.Duration().Microseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nanoseconds 返回时间段的持续纳秒数
|
||||||
|
func (p Period) Nanoseconds() int {
|
||||||
|
return int(p.Duration().Nanoseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero 判断时间段是否为零值
|
||||||
|
func (p Period) IsZero() bool {
|
||||||
|
return p[0].IsZero() && p[1].IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalid 判断时间段是否无效
|
||||||
|
func (p Period) IsInvalid() bool {
|
||||||
|
return p[0].IsZero() || p[1].IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBefore 判断时间段是否在指定时间之前
|
||||||
|
func (p Period) IsBefore(t time.Time) bool {
|
||||||
|
return p[1].Before(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAfter 判断时间段是否在指定时间之后
|
||||||
|
func (p Period) IsAfter(t time.Time) bool {
|
||||||
|
return p[0].After(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBetween 判断指定时间是否在时间段之间
|
||||||
|
func (p Period) IsBetween(t time.Time) bool {
|
||||||
|
return p[0].Before(t) && p[1].After(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOngoing 判断指定时间是否正在进行时
|
||||||
|
// - 如果时间段的开始时间在指定时间之前或者等于指定时间,且时间段的结束时间在指定时间之后,则返回 true
|
||||||
|
func (p Period) IsOngoing(t time.Time) bool {
|
||||||
|
return (p[0].Before(t) || p[0].Equal(t)) && p[1].After(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBetweenOrEqual 判断指定时间是否在时间段之间或者等于时间段的开始或结束时间
|
||||||
|
func (p Period) IsBetweenOrEqual(t time.Time) bool {
|
||||||
|
return p.IsBetween(t) || p[0].Equal(t) || p[1].Equal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBetweenOrEqualPeriod 判断指定时间是否在时间段之间或者等于时间段的开始或结束时间
|
||||||
|
func (p Period) IsBetweenOrEqualPeriod(t Period) bool {
|
||||||
|
return p.IsBetween(t[0]) || p.IsBetween(t[1]) || p[0].Equal(t[0]) || p[1].Equal(t[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOverlap 判断时间段是否与指定时间段重叠
|
||||||
|
func (p Period) IsOverlap(t Period) bool {
|
||||||
|
return p.IsBetweenOrEqualPeriod(t) || t.IsBetweenOrEqualPeriod(p)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package chrono_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/chrono"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewPeriodWindow(t *testing.T) {
|
||||||
|
cur := time.Now()
|
||||||
|
fmt.Println(cur)
|
||||||
|
window := chrono.NewPeriodWindow(cur, chrono.Day)
|
||||||
|
fmt.Println(window)
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/RussellLuo/timingwheel"
|
||||||
|
"github.com/gorhill/cronexpr"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultSchedulerTick = SchedulerPoolDefaultTick
|
||||||
|
DefaultSchedulerWheelSize = SchedulerPoolDefaultWheelSize
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SchedulerForever = -1 // 无限循环
|
||||||
|
SchedulerOnce = 1 // 一次
|
||||||
|
SchedulerInstantly = 0 // 立刻
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDefaultScheduler 创建一个默认的时间调度器
|
||||||
|
// - tick: DefaultSchedulerTick
|
||||||
|
// - wheelSize: DefaultSchedulerWheelSize
|
||||||
|
func NewDefaultScheduler() *Scheduler {
|
||||||
|
return NewScheduler(DefaultSchedulerTick, DefaultSchedulerWheelSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScheduler 创建一个并发安全的时间调度器
|
||||||
|
// - tick: 时间轮的刻度间隔。
|
||||||
|
// - wheelSize: 时间轮的大小。
|
||||||
|
func NewScheduler(tick time.Duration, wheelSize int64) *Scheduler {
|
||||||
|
return newScheduler(nil, tick, wheelSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newScheduler(pool *SchedulerPool, tick time.Duration, wheelSize int64) *Scheduler {
|
||||||
|
scheduler := &Scheduler{
|
||||||
|
pool: pool,
|
||||||
|
wheel: timingwheel.NewTimingWheel(tick, wheelSize),
|
||||||
|
tasks: make(map[string]*schedulerTask),
|
||||||
|
}
|
||||||
|
if pool != nil {
|
||||||
|
scheduler.generation = pool.getGeneration()
|
||||||
|
}
|
||||||
|
scheduler.wheel.Start()
|
||||||
|
return scheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scheduler 并发安全的时间调度器
|
||||||
|
type Scheduler struct {
|
||||||
|
pool *SchedulerPool // 时间调度器所属的池,当该值为 nil 时,该时间调度器不属于任何池
|
||||||
|
wheel *timingwheel.TimingWheel // 时间轮
|
||||||
|
tasks map[string]*schedulerTask // 所有任务
|
||||||
|
lock sync.RWMutex // 用于确保并发安全的锁
|
||||||
|
generation int64 // 时间调度器的代数
|
||||||
|
tick time.Duration // 时间周期
|
||||||
|
|
||||||
|
executor func(name string, caller func()) // 任务执行器
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExecutor 设置任务执行器
|
||||||
|
// - 如果该任务执行器来自于时间调度器对象池,那么默认情况下将会使用时间调度器对象池的任务执行器,主动设置将会覆盖默认的任务执行器
|
||||||
|
func (s *Scheduler) SetExecutor(executor func(name string, caller func())) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
s.executor = executor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release 释放时间调度器,时间调度器被释放后将不再可用,如果时间调度器属于某个池且池未满,则会重新加入到池中
|
||||||
|
// - 释放后所有已注册的任务将会被取消
|
||||||
|
func (s *Scheduler) Release() {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
for name, task := range s.tasks {
|
||||||
|
task.close()
|
||||||
|
delete(s.tasks, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.pool == nil || s.pool.getGeneration() != s.generation {
|
||||||
|
s.wheel.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.pool.lock.Lock()
|
||||||
|
if len(s.pool.schedulers) < s.pool.size {
|
||||||
|
s.pool.schedulers = append(s.pool.schedulers, s)
|
||||||
|
s.pool.lock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.pool.lock.Unlock()
|
||||||
|
s.wheel.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterTask 取消特定任务的执行计划的注册
|
||||||
|
// - 如果任务不存在,则不执行任何操作
|
||||||
|
func (s *Scheduler) UnregisterTask(name string) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
if task, exist := s.tasks[name]; exist {
|
||||||
|
task.close()
|
||||||
|
delete(s.tasks, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRegisteredTasks 获取所有未执行完成的任务名称
|
||||||
|
func (s *Scheduler) GetRegisteredTasks() []string {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
return collection.ConvertMapKeysToSlice(s.tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCronTask 通过 cron 表达式注册一个任务。
|
||||||
|
// - 当 cron 表达式错误时,将会返回错误信息
|
||||||
|
func (s *Scheduler) RegisterCronTask(name, expression string, function interface{}, args ...interface{}) error {
|
||||||
|
expr, err := cronexpr.Parse(expression)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.task(name, 0, 0, expr, 0, function, args...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterImmediateCronTask 与 RegisterCronE 相同,但是会立即执行一次
|
||||||
|
func (s *Scheduler) RegisterImmediateCronTask(name, expression string, function interface{}, args ...interface{}) error {
|
||||||
|
if err := s.RegisterCronTask(name, expression, function, args...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.call(name, function, args...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterAfterTask 注册一个在特定时间后执行一次的任务
|
||||||
|
func (s *Scheduler) RegisterAfterTask(name string, after time.Duration, function interface{}, args ...interface{}) {
|
||||||
|
s.task(name, after, s.pool.tick, nil, 1, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRepeatedTask 注册一个在特定时间后反复执行的任务
|
||||||
|
func (s *Scheduler) RegisterRepeatedTask(name string, after, interval time.Duration, times int, function interface{}, args ...interface{}) {
|
||||||
|
s.task(name, after, interval, nil, times, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterDayMomentTask 注册一个在每天特定时刻执行的任务
|
||||||
|
// - 其中 lastExecuted 为上次执行时间,adjust 为时间偏移量,hour、min、sec 为时、分、秒
|
||||||
|
// - 当上次执行时间被错过时,将会立即执行一次
|
||||||
|
func (s *Scheduler) RegisterDayMomentTask(name string, lastExecuted time.Time, offset time.Duration, hour, min, sec int, function interface{}, args ...interface{}) {
|
||||||
|
now := time.Now().Add(offset)
|
||||||
|
if IsMomentReached(now, lastExecuted, hour, min, sec) {
|
||||||
|
s.call(name, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
moment := GetNextMoment(now, hour, min, sec)
|
||||||
|
s.RegisterRepeatedTask(name, moment.Sub(now), time.Hour*24, SchedulerForever, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) task(name string, after, interval time.Duration, expr *cronexpr.Expression, times int, function interface{}, args ...interface{}) {
|
||||||
|
s.UnregisterTask(name)
|
||||||
|
|
||||||
|
if expr == nil {
|
||||||
|
if after < s.tick {
|
||||||
|
after = s.tick
|
||||||
|
}
|
||||||
|
if interval < s.tick {
|
||||||
|
interval = s.tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = make([]reflect.Value, len(args))
|
||||||
|
for i, v := range args {
|
||||||
|
values[i] = reflect.ValueOf(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
task := &schedulerTask{
|
||||||
|
name: name,
|
||||||
|
after: after,
|
||||||
|
interval: interval,
|
||||||
|
total: times,
|
||||||
|
function: reflect.ValueOf(function),
|
||||||
|
args: values,
|
||||||
|
scheduler: s,
|
||||||
|
expr: expr,
|
||||||
|
}
|
||||||
|
var executor func(name string, caller func())
|
||||||
|
if s.pool != nil {
|
||||||
|
executor = s.pool.getExecutor()
|
||||||
|
}
|
||||||
|
s.lock.Lock()
|
||||||
|
if s.executor != nil {
|
||||||
|
executor = s.pool.getExecutor()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tasks[name] = task
|
||||||
|
if executor != nil {
|
||||||
|
task.timer = s.wheel.ScheduleFunc(task, func() {
|
||||||
|
executor(task.Name(), task.caller)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
task.timer = s.wheel.ScheduleFunc(task, task.caller)
|
||||||
|
}
|
||||||
|
s.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) call(name string, function any, args ...any) {
|
||||||
|
var values = make([]reflect.Value, len(args))
|
||||||
|
for i, v := range args {
|
||||||
|
values[i] = reflect.ValueOf(v)
|
||||||
|
}
|
||||||
|
f := reflect.ValueOf(function)
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
if s.executor != nil {
|
||||||
|
s.executor(name, func() {
|
||||||
|
f.Call(values)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
f.Call(values)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const (
|
||||||
|
BuiltInSchedulerWheelSize = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
buildInSchedulerPool *SchedulerPool
|
||||||
|
builtInScheduler *Scheduler
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
buildInSchedulerPool = NewDefaultSchedulerPool()
|
||||||
|
builtInScheduler = NewScheduler(DefaultSchedulerTick, BuiltInSchedulerWheelSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuiltInSchedulerPool 获取内置的由 NewDefaultSchedulerPool 函数创建的时间调度器对象池
|
||||||
|
func BuiltInSchedulerPool() *SchedulerPool {
|
||||||
|
return buildInSchedulerPool
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuiltInScheduler 获取内置的由 NewScheduler(DefaultSchedulerTick, BuiltInSchedulerWheelSize) 创建的时间调度器
|
||||||
|
func BuiltInScheduler() *Scheduler {
|
||||||
|
return builtInScheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterTask 调用内置时间调度器 BuiltInScheduler 的 Scheduler.UnregisterTask 函数
|
||||||
|
func UnregisterTask(name string) {
|
||||||
|
BuiltInScheduler().UnregisterTask(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCronTask 调用内置时间调度器 BuiltInScheduler 的 Scheduler.RegisterCronTask 函数
|
||||||
|
func RegisterCronTask(name, expression string, function interface{}, args ...interface{}) error {
|
||||||
|
return BuiltInScheduler().RegisterCronTask(name, expression, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterImmediateCronTask 调用内置时间调度器 BuiltInScheduler 的 Scheduler.RegisterImmediateCronTask 函数
|
||||||
|
func RegisterImmediateCronTask(name, expression string, function interface{}, args ...interface{}) error {
|
||||||
|
return BuiltInScheduler().RegisterImmediateCronTask(name, expression, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterAfterTask 调用内置时间调度器 BuiltInScheduler 的 Scheduler.RegisterAfterTask 函数
|
||||||
|
func RegisterAfterTask(name string, after time.Duration, function interface{}, args ...interface{}) {
|
||||||
|
BuiltInScheduler().RegisterAfterTask(name, after, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRepeatedTask 调用内置时间调度器 BuiltInScheduler 的 Scheduler.RegisterRepeatedTask 函数
|
||||||
|
func RegisterRepeatedTask(name string, after, interval time.Duration, times int, function interface{}, args ...interface{}) {
|
||||||
|
BuiltInScheduler().RegisterRepeatedTask(name, after, interval, times, function, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterDayMomentTask 调用内置时间调度器 BuiltInScheduler 的 Scheduler.RegisterDayMomentTask 函数
|
||||||
|
func RegisterDayMomentTask(name string, lastExecuted time.Time, offset time.Duration, hour, min, sec int, function interface{}, args ...interface{}) {
|
||||||
|
BuiltInScheduler().RegisterDayMomentTask(name, lastExecuted, offset, hour, min, sec, function, args...)
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SchedulerPoolDefaultSize = 96
|
||||||
|
SchedulerPoolDefaultTick = time.Millisecond * 10
|
||||||
|
SchedulerPoolDefaultWheelSize = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDefaultSchedulerPool 创建一个默认参数的并发安全的时间调度器对象池
|
||||||
|
// - size: SchedulerPoolDefaultSize
|
||||||
|
// - tick: SchedulerPoolDefaultTick
|
||||||
|
// - wheelSize: SchedulerPoolDefaultWheelSize
|
||||||
|
func NewDefaultSchedulerPool() *SchedulerPool {
|
||||||
|
scheduler, err := NewSchedulerPool(SchedulerPoolDefaultSize, SchedulerPoolDefaultTick, SchedulerPoolDefaultWheelSize)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // 该错误不应该发生,用于在参数或实现变更后的提示
|
||||||
|
}
|
||||||
|
return scheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSchedulerPool 创建一个并发安全的时间调度器对象池
|
||||||
|
func NewSchedulerPool(size int, tick time.Duration, wheelSize int64) (*SchedulerPool, error) {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil, fmt.Errorf("scheduler pool size must greater than 0, got: %d", size)
|
||||||
|
}
|
||||||
|
if wheelSize <= 0 {
|
||||||
|
return nil, fmt.Errorf("scheduler pool wheelSize must greater than 0, got: %d", size)
|
||||||
|
}
|
||||||
|
return &SchedulerPool{
|
||||||
|
size: size,
|
||||||
|
tick: tick,
|
||||||
|
wheelSize: wheelSize,
|
||||||
|
generation: 1,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedulerPool 并发安全的时间调度器对象池
|
||||||
|
type SchedulerPool struct {
|
||||||
|
schedulers []*Scheduler // 池中维护的时间调度器
|
||||||
|
lock sync.RWMutex // 用于并发安全的锁
|
||||||
|
tick time.Duration // 时间周期
|
||||||
|
wheelSize int64 // 时间轮尺寸
|
||||||
|
size int // 池大小,控制了池中时间调度器的数量
|
||||||
|
generation int64 // 池的代数
|
||||||
|
executor func(name string, caller func()) // 任务执行器
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExecutor 设置该事件调度器对象池中整体的任务执行器
|
||||||
|
func (p *SchedulerPool) SetExecutor(executor func(name string, caller func())) {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
p.executor = executor
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize 改变时间调度器对象池的大小,当传入的大小小于或等于 0 时,将会返回错误,并且不会发生任何改变
|
||||||
|
// - 设置时间调度器对象池的大小可以在运行时动态调整,但是需要注意的是,调整后的大小不会影响已经产生的 Scheduler
|
||||||
|
// - 已经产生的 Scheduler 在被释放后将不会回到 SchedulerPool 中
|
||||||
|
func (p *SchedulerPool) SetSize(size int) error {
|
||||||
|
if size <= 0 {
|
||||||
|
return fmt.Errorf("scheduler pool size must greater than 0, got: %d", size)
|
||||||
|
}
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
p.size = size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取一个特定时间周期及时间轮尺寸的时间调度器,当池中存在可用的时间调度器时,将会直接返回,否则将会创建一个新的时间调度器
|
||||||
|
func (p *SchedulerPool) Get() *Scheduler {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
var scheduler *Scheduler
|
||||||
|
if len(p.schedulers) > 0 {
|
||||||
|
scheduler = p.schedulers[0]
|
||||||
|
p.schedulers = p.schedulers[1:]
|
||||||
|
return scheduler
|
||||||
|
}
|
||||||
|
return newScheduler(p, p.tick, p.wheelSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recycle 释放定时器池的资源并将其重置为全新的状态
|
||||||
|
// - 执行该函数后,已有的时间调度器将会被停止,且不会重新加入到池中,一切都是新的开始
|
||||||
|
func (p *SchedulerPool) Recycle() {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
for _, scheduler := range p.schedulers {
|
||||||
|
scheduler.wheel.Stop()
|
||||||
|
}
|
||||||
|
p.schedulers = nil
|
||||||
|
p.generation++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SchedulerPool) getGeneration() int64 {
|
||||||
|
p.lock.RLock()
|
||||||
|
defer p.lock.RUnlock()
|
||||||
|
return p.generation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SchedulerPool) getExecutor() func(name string, caller func()) {
|
||||||
|
p.lock.RLock()
|
||||||
|
defer p.lock.RUnlock()
|
||||||
|
return p.executor
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/RussellLuo/timingwheel"
|
||||||
|
"github.com/gorhill/cronexpr"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// schedulerTask 调度器
|
||||||
|
type schedulerTask struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
scheduler *Scheduler // 任务所属的调度器
|
||||||
|
timer *timingwheel.Timer // 任务执行定时器
|
||||||
|
name string // 任务名称
|
||||||
|
after time.Duration // 任务首次执行延迟
|
||||||
|
interval time.Duration // 任务执行间隔
|
||||||
|
function reflect.Value // 任务执行函数
|
||||||
|
args []reflect.Value // 任务执行参数
|
||||||
|
expr *cronexpr.Expression // 任务执行时间表达式
|
||||||
|
|
||||||
|
total int // 任务执行次数
|
||||||
|
trigger int // 任务已执行次数
|
||||||
|
kill bool // 任务是否已关闭
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name 获取任务名称
|
||||||
|
func (t *schedulerTask) Name() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next 获取任务下一次执行的时间
|
||||||
|
func (t *schedulerTask) Next(prev time.Time) time.Time {
|
||||||
|
t.lock.RLock()
|
||||||
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
if t.kill || (t.expr != nil && t.total > 0 && t.trigger > t.total) {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
if t.expr != nil {
|
||||||
|
next := t.expr.Next(prev)
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
if t.trigger == 0 {
|
||||||
|
t.trigger++
|
||||||
|
return prev.Add(t.after)
|
||||||
|
}
|
||||||
|
t.trigger++
|
||||||
|
return prev.Add(t.interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *schedulerTask) caller() {
|
||||||
|
t.lock.Lock()
|
||||||
|
|
||||||
|
if t.kill {
|
||||||
|
t.lock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.total > 0 && t.trigger > t.total {
|
||||||
|
t.lock.Unlock()
|
||||||
|
t.scheduler.UnregisterTask(t.name)
|
||||||
|
} else {
|
||||||
|
t.lock.Unlock()
|
||||||
|
}
|
||||||
|
t.function.Call(t.args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *schedulerTask) close() {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
if t.kill {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.kill = true
|
||||||
|
if t.total <= 0 || t.trigger < t.total {
|
||||||
|
t.timer.Stop()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package chrono_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/chrono"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterCronTask(t *testing.T) {
|
||||||
|
chrono.RegisterDayMomentTask("newday", time.Now().Add(time.Minute*-2), 0, 0, 0, 0, func() {
|
||||||
|
fmt.Println("newday")
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,261 @@
|
||||||
|
package chrono
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewStateLine 创建一个从左向右由早到晚的状态时间线
|
||||||
|
func NewStateLine[State generic.Basic](zero State) *StateLine[State] {
|
||||||
|
return &StateLine[State]{
|
||||||
|
states: []State{zero},
|
||||||
|
points: []time.Time{{}},
|
||||||
|
trigger: [][]func(){{}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateLine 表示一个状态时间线,它记录了一系列时间点及其对应的状态和触发器。
|
||||||
|
// 在时间线中,每个时间点都与一个状态和一组触发器相关联,可以通过时间点查找状态,并触发与之相关联的触发器。
|
||||||
|
type StateLine[State generic.Basic] struct {
|
||||||
|
states []State // 每个时间点对应的状态
|
||||||
|
points []time.Time // 每个时间点
|
||||||
|
trigger [][]func() // 每个时间点对应的触发器
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 根据状态顺序检查时间线是否合法
|
||||||
|
// - missingAllowed: 是否允许状态缺失,如果为 true,则状态可以不连续,如果为 false,则状态必须连续
|
||||||
|
//
|
||||||
|
// 状态不连续表示时间线中存在状态缺失,例如:
|
||||||
|
// - 状态为 [1, 2, 3, 4, 5] 的时间线,如果 missingAllowed 为 true,则状态为 [1, 3, 5] 也是合法的
|
||||||
|
// - 状态为 [1, 2, 3, 4, 5] 的时间线,如果 missingAllowed 为 false,则状态为 [1, 3, 5] 是不合法的
|
||||||
|
func (s *StateLine[State]) Check(missingAllowed bool, states ...State) bool {
|
||||||
|
var indexStored int
|
||||||
|
var indexInput int
|
||||||
|
|
||||||
|
for indexStored < len(s.states) && indexInput < len(states) {
|
||||||
|
if s.states[indexStored] == states[indexInput] {
|
||||||
|
indexStored++
|
||||||
|
indexInput++
|
||||||
|
} else if missingAllowed {
|
||||||
|
indexInput++
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果存储序列还有剩余, 而输入序列已经遍历完
|
||||||
|
if indexStored != len(s.states) && indexInput == len(states) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果输入序列还有剩余, 而存储序列已经遍历完
|
||||||
|
if indexStored == len(s.states) && indexInput != len(states) && !missingAllowed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMissingStates 获取缺失的状态
|
||||||
|
func (s *StateLine[State]) GetMissingStates(states ...State) []State {
|
||||||
|
var missing = make([]State, 0, len(states))
|
||||||
|
for _, state := range states {
|
||||||
|
if !collection.InComparableSlice(s.states, state) {
|
||||||
|
missing = append(missing, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missing
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasState 检查时间线中是否包含指定状态
|
||||||
|
func (s *StateLine[State]) HasState(state State) bool {
|
||||||
|
return collection.InComparableSlice(s.states, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String 获取时间线的字符串表示
|
||||||
|
func (s *StateLine[State]) String() string {
|
||||||
|
var parts []string
|
||||||
|
for i := 0; i < len(s.states); i++ {
|
||||||
|
parts = append(parts, fmt.Sprintf("[%v] %v", s.states[i], s.points[i]))
|
||||||
|
}
|
||||||
|
return strings.Join(parts, " > ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddState 添加一个状态到时间线中,状态不能与任一时间点重合,否则将被忽略
|
||||||
|
// - onTrigger: 该状态绑定的触发器,该触发器不会被主动执行,需要主动获取触发器执行
|
||||||
|
func (s *StateLine[State]) AddState(state State, t time.Time, onTrigger ...func()) *StateLine[State] {
|
||||||
|
if collection.InComparableSlice(s.states, state) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
// 将 t 按照从左到右由早到晚的顺序插入到 points 中
|
||||||
|
for i := 0; i < len(s.points); i++ {
|
||||||
|
if s.points[i].After(t) {
|
||||||
|
s.points = append(s.points[:i], append([]time.Time{t}, s.points[i:]...)...)
|
||||||
|
s.states = append(s.states[:i], append([]State{state}, s.states[i:]...)...)
|
||||||
|
s.trigger = append(s.trigger[:i], append([][]func(){onTrigger}, s.trigger[i:]...)...)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.points = append(s.points, t)
|
||||||
|
s.states = append(s.states, state)
|
||||||
|
s.trigger = append(s.trigger, onTrigger)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeByState 获取指定状态的时间点
|
||||||
|
func (s *StateLine[State]) GetTimeByState(state State) time.Time {
|
||||||
|
for i := 0; i < len(s.states); i++ {
|
||||||
|
if s.states[i] == state {
|
||||||
|
return s.points[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextTimeByState 获取指定状态的下一个时间点
|
||||||
|
func (s *StateLine[State]) GetNextTimeByState(state State) time.Time {
|
||||||
|
for i := 0; i < len(s.states); i++ {
|
||||||
|
if s.states[i] == state && i+1 < len(s.points) {
|
||||||
|
return s.points[i+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.points[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastState 获取最后一个状态
|
||||||
|
func (s *StateLine[State]) GetLastState() State {
|
||||||
|
return s.states[len(s.states)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrevTimeByState 获取指定状态的上一个时间点
|
||||||
|
func (s *StateLine[State]) GetPrevTimeByState(state State) time.Time {
|
||||||
|
for i := len(s.states) - 1; i >= 0; i-- {
|
||||||
|
if s.states[i] == state && i > 0 {
|
||||||
|
return s.points[i-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndexByState 获取指定状态的索引
|
||||||
|
func (s *StateLine[State]) GetIndexByState(state State) int {
|
||||||
|
for i := 0; i < len(s.states); i++ {
|
||||||
|
if s.states[i] == state {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateByTime 获取指定时间点的状态
|
||||||
|
func (s *StateLine[State]) GetStateByTime(t time.Time) State {
|
||||||
|
for i := len(s.points) - 1; i >= 0; i-- {
|
||||||
|
point := s.points[i]
|
||||||
|
if point.Before(t) || point.Equal(t) {
|
||||||
|
return s.states[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.states[len(s.points)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeByIndex 获取指定索引的时间点
|
||||||
|
func (s *StateLine[State]) GetTimeByIndex(index int) time.Time {
|
||||||
|
return s.points[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move 时间线整体移动
|
||||||
|
func (s *StateLine[State]) Move(d time.Duration) *StateLine[State] {
|
||||||
|
for i := 0; i < len(s.points); i++ {
|
||||||
|
s.points[i] = s.points[i].Add(d)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextStateTimeByIndex 获取指定索引的下一个时间点
|
||||||
|
func (s *StateLine[State]) GetNextStateTimeByIndex(index int) time.Time {
|
||||||
|
return s.points[index+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrevStateTimeByIndex 获取指定索引的上一个时间点
|
||||||
|
func (s *StateLine[State]) GetPrevStateTimeByIndex(index int) time.Time {
|
||||||
|
return s.points[index-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateIndexByTime 获取指定时间点的索引
|
||||||
|
func (s *StateLine[State]) GetStateIndexByTime(t time.Time) int {
|
||||||
|
for i := len(s.points) - 1; i >= 0; i-- {
|
||||||
|
var point = s.points[i]
|
||||||
|
if point.Before(t) || point.Equal(t) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateCount 获取状态数量
|
||||||
|
func (s *StateLine[State]) GetStateCount() int {
|
||||||
|
return len(s.states)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateByIndex 获取指定索引的状态
|
||||||
|
func (s *StateLine[State]) GetStateByIndex(index int) State {
|
||||||
|
return s.states[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTriggerByTime 获取指定时间点的触发器
|
||||||
|
func (s *StateLine[State]) GetTriggerByTime(t time.Time) []func() {
|
||||||
|
for i := len(s.points) - 1; i >= 0; i-- {
|
||||||
|
var point = s.points[i]
|
||||||
|
if point.Before(t) || point.Equal(t) {
|
||||||
|
return s.trigger[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTriggerByIndex 获取指定索引的触发器
|
||||||
|
func (s *StateLine[State]) GetTriggerByIndex(index int) []func() {
|
||||||
|
return s.trigger[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTriggerByState 获取指定状态的触发器
|
||||||
|
func (s *StateLine[State]) GetTriggerByState(state State) []func() {
|
||||||
|
for i := 0; i < len(s.states); i++ {
|
||||||
|
if s.states[i] == state {
|
||||||
|
return s.trigger[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTriggerToState 给指定状态添加触发器
|
||||||
|
func (s *StateLine[State]) AddTriggerToState(state State, onTrigger ...func()) *StateLine[State] {
|
||||||
|
for i := 0; i < len(s.states); i++ {
|
||||||
|
if s.states[i] == state {
|
||||||
|
s.trigger[i] = append(s.trigger[i], onTrigger...)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate 按照时间顺序遍历时间线
|
||||||
|
func (s *StateLine[State]) Iterate(handler func(index int, state State, t time.Time) bool) {
|
||||||
|
for i := 0; i < len(s.points); i++ {
|
||||||
|
if !handler(i, s.states[i], s.points[i]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IterateReverse 按照时间逆序遍历时间线
|
||||||
|
func (s *StateLine[State]) IterateReverse(handler func(index int, state State, t time.Time) bool) {
|
||||||
|
for i := len(s.points) - 1; i >= 0; i-- {
|
||||||
|
if !handler(i, s.states[i], s.points[i]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package chrono_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/chrono"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewStateLine(t *testing.T) {
|
||||||
|
sl := chrono.NewStateLine(0)
|
||||||
|
sl.AddState(1, time.Now())
|
||||||
|
sl.AddState(2, time.Now().Add(-chrono.Hour))
|
||||||
|
|
||||||
|
sl.Iterate(func(index int, state int, ts time.Time) bool {
|
||||||
|
t.Log(index, state, ts)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Log(sl.GetStateByTime(time.Now()))
|
||||||
|
}
|
Loading…
Reference in New Issue