This commit is contained in:
gorhill 2013-08-31 08:06:59 -04:00
parent ae9c090812
commit 47d3d6e543
3 changed files with 47 additions and 86 deletions

View File

@ -77,47 +77,32 @@ Import the library:
Simplest way: 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". 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 You can keep the returned Expression pointer around if you want to reuse it:
to create a `CronExpression` object once and keep a copy of it for reuse:
cronexpr := cronexpression.NewCronExpression("0 0 29 2 *") cronexpr := cronexpression.Parse("0 0 29 2 *")
nextTime := cronexpr.NextTime(time.Now()) 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 will return `true`, whereas
cronexpression.NextTime("* * * * * 2050", time.Now()).IsZero() cronexpression.Next("* * * * * 2050", time.Now()).IsZero()
will return `false` (as of 2013-08-29...) 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 which returns a slice of time.Time objects, containing the following time stamps (as of 2013-08-30):
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):
2016-02-29 00:00:00 2016-02-29 00:00:00
2020-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 2028-02-29 00:00:00
2032-02-29 00:00:00 2032-02-29 00:00:00
#### func NewCronExpression API
---
func NewCronExpression(cronLine string) *CronExpression <http://godoc.org/github.com/gorhill/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`.

View File

@ -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 // Wikipedia: https://en.wikipedia.org/wiki/Cron#CRON_expression
type CronExpression struct { type Expression struct {
expression string expression string
secondList []int secondList []int
minuteList []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 // a well-formed cron expression. If a malformed cron expression is
// supplied, the result is undefined. // supplied, the result is undefined.
func NewCronExpression(cronLine string) *CronExpression { func Parse(cronLine string) *Expression {
cronLineNormalized := cronNormalize(cronLine) cronLineNormalized := cronNormalize(cronLine)
// Split into fields // Split into fields
@ -76,7 +88,7 @@ func NewCronExpression(cronLine string) *CronExpression {
} }
// Generic parser can be used for most fields // Generic parser can be used for most fields
cronExpr := &CronExpression{ cronExpr := &Expression{
expression: cronLine, expression: cronLine,
secondList: genericFieldParse(cronFields[0], 0, 59), secondList: genericFieldParse(cronFields[0], 0, 59),
minuteList: genericFieldParse(cronFields[1], 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 // Given a time stamp `fromTime`, return the closest following time stamp which
// matches the cron expression `cronexpr`. The `time.Location` of the returned // matches the cron expression `cronexpr`. The `time.Location` of the returned
// time stamp is the same as `fromTime`. // 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 // Special case
if fromTime.IsZero() { if fromTime.IsZero() {
return fromTime 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 // stamps which match the cron expression `cronexpr`. The time stamps in the
// returned slice are in chronological ascending order. The `time.Location` of // returned slice are in chronological ascending order. The `time.Location` of
// the returned time stamps is the same as `fromTime`. // 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 { if n <= 0 {
panic("CronExpression.NextTimeN(): invalid count") panic("Expression.NextN(): invalid count")
} }
nextTimes := make([]time.Time, 0) nextTimes := make([]time.Time, 0)
fromTime = cronexpr.NextTime(fromTime) fromTime = cronexpr.Next(fromTime)
for { for {
if fromTime.IsZero() { if fromTime.IsZero() {
break 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 // Find index at which item in list is greater or equal to
// candidate year // candidate year
i := sort.SearchInts(cronexpr.yearList, t.Year()+1) 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 // Find index at which item in list is greater or equal to
// candidate month // candidate month
i := sort.SearchInts(cronexpr.monthList, int(t.Month())+1) 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) actualDaysOfMonthMap := make(map[int]bool)
timeOrigin := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) timeOrigin := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)
lastDayOfMonth := timeOrigin.AddDate(0, 1, -1).Day() 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 // Find index at which item in list is greater or equal to
// candidate day of month // candidate day of month
i := sort.SearchInts(cronexpr.actualDaysOfMonthList, t.Day()+1) 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 // Find index at which item in list is greater or equal to
// candidate hour // candidate hour
i := sort.SearchInts(cronexpr.hourList, t.Hour()+1) 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 // Find index at which item in list is greater or equal to
// candidate minute // candidate minute
i := sort.SearchInts(cronexpr.minuteList, t.Minute()+1) 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 // nextSecond() assumes all other fields are exactly matched
// to the cron expression // 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 // Defaults
cronexpr.daysOfWeekRestricted = true cronexpr.daysOfWeekRestricted = true
cronexpr.lastWeekDaysOfWeek = make(map[int]bool) 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 // Defaults
cronexpr.daysOfMonthRestricted = true cronexpr.daysOfMonthRestricted = true
cronexpr.lastDayOfMonth = false cronexpr.lastDayOfMonth = false

View File

@ -109,14 +109,14 @@ var crontests = []crontest{
// TODO: more tests // TODO: more tests
} }
func TestCronExpressions(t *testing.T) { func TestExpressions(t *testing.T) {
for _, test := range crontests { for _, test := range crontests {
for _, times := range test.times { for _, times := range test.times {
from, _ := time.Parse("2006-01-02 15:04:05", times.from) 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) nextstr := next.Format(test.layout)
if nextstr != times.next { 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)
} }
} }
} }