Expose cron expression fields

This commit is contained in:
Bruno Luiz 2018-08-18 13:44:57 -03:00
parent 88b0669f7d
commit 02e4e6fd45
3 changed files with 120 additions and 120 deletions

View File

@ -26,22 +26,22 @@ import (
// A Expression represents a specific cron time expression as defined at
// <https://github.com/gorhill/cronexpr#implementation>
type Expression struct {
expression string
secondList []int
minuteList []int
hourList []int
daysOfMonth map[int]bool
workdaysOfMonth map[int]bool
lastDayOfMonth bool
lastWorkdayOfMonth bool
daysOfMonthRestricted bool
actualDaysOfMonthList []int
monthList []int
daysOfWeek map[int]bool
specificWeekDaysOfWeek map[int]bool
lastWeekDaysOfWeek map[int]bool
daysOfWeekRestricted bool
yearList []int
Expression string
SecondList []int
MinuteList []int
HourList []int
DaysOfMonth map[int]bool
WorkdaysOfMonth map[int]bool
LastDayOfMonth bool
LastWorkdayOfMonth bool
DaysOfMonthRestricted bool
ActualDaysOfMonthList []int
MonthList []int
DaysOfWeek map[int]bool
SpecificWeekDaysOfWeek map[int]bool
LastWeekDaysOfWeek map[int]bool
DaysOfWeekRestricted bool
YearList []int
}
/******************************************************************************/
@ -93,7 +93,7 @@ func Parse(cronLine string) (*Expression, error) {
}
field += 1
} else {
expr.secondList = []int{0}
expr.SecondList = []int{0}
}
// minute field
@ -138,7 +138,7 @@ func Parse(cronLine string) (*Expression, error) {
return nil, err
}
} else {
expr.yearList = yearDescriptor.defaultList
expr.YearList = yearDescriptor.defaultList
}
return &expr, nil
@ -172,59 +172,59 @@ func (expr *Expression) Next(fromTime time.Time) time.Time {
// year
v := fromTime.Year()
i := sort.SearchInts(expr.yearList, v)
if i == len(expr.yearList) {
i := sort.SearchInts(expr.YearList, v)
if i == len(expr.YearList) {
return time.Time{}
}
if v != expr.yearList[i] {
if v != expr.YearList[i] {
return expr.nextYear(fromTime)
}
// month
v = int(fromTime.Month())
i = sort.SearchInts(expr.monthList, v)
if i == len(expr.monthList) {
i = sort.SearchInts(expr.MonthList, v)
if i == len(expr.MonthList) {
return expr.nextYear(fromTime)
}
if v != expr.monthList[i] {
if v != expr.MonthList[i] {
return expr.nextMonth(fromTime)
}
expr.actualDaysOfMonthList = expr.calculateActualDaysOfMonth(fromTime.Year(), int(fromTime.Month()))
if len(expr.actualDaysOfMonthList) == 0 {
expr.ActualDaysOfMonthList = expr.calculateActualDaysOfMonth(fromTime.Year(), int(fromTime.Month()))
if len(expr.ActualDaysOfMonthList) == 0 {
return expr.nextMonth(fromTime)
}
// day of month
v = fromTime.Day()
i = sort.SearchInts(expr.actualDaysOfMonthList, v)
if i == len(expr.actualDaysOfMonthList) {
i = sort.SearchInts(expr.ActualDaysOfMonthList, v)
if i == len(expr.ActualDaysOfMonthList) {
return expr.nextMonth(fromTime)
}
if v != expr.actualDaysOfMonthList[i] {
if v != expr.ActualDaysOfMonthList[i] {
return expr.nextDayOfMonth(fromTime)
}
// hour
v = fromTime.Hour()
i = sort.SearchInts(expr.hourList, v)
if i == len(expr.hourList) {
i = sort.SearchInts(expr.HourList, v)
if i == len(expr.HourList) {
return expr.nextDayOfMonth(fromTime)
}
if v != expr.hourList[i] {
if v != expr.HourList[i] {
return expr.nextHour(fromTime)
}
// minute
v = fromTime.Minute()
i = sort.SearchInts(expr.minuteList, v)
if i == len(expr.minuteList) {
i = sort.SearchInts(expr.MinuteList, v)
if i == len(expr.MinuteList) {
return expr.nextHour(fromTime)
}
if v != expr.minuteList[i] {
if v != expr.MinuteList[i] {
return expr.nextMinute(fromTime)
}
// second
v = fromTime.Second()
i = sort.SearchInts(expr.secondList, v)
if i == len(expr.secondList) {
i = sort.SearchInts(expr.SecondList, v)
if i == len(expr.SecondList) {
return expr.nextMinute(fromTime)
}

View File

@ -36,30 +36,30 @@ var dowNormalizedOffsets = [][]int{
func (expr *Expression) nextYear(t time.Time) time.Time {
// Find index at which item in list is greater or equal to
// candidate year
i := sort.SearchInts(expr.yearList, t.Year()+1)
if i == len(expr.yearList) {
i := sort.SearchInts(expr.YearList, t.Year()+1)
if i == len(expr.YearList) {
return time.Time{}
}
// Year changed, need to recalculate actual days of month
expr.actualDaysOfMonthList = expr.calculateActualDaysOfMonth(expr.yearList[i], expr.monthList[0])
if len(expr.actualDaysOfMonthList) == 0 {
expr.ActualDaysOfMonthList = expr.calculateActualDaysOfMonth(expr.YearList[i], expr.MonthList[0])
if len(expr.ActualDaysOfMonthList) == 0 {
return expr.nextMonth(time.Date(
expr.yearList[i],
time.Month(expr.monthList[0]),
expr.YearList[i],
time.Month(expr.MonthList[0]),
1,
expr.hourList[0],
expr.minuteList[0],
expr.secondList[0],
expr.HourList[0],
expr.MinuteList[0],
expr.SecondList[0],
0,
t.Location()))
}
return time.Date(
expr.yearList[i],
time.Month(expr.monthList[0]),
expr.actualDaysOfMonthList[0],
expr.hourList[0],
expr.minuteList[0],
expr.secondList[0],
expr.YearList[i],
time.Month(expr.MonthList[0]),
expr.ActualDaysOfMonthList[0],
expr.HourList[0],
expr.MinuteList[0],
expr.SecondList[0],
0,
t.Location())
}
@ -69,31 +69,31 @@ func (expr *Expression) nextYear(t time.Time) time.Time {
func (expr *Expression) nextMonth(t time.Time) time.Time {
// Find index at which item in list is greater or equal to
// candidate month
i := sort.SearchInts(expr.monthList, int(t.Month())+1)
if i == len(expr.monthList) {
i := sort.SearchInts(expr.MonthList, int(t.Month())+1)
if i == len(expr.MonthList) {
return expr.nextYear(t)
}
// Month changed, need to recalculate actual days of month
expr.actualDaysOfMonthList = expr.calculateActualDaysOfMonth(t.Year(), expr.monthList[i])
if len(expr.actualDaysOfMonthList) == 0 {
expr.ActualDaysOfMonthList = expr.calculateActualDaysOfMonth(t.Year(), expr.MonthList[i])
if len(expr.ActualDaysOfMonthList) == 0 {
return expr.nextMonth(time.Date(
t.Year(),
time.Month(expr.monthList[i]),
time.Month(expr.MonthList[i]),
1,
expr.hourList[0],
expr.minuteList[0],
expr.secondList[0],
expr.HourList[0],
expr.MinuteList[0],
expr.SecondList[0],
0,
t.Location()))
}
return time.Date(
t.Year(),
time.Month(expr.monthList[i]),
expr.actualDaysOfMonthList[0],
expr.hourList[0],
expr.minuteList[0],
expr.secondList[0],
time.Month(expr.MonthList[i]),
expr.ActualDaysOfMonthList[0],
expr.HourList[0],
expr.MinuteList[0],
expr.SecondList[0],
0,
t.Location())
}
@ -103,18 +103,18 @@ func (expr *Expression) nextMonth(t time.Time) time.Time {
func (expr *Expression) nextDayOfMonth(t time.Time) time.Time {
// Find index at which item in list is greater or equal to
// candidate day of month
i := sort.SearchInts(expr.actualDaysOfMonthList, t.Day()+1)
if i == len(expr.actualDaysOfMonthList) {
i := sort.SearchInts(expr.ActualDaysOfMonthList, t.Day()+1)
if i == len(expr.ActualDaysOfMonthList) {
return expr.nextMonth(t)
}
return time.Date(
t.Year(),
t.Month(),
expr.actualDaysOfMonthList[i],
expr.hourList[0],
expr.minuteList[0],
expr.secondList[0],
expr.ActualDaysOfMonthList[i],
expr.HourList[0],
expr.MinuteList[0],
expr.SecondList[0],
0,
t.Location())
}
@ -124,8 +124,8 @@ func (expr *Expression) nextDayOfMonth(t time.Time) time.Time {
func (expr *Expression) nextHour(t time.Time) time.Time {
// Find index at which item in list is greater or equal to
// candidate hour
i := sort.SearchInts(expr.hourList, t.Hour()+1)
if i == len(expr.hourList) {
i := sort.SearchInts(expr.HourList, t.Hour()+1)
if i == len(expr.HourList) {
return expr.nextDayOfMonth(t)
}
@ -133,9 +133,9 @@ func (expr *Expression) nextHour(t time.Time) time.Time {
t.Year(),
t.Month(),
t.Day(),
expr.hourList[i],
expr.minuteList[0],
expr.secondList[0],
expr.HourList[i],
expr.MinuteList[0],
expr.SecondList[0],
0,
t.Location())
}
@ -145,8 +145,8 @@ func (expr *Expression) nextHour(t time.Time) time.Time {
func (expr *Expression) nextMinute(t time.Time) time.Time {
// Find index at which item in list is greater or equal to
// candidate minute
i := sort.SearchInts(expr.minuteList, t.Minute()+1)
if i == len(expr.minuteList) {
i := sort.SearchInts(expr.MinuteList, t.Minute()+1)
if i == len(expr.MinuteList) {
return expr.nextHour(t)
}
@ -155,8 +155,8 @@ func (expr *Expression) nextMinute(t time.Time) time.Time {
t.Month(),
t.Day(),
t.Hour(),
expr.minuteList[i],
expr.secondList[0],
expr.MinuteList[i],
expr.SecondList[0],
0,
t.Location())
}
@ -169,8 +169,8 @@ func (expr *Expression) nextSecond(t time.Time) time.Time {
// Find index at which item in list is greater or equal to
// candidate second
i := sort.SearchInts(expr.secondList, t.Second()+1)
if i == len(expr.secondList) {
i := sort.SearchInts(expr.SecondList, t.Second()+1)
if i == len(expr.SecondList) {
return expr.nextMinute(t)
}
@ -180,7 +180,7 @@ func (expr *Expression) nextSecond(t time.Time) time.Time {
t.Day(),
t.Hour(),
t.Minute(),
expr.secondList[i],
expr.SecondList[i],
0,
t.Location())
}
@ -199,22 +199,22 @@ func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
// "either field matches the current time"
// If both fields are not restricted, all days of the month are a hit
if expr.daysOfMonthRestricted == false && expr.daysOfWeekRestricted == false {
if expr.DaysOfMonthRestricted == false && expr.DaysOfWeekRestricted == false {
return genericDefaultList[1 : lastDayOfMonth.Day()+1]
}
// day-of-month != `*`
if expr.daysOfMonthRestricted {
if expr.DaysOfMonthRestricted {
// Last day of month
if expr.lastDayOfMonth {
if expr.LastDayOfMonth {
actualDaysOfMonthMap[lastDayOfMonth.Day()] = true
}
// Last work day of month
if expr.lastWorkdayOfMonth {
if expr.LastWorkdayOfMonth {
actualDaysOfMonthMap[workdayOfMonth(lastDayOfMonth, lastDayOfMonth)] = true
}
// Days of month
for v := range expr.daysOfMonth {
for v := range expr.DaysOfMonth {
// Ignore days beyond end of month
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[v] = true
@ -222,7 +222,7 @@ func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
}
// Work days of month
// As per Wikipedia: month boundaries are not crossed.
for v := range expr.workdaysOfMonth {
for v := range expr.WorkdaysOfMonth {
// Ignore days beyond end of month
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[workdayOfMonth(firstDayOfMonth.AddDate(0, 0, v-1), lastDayOfMonth)] = true
@ -231,13 +231,13 @@ func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
}
// day-of-week != `*`
if expr.daysOfWeekRestricted {
if expr.DaysOfWeekRestricted {
// How far first sunday is from first day of month
offset := 7 - int(firstDayOfMonth.Weekday())
// days of week
// offset : (7 - day_of_week_of_1st_day_of_month)
// target : 1 + (7 * week_of_month) + (offset + day_of_week) % 7
for v := range expr.daysOfWeek {
for v := range expr.DaysOfWeek {
w := dowNormalizedOffsets[(offset+v)%7]
actualDaysOfMonthMap[w[0]] = true
actualDaysOfMonthMap[w[1]] = true
@ -250,7 +250,7 @@ func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
// days of week of specific week in the month
// offset : (7 - day_of_week_of_1st_day_of_month)
// target : 1 + (7 * week_of_month) + (offset + day_of_week) % 7
for v := range expr.specificWeekDaysOfWeek {
for v := range expr.SpecificWeekDaysOfWeek {
v = 1 + 7*(v/7) + (offset+v)%7
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[v] = true
@ -259,7 +259,7 @@ func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
// Last days of week of the month
lastWeekOrigin := firstDayOfMonth.AddDate(0, 1, -7)
offset = 7 - int(lastWeekOrigin.Weekday())
for v := range expr.lastWeekDaysOfWeek {
for v := range expr.LastWeekDaysOfWeek {
v = lastWeekOrigin.Day() + (offset+v)%7
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[v] = true

View File

@ -212,7 +212,7 @@ var cronNormalizer = strings.NewReplacer(
func (expr *Expression) secondFieldHandler(s string) error {
var err error
expr.secondList, err = genericFieldHandler(s, secondDescriptor)
expr.SecondList, err = genericFieldHandler(s, secondDescriptor)
return err
}
@ -220,7 +220,7 @@ func (expr *Expression) secondFieldHandler(s string) error {
func (expr *Expression) minuteFieldHandler(s string) error {
var err error
expr.minuteList, err = genericFieldHandler(s, minuteDescriptor)
expr.MinuteList, err = genericFieldHandler(s, minuteDescriptor)
return err
}
@ -228,7 +228,7 @@ func (expr *Expression) minuteFieldHandler(s string) error {
func (expr *Expression) hourFieldHandler(s string) error {
var err error
expr.hourList, err = genericFieldHandler(s, hourDescriptor)
expr.HourList, err = genericFieldHandler(s, hourDescriptor)
return err
}
@ -236,7 +236,7 @@ func (expr *Expression) hourFieldHandler(s string) error {
func (expr *Expression) monthFieldHandler(s string) error {
var err error
expr.monthList, err = genericFieldHandler(s, monthDescriptor)
expr.MonthList, err = genericFieldHandler(s, monthDescriptor)
return err
}
@ -244,7 +244,7 @@ func (expr *Expression) monthFieldHandler(s string) error {
func (expr *Expression) yearFieldHandler(s string) error {
var err error
expr.yearList, err = genericFieldHandler(s, yearDescriptor)
expr.YearList, err = genericFieldHandler(s, yearDescriptor)
return err
}
@ -288,10 +288,10 @@ func genericFieldHandler(s string, desc fieldDescriptor) ([]int, error) {
}
func (expr *Expression) dowFieldHandler(s string) error {
expr.daysOfWeekRestricted = true
expr.daysOfWeek = make(map[int]bool)
expr.lastWeekDaysOfWeek = make(map[int]bool)
expr.specificWeekDaysOfWeek = make(map[int]bool)
expr.DaysOfWeekRestricted = true
expr.DaysOfWeek = make(map[int]bool)
expr.LastWeekDaysOfWeek = make(map[int]bool)
expr.SpecificWeekDaysOfWeek = make(map[int]bool)
directives, err := genericFieldParse(s, dowDescriptor)
if err != nil {
@ -306,34 +306,34 @@ func (expr *Expression) dowFieldHandler(s string) error {
// `5L`
pairs := makeLayoutRegexp(layoutDowOfLastWeek, dowDescriptor.valuePattern).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 {
populateOne(expr.lastWeekDaysOfWeek, dowDescriptor.atoi(snormal[pairs[2]:pairs[3]]))
populateOne(expr.LastWeekDaysOfWeek, dowDescriptor.atoi(snormal[pairs[2]:pairs[3]]))
} else {
// `5#3`
pairs := makeLayoutRegexp(layoutDowOfSpecificWeek, dowDescriptor.valuePattern).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 {
populateOne(expr.specificWeekDaysOfWeek, (dowDescriptor.atoi(snormal[pairs[4]:pairs[5]])-1)*7+(dowDescriptor.atoi(snormal[pairs[2]:pairs[3]])%7))
populateOne(expr.SpecificWeekDaysOfWeek, (dowDescriptor.atoi(snormal[pairs[4]:pairs[5]])-1)*7+(dowDescriptor.atoi(snormal[pairs[2]:pairs[3]])%7))
} else {
return fmt.Errorf("syntax error in day-of-week field: '%s'", sdirective)
}
}
case one:
populateOne(expr.daysOfWeek, directive.first)
populateOne(expr.DaysOfWeek, directive.first)
case span:
populateMany(expr.daysOfWeek, directive.first, directive.last, directive.step)
populateMany(expr.DaysOfWeek, directive.first, directive.last, directive.step)
case all:
populateMany(expr.daysOfWeek, directive.first, directive.last, directive.step)
expr.daysOfWeekRestricted = false
populateMany(expr.DaysOfWeek, directive.first, directive.last, directive.step)
expr.DaysOfWeekRestricted = false
}
}
return nil
}
func (expr *Expression) domFieldHandler(s string) error {
expr.daysOfMonthRestricted = true
expr.lastDayOfMonth = false
expr.lastWorkdayOfMonth = false
expr.daysOfMonth = make(map[int]bool) // days of month map
expr.workdaysOfMonth = make(map[int]bool) // work days of month map
expr.DaysOfMonthRestricted = true
expr.LastDayOfMonth = false
expr.LastWorkdayOfMonth = false
expr.DaysOfMonth = make(map[int]bool) // days of month map
expr.WorkdaysOfMonth = make(map[int]bool) // work days of month map
directives, err := genericFieldParse(s, domDescriptor)
if err != nil {
@ -347,28 +347,28 @@ func (expr *Expression) domFieldHandler(s string) error {
snormal := strings.ToLower(sdirective)
// `L`
if makeLayoutRegexp(layoutLastDom, domDescriptor.valuePattern).MatchString(snormal) {
expr.lastDayOfMonth = true
expr.LastDayOfMonth = true
} else {
// `LW`
if makeLayoutRegexp(layoutLastWorkdom, domDescriptor.valuePattern).MatchString(snormal) {
expr.lastWorkdayOfMonth = true
expr.LastWorkdayOfMonth = true
} else {
// `15W`
pairs := makeLayoutRegexp(layoutWorkdom, domDescriptor.valuePattern).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 {
populateOne(expr.workdaysOfMonth, domDescriptor.atoi(snormal[pairs[2]:pairs[3]]))
populateOne(expr.WorkdaysOfMonth, domDescriptor.atoi(snormal[pairs[2]:pairs[3]]))
} else {
return fmt.Errorf("syntax error in day-of-month field: '%s'", sdirective)
}
}
}
case one:
populateOne(expr.daysOfMonth, directive.first)
populateOne(expr.DaysOfMonth, directive.first)
case span:
populateMany(expr.daysOfMonth, directive.first, directive.last, directive.step)
populateMany(expr.DaysOfMonth, directive.first, directive.last, directive.step)
case all:
populateMany(expr.daysOfMonth, directive.first, directive.last, directive.step)
expr.daysOfMonthRestricted = false
populateMany(expr.DaysOfMonth, directive.first, directive.last, directive.step)
expr.DaysOfMonthRestricted = false
}
}
return nil