diff --git a/README.md b/README.md index 2461466..3741fd5 100644 --- a/README.md +++ b/README.md @@ -77,47 +77,32 @@ Import the library: Simplest way: - nextTime := cronexpression.NextTime("0 0 29 2 *", time.Now()) + nextTime := cronexpression.Parse("0 0 29 2 *").Next(time.Now()) Assuming `time.Now()` is "2013-08-29 09:28:00", then `nextTime` will be "2016-02-29 00:00:00". -If you need to reuse many times the same cron expression in your code, it is more efficient -to create a `CronExpression` object once and keep a copy of it for reuse: +You can keep the returned Expression pointer around if you want to reuse it: - cronexpr := cronexpression.NewCronExpression("0 0 29 2 *") - nextTime := cronexpr.NextTime(time.Now()) + cronexpr := cronexpression.Parse("0 0 29 2 *") + nextTime := cronexpr.Next(time.Now()) ... + nextTime := cronexpr.Next(nextTime) -Use `nextTime.IsZero()` to find out whether a valid time was returned. For example, +Use `time.IsZero()` to find out whether a valid time was returned. For example, - cronexpression.NextTime("* * * * * 1980", time.Now()).IsZero() + cronexpression.Next("* * * * * 1980", time.Now()).IsZero() will return `true`, whereas - cronexpression.NextTime("* * * * * 2050", time.Now()).IsZero() + cronexpression.Next("* * * * * 2050", time.Now()).IsZero() will return `false` (as of 2013-08-29...) -API ---- +You may also query for `n` next time stamps: -#### func NextTime + cronexpression.Parse("0 0 29 2 *").NextN(time.Now(), 5) - func NextTime(cronLine string, fromTime time.Time) time.Time - -Given a time stamp `fromTime`, return the closest following time stamp which matches the cron expression string `cronLine`. The `time.Location` of the returned time stamp is the same as `fromTime`. - -#### func NextTimeN - - func NextTimeN(cronLine string, fromTime time.Time, n int) []time.Time - -Given a time stamp `fromTime`, return a slice of `n` closest following time stamps which match the cron expression string `cronLine`. The time stamps in the returned slice are in chronological ascending order. The `time.Location` of the returned time stamps is the same as `fromTime`. - -Example: - - cronexpression.NextTimeN("0 0 0 29 2 ? *", time.Now(), 5) - -will result in the following time stamps being returned (as of 2013-08-30): +which returns a slice of time.Time objects, containing the following time stamps (as of 2013-08-30): 2016-02-29 00:00:00 2020-02-29 00:00:00 @@ -125,21 +110,6 @@ will result in the following time stamps being returned (as of 2013-08-30): 2028-02-29 00:00:00 2032-02-29 00:00:00 -#### func NewCronExpression - - func NewCronExpression(cronLine string) *CronExpression - -Return a new `CronExpression` pointer which will interpret the cron expression string `cronLine`. - -#### func (*CronExpression) NextTime - - func (cronexpr *CronExpression) NextTime(fromTime time.Time) time.Time - -Given a time stamp `fromTime`, return the closest following time stamp which matches the cron expression `cronexpr`. The `time.Location` of the returned time stamp is the same as `fromTime`. - -#### func (*CronExpression) NextTimeN - - func (cronexpr *CronExpression) NextTimeN(fromTime time.Time, n int) []time.Time - -Given a time stamp `fromTime`, return a slice of `n` closest following time stamps which match the cron expression `cronexpr`. The time stamps in the returned slice are in chronological ascending order. The `time.Location` of the returned time stamps is the same as `fromTime`. - +API +--- + diff --git a/cronexpression.go b/cronexpression.go index 3433bdb..eaae8cd 100644 --- a/cronexpression.go +++ b/cronexpression.go @@ -23,9 +23,9 @@ import ( /******************************************************************************/ -// A CronExpression represents a specific cron time expression as defined on +// A Expression represents a specific cron time expression as defined on // Wikipedia: https://en.wikipedia.org/wiki/Cron#CRON_expression -type CronExpression struct { +type Expression struct { expression string secondList []int minuteList []int @@ -45,10 +45,22 @@ type CronExpression struct { /******************************************************************************/ -// NewCronExpression() returns a new CronExpression pointer. It expects +// Check whether the cron expression `cronLine` is valid. If not valid, the +// index in the `cronLine` string at which there is an error is returned. +func Check(cronLine string) int { + + // Split into fields + _ = regexp.MustCompile(`\s+`).Split(cronLine, -1) + + return -1 +} + +/******************************************************************************/ + +// Parse() returns a new Expression pointer. It expects // a well-formed cron expression. If a malformed cron expression is // supplied, the result is undefined. -func NewCronExpression(cronLine string) *CronExpression { +func Parse(cronLine string) *Expression { cronLineNormalized := cronNormalize(cronLine) // Split into fields @@ -76,7 +88,7 @@ func NewCronExpression(cronLine string) *CronExpression { } // Generic parser can be used for most fields - cronExpr := &CronExpression{ + cronExpr := &Expression{ expression: cronLine, secondList: genericFieldParse(cronFields[0], 0, 59), minuteList: genericFieldParse(cronFields[1], 0, 59), @@ -97,31 +109,10 @@ func NewCronExpression(cronLine string) *CronExpression { /******************************************************************************/ -// Given a time stamp `fromTime`, return the closest following time stamp -// which matches the cron expression string .. `cronLine`. The `time.Location` -// of the returned time stamp is the same as `fromTime`. -func NextTime(cronLine string, fromTime time.Time) time.Time { - cronexpr := NewCronExpression(cronLine) - return cronexpr.NextTime(fromTime) -} - -/******************************************************************************/ - -// Given a time stamp `fromTime`, return a slice of `n` closest following time -// stamps which match the cron expression string `cronLine`. The time stamps in -// the returned slice are in chronological ascending order. The `time.Location` -// of the returned time stamps is the same as `fromTime`. -func NextTimeN(cronLine string, fromTime time.Time, n int) []time.Time { - cronexpr := NewCronExpression(cronLine) - return cronexpr.NextTimeN(fromTime, n) -} - -/******************************************************************************/ - // Given a time stamp `fromTime`, return the closest following time stamp which // matches the cron expression `cronexpr`. The `time.Location` of the returned // time stamp is the same as `fromTime`. -func (cronexpr *CronExpression) NextTime(fromTime time.Time) time.Time { +func (cronexpr *Expression) Next(fromTime time.Time) time.Time { // Special case if fromTime.IsZero() { return fromTime @@ -207,12 +198,12 @@ func (cronexpr *CronExpression) NextTime(fromTime time.Time) time.Time { // stamps which match the cron expression `cronexpr`. The time stamps in the // returned slice are in chronological ascending order. The `time.Location` of // the returned time stamps is the same as `fromTime`. -func (cronexpr *CronExpression) NextTimeN(fromTime time.Time, n int) []time.Time { +func (cronexpr *Expression) NextN(fromTime time.Time, n int) []time.Time { if n <= 0 { - panic("CronExpression.NextTimeN(): invalid count") + panic("Expression.NextN(): invalid count") } nextTimes := make([]time.Time, 0) - fromTime = cronexpr.NextTime(fromTime) + fromTime = cronexpr.Next(fromTime) for { if fromTime.IsZero() { break @@ -229,7 +220,7 @@ func (cronexpr *CronExpression) NextTimeN(fromTime time.Time, n int) []time.Time /******************************************************************************/ -func (cronexpr *CronExpression) nextYear(t time.Time) time.Time { +func (cronexpr *Expression) nextYear(t time.Time) time.Time { // Find index at which item in list is greater or equal to // candidate year i := sort.SearchInts(cronexpr.yearList, t.Year()+1) @@ -262,7 +253,7 @@ func (cronexpr *CronExpression) nextYear(t time.Time) time.Time { /******************************************************************************/ -func (cronexpr *CronExpression) nextMonth(t time.Time) time.Time { +func (cronexpr *Expression) nextMonth(t time.Time) time.Time { // Find index at which item in list is greater or equal to // candidate month i := sort.SearchInts(cronexpr.monthList, int(t.Month())+1) @@ -296,7 +287,7 @@ func (cronexpr *CronExpression) nextMonth(t time.Time) time.Time { /******************************************************************************/ -func (cronexpr *CronExpression) calculateActualDaysOfMonth(year, month int) []int { +func (cronexpr *Expression) calculateActualDaysOfMonth(year, month int) []int { actualDaysOfMonthMap := make(map[int]bool) timeOrigin := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) lastDayOfMonth := timeOrigin.AddDate(0, 1, -1).Day() @@ -382,7 +373,7 @@ func (cronexpr *CronExpression) calculateActualDaysOfMonth(year, month int) []in /******************************************************************************/ -func (cronexpr *CronExpression) nextDayOfMonth(t time.Time) time.Time { +func (cronexpr *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(cronexpr.actualDaysOfMonthList, t.Day()+1) @@ -403,7 +394,7 @@ func (cronexpr *CronExpression) nextDayOfMonth(t time.Time) time.Time { /******************************************************************************/ -func (cronexpr *CronExpression) nextHour(t time.Time) time.Time { +func (cronexpr *Expression) nextHour(t time.Time) time.Time { // Find index at which item in list is greater or equal to // candidate hour i := sort.SearchInts(cronexpr.hourList, t.Hour()+1) @@ -424,7 +415,7 @@ func (cronexpr *CronExpression) nextHour(t time.Time) time.Time { /******************************************************************************/ -func (cronexpr *CronExpression) nextMinute(t time.Time) time.Time { +func (cronexpr *Expression) nextMinute(t time.Time) time.Time { // Find index at which item in list is greater or equal to // candidate minute i := sort.SearchInts(cronexpr.minuteList, t.Minute()+1) @@ -445,7 +436,7 @@ func (cronexpr *CronExpression) nextMinute(t time.Time) time.Time { /******************************************************************************/ -func (cronexpr *CronExpression) nextSecond(t time.Time) time.Time { +func (cronexpr *Expression) nextSecond(t time.Time) time.Time { // nextSecond() assumes all other fields are exactly matched // to the cron expression @@ -525,7 +516,7 @@ func cronNormalize(cronLine string) string { /******************************************************************************/ -func (cronexpr *CronExpression) dayofweekFieldParse(cronField string) error { +func (cronexpr *Expression) dayofweekFieldParse(cronField string) error { // Defaults cronexpr.daysOfWeekRestricted = true cronexpr.lastWeekDaysOfWeek = make(map[int]bool) @@ -585,7 +576,7 @@ func (cronexpr *CronExpression) dayofweekFieldParse(cronField string) error { /******************************************************************************/ -func (cronexpr *CronExpression) dayofmonthFieldParse(cronField string) error { +func (cronexpr *Expression) dayofmonthFieldParse(cronField string) error { // Defaults cronexpr.daysOfMonthRestricted = true cronexpr.lastDayOfMonth = false diff --git a/cronexpression_test.go b/cronexpression_test.go index ad803db..3bd76fe 100644 --- a/cronexpression_test.go +++ b/cronexpression_test.go @@ -109,14 +109,14 @@ var crontests = []crontest{ // TODO: more tests } -func TestCronExpressions(t *testing.T) { +func TestExpressions(t *testing.T) { for _, test := range crontests { for _, times := range test.times { from, _ := time.Parse("2006-01-02 15:04:05", times.from) - next := cronexpression.NextTime(test.expr, from) + next := cronexpression.Parse(test.expr).Next(from) nextstr := next.Format(test.layout) if nextstr != times.next { - t.Errorf("(\"%s\").NextTime(\"%s\") = \"%s\", got \"%s\"", test.expr, times.from, times.next, nextstr) + t.Errorf("(\"%s\").Next(\"%s\") = \"%s\", got \"%s\"", test.expr, times.from, times.next, nextstr) } } }