Add previous commands
This commit is contained in:
		
							parent
							
								
									d520615e53
								
							
						
					
					
						commit
						7d34e02fab
					
				
							
								
								
									
										116
									
								
								cronexpr.go
								
								
								
								
							
							
						
						
									
										116
									
								
								cronexpr.go
								
								
								
								
							| 
						 | 
				
			
			@ -201,13 +201,13 @@ func (expr *Expression) Next(fromTime time.Time) time.Time {
 | 
			
		|||
		return expr.nextMonth(fromTime)
 | 
			
		||||
	}
 | 
			
		||||
	if v != expr.actualDaysOfMonthList[i] {
 | 
			
		||||
		return expr.nextDayOfMonth(fromTime)
 | 
			
		||||
		return expr.nextActualDayOfMonth(fromTime)
 | 
			
		||||
	}
 | 
			
		||||
	// hour
 | 
			
		||||
	v = fromTime.Hour()
 | 
			
		||||
	i = sort.SearchInts(expr.hourList, v)
 | 
			
		||||
	if i == len(expr.hourList) {
 | 
			
		||||
		return expr.nextDayOfMonth(fromTime)
 | 
			
		||||
		return expr.nextActualDayOfMonth(fromTime)
 | 
			
		||||
	}
 | 
			
		||||
	if v != expr.hourList[i] {
 | 
			
		||||
		return expr.nextHour(fromTime)
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +236,87 @@ func (expr *Expression) Next(fromTime time.Time) time.Time {
 | 
			
		|||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
// Last returns the closest time instant immediately before `fromTime` which
 | 
			
		||||
// matches the cron expression `expr`.
 | 
			
		||||
//
 | 
			
		||||
// The `time.Location` of the returned time instant is the same as that of
 | 
			
		||||
// `fromTime`.
 | 
			
		||||
//
 | 
			
		||||
// The zero value of time.Time is returned if no matching time instant exists
 | 
			
		||||
// or if a `fromTime` is itself a zero value.
 | 
			
		||||
func (expr *Expression) Last(fromTime time.Time) time.Time {
 | 
			
		||||
	// Special case
 | 
			
		||||
	if fromTime.IsZero() {
 | 
			
		||||
		return fromTime
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// year
 | 
			
		||||
	v := fromTime.Year()
 | 
			
		||||
	i := sort.SearchInts(expr.yearList, v)
 | 
			
		||||
	if i == 0 && v != expr.yearList[i] {
 | 
			
		||||
		return time.Time{}
 | 
			
		||||
	}
 | 
			
		||||
	if i == len(expr.yearList) || v != expr.yearList[i] {
 | 
			
		||||
		return expr.lastYear(fromTime, false)
 | 
			
		||||
	}
 | 
			
		||||
	// month
 | 
			
		||||
	v = int(fromTime.Month())
 | 
			
		||||
	i = sort.SearchInts(expr.monthList, v)
 | 
			
		||||
	if i == 0 && v != expr.monthList[i] {
 | 
			
		||||
		return expr.lastYear(fromTime, true)
 | 
			
		||||
	}
 | 
			
		||||
	if i == len(expr.monthList) || v != expr.monthList[i] {
 | 
			
		||||
		return expr.lastMonth(fromTime, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expr.actualDaysOfMonthList = expr.calculateActualDaysOfMonth(fromTime.Year(), int(fromTime.Month()))
 | 
			
		||||
	if len(expr.actualDaysOfMonthList) == 0 {
 | 
			
		||||
		return expr.lastMonth(fromTime, true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// day of month
 | 
			
		||||
	v = fromTime.Day()
 | 
			
		||||
	i = sort.SearchInts(expr.actualDaysOfMonthList, v)
 | 
			
		||||
	if i == 0 && v != expr.actualDaysOfMonthList[i] {
 | 
			
		||||
		return expr.lastMonth(fromTime, true)
 | 
			
		||||
	}
 | 
			
		||||
	if i == len(expr.actualDaysOfMonthList) || v != expr.actualDaysOfMonthList[i] {
 | 
			
		||||
		return expr.lastActualDayOfMonth(fromTime, false)
 | 
			
		||||
	}
 | 
			
		||||
	// hour
 | 
			
		||||
	v = fromTime.Hour()
 | 
			
		||||
	i = sort.SearchInts(expr.hourList, v)
 | 
			
		||||
	if i == 0 && v != expr.hourList[i] {
 | 
			
		||||
		return expr.lastActualDayOfMonth(fromTime, true)
 | 
			
		||||
	}
 | 
			
		||||
	if i == len(expr.hourList) || v != expr.hourList[i] {
 | 
			
		||||
		return expr.lastHour(fromTime, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// minute
 | 
			
		||||
	v = fromTime.Minute()
 | 
			
		||||
	i = sort.SearchInts(expr.minuteList, v)
 | 
			
		||||
	if i == 0 && v != expr.minuteList[i] {
 | 
			
		||||
		return expr.lastHour(fromTime, true)
 | 
			
		||||
	}
 | 
			
		||||
	if i == len(expr.minuteList) || v != expr.minuteList[i] {
 | 
			
		||||
		return expr.lastMinute(fromTime, false)
 | 
			
		||||
	}
 | 
			
		||||
	// second
 | 
			
		||||
	v = fromTime.Second()
 | 
			
		||||
	i = sort.SearchInts(expr.secondList, v)
 | 
			
		||||
	if i == len(expr.secondList) {
 | 
			
		||||
		return expr.lastMinute(fromTime, true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we reach this point, there is nothing better to do
 | 
			
		||||
	// than to move to the next second
 | 
			
		||||
 | 
			
		||||
	return expr.lastSecond(fromTime)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
// NextN returns a slice of `n` closest time instants immediately following
 | 
			
		||||
// `fromTime` which match the cron expression `expr`.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +336,7 @@ func (expr *Expression) NextN(fromTime time.Time, n uint) []time.Time {
 | 
			
		|||
				break
 | 
			
		||||
			}
 | 
			
		||||
			nextTimes = append(nextTimes, fromTime)
 | 
			
		||||
			n -= 1
 | 
			
		||||
			n--
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -264,3 +345,32 @@ func (expr *Expression) NextN(fromTime time.Time, n uint) []time.Time {
 | 
			
		|||
	}
 | 
			
		||||
	return nextTimes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LastN returns a slice of `n` closest time instants immediately before
 | 
			
		||||
// `fromTime` which match the cron expression `expr`.
 | 
			
		||||
//
 | 
			
		||||
// The time instants in the returned slice are in chronological ascending order.
 | 
			
		||||
// The `time.Location` of the returned time instants is the same as that of
 | 
			
		||||
// `fromTime`.
 | 
			
		||||
//
 | 
			
		||||
// A slice with len between [0-`n`] is returned, that is, if not enough existing
 | 
			
		||||
// matching time instants exist, the number of returned entries will be less
 | 
			
		||||
// than `n`.
 | 
			
		||||
func (expr *Expression) LastN(fromTime time.Time, n uint) []time.Time {
 | 
			
		||||
	nextTimes := make([]time.Time, 0, n)
 | 
			
		||||
	if n > 0 {
 | 
			
		||||
		fromTime = expr.Last(fromTime)
 | 
			
		||||
		for {
 | 
			
		||||
			if fromTime.IsZero() {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			nextTimes = append(nextTimes, fromTime)
 | 
			
		||||
			n--
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			fromTime = expr.lastSecond(fromTime)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nextTimes
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										203
									
								
								cronexpr_next.go
								
								
								
								
							
							
						
						
									
										203
									
								
								cronexpr_next.go
								
								
								
								
							| 
						 | 
				
			
			@ -66,6 +66,50 @@ func (expr *Expression) nextYear(t time.Time) time.Time {
 | 
			
		|||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) lastYear(t time.Time, acc bool) time.Time {
 | 
			
		||||
	// candidate year
 | 
			
		||||
	v := t.Year()
 | 
			
		||||
	if acc {
 | 
			
		||||
		v--
 | 
			
		||||
	}
 | 
			
		||||
	i := sort.SearchInts(expr.yearList, v)
 | 
			
		||||
	var year int
 | 
			
		||||
	if i < len(expr.yearList) && v == expr.yearList[i] {
 | 
			
		||||
		year = expr.yearList[i]
 | 
			
		||||
	} else if i == 0 {
 | 
			
		||||
		return time.Time{}
 | 
			
		||||
	} else {
 | 
			
		||||
		year = expr.yearList[i-1]
 | 
			
		||||
	}
 | 
			
		||||
	// Year changed, need to recalculate actual days of month
 | 
			
		||||
	expr.actualDaysOfMonthList = expr.calculateActualDaysOfMonth(
 | 
			
		||||
		year,
 | 
			
		||||
		expr.monthList[len(expr.monthList)-1])
 | 
			
		||||
 | 
			
		||||
	if len(expr.actualDaysOfMonthList) == 0 {
 | 
			
		||||
		return expr.lastMonth(time.Date(
 | 
			
		||||
			year,
 | 
			
		||||
			time.Month(expr.monthList[len(expr.monthList)-1]),
 | 
			
		||||
			1,
 | 
			
		||||
			expr.hourList[len(expr.hourList)-1],
 | 
			
		||||
			expr.minuteList[len(expr.minuteList)-1],
 | 
			
		||||
			expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
			0,
 | 
			
		||||
			t.Location()), true)
 | 
			
		||||
	}
 | 
			
		||||
	return time.Date(
 | 
			
		||||
		year,
 | 
			
		||||
		time.Month(expr.monthList[len(expr.monthList)-1]),
 | 
			
		||||
		expr.actualDaysOfMonthList[len(expr.actualDaysOfMonthList)-1],
 | 
			
		||||
		expr.hourList[len(expr.hourList)-1],
 | 
			
		||||
		expr.minuteList[len(expr.minuteList)-1],
 | 
			
		||||
		expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
		0,
 | 
			
		||||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) nextMonth(t time.Time) time.Time {
 | 
			
		||||
	// Find index at which item in list is greater or equal to
 | 
			
		||||
	// candidate month
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +144,51 @@ func (expr *Expression) nextMonth(t time.Time) time.Time {
 | 
			
		|||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) nextDayOfMonth(t time.Time) time.Time {
 | 
			
		||||
func (expr *Expression) lastMonth(t time.Time, acc bool) time.Time {
 | 
			
		||||
	// candidate month
 | 
			
		||||
	v := int(t.Month())
 | 
			
		||||
	if acc {
 | 
			
		||||
		v--
 | 
			
		||||
	}
 | 
			
		||||
	i := sort.SearchInts(expr.monthList, v)
 | 
			
		||||
 | 
			
		||||
	var month int
 | 
			
		||||
	if i < len(expr.monthList) && v == expr.monthList[i] {
 | 
			
		||||
		month = expr.monthList[i]
 | 
			
		||||
	} else if i == 0 {
 | 
			
		||||
		return expr.lastYear(t, true)
 | 
			
		||||
	} else {
 | 
			
		||||
		month = expr.monthList[i-1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Month changed, need to recalculate actual days of month
 | 
			
		||||
	expr.actualDaysOfMonthList = expr.calculateActualDaysOfMonth(t.Year(), month)
 | 
			
		||||
	if len(expr.actualDaysOfMonthList) == 0 {
 | 
			
		||||
		return expr.lastMonth(time.Date(
 | 
			
		||||
			t.Year(),
 | 
			
		||||
			time.Month(month),
 | 
			
		||||
			1,
 | 
			
		||||
			expr.hourList[len(expr.hourList)-1],
 | 
			
		||||
			expr.minuteList[len(expr.minuteList)-1],
 | 
			
		||||
			expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
			0,
 | 
			
		||||
			t.Location()), true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Date(
 | 
			
		||||
		t.Year(),
 | 
			
		||||
		time.Month(month),
 | 
			
		||||
		expr.actualDaysOfMonthList[len(expr.actualDaysOfMonthList)-1],
 | 
			
		||||
		expr.hourList[len(expr.hourList)-1],
 | 
			
		||||
		expr.minuteList[len(expr.minuteList)-1],
 | 
			
		||||
		expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
		0,
 | 
			
		||||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) nextActualDayOfMonth(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)
 | 
			
		||||
| 
						 | 
				
			
			@ -121,12 +209,42 @@ func (expr *Expression) nextDayOfMonth(t time.Time) time.Time {
 | 
			
		|||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) lastActualDayOfMonth(t time.Time, acc bool) time.Time {
 | 
			
		||||
	// candidate day of month
 | 
			
		||||
	v := t.Day()
 | 
			
		||||
	if acc {
 | 
			
		||||
		v--
 | 
			
		||||
	}
 | 
			
		||||
	i := sort.SearchInts(expr.actualDaysOfMonthList, v)
 | 
			
		||||
 | 
			
		||||
	var day int
 | 
			
		||||
	if i < len(expr.actualDaysOfMonthList) && v == expr.actualDaysOfMonthList[i] {
 | 
			
		||||
		day = expr.actualDaysOfMonthList[i]
 | 
			
		||||
	} else if i == 0 {
 | 
			
		||||
		return expr.lastMonth(t, true)
 | 
			
		||||
	} else {
 | 
			
		||||
		day = expr.actualDaysOfMonthList[i-1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Date(
 | 
			
		||||
		t.Year(),
 | 
			
		||||
		t.Month(),
 | 
			
		||||
		day,
 | 
			
		||||
		expr.hourList[len(expr.hourList)-1],
 | 
			
		||||
		expr.minuteList[len(expr.minuteList)-1],
 | 
			
		||||
		expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
		0,
 | 
			
		||||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
		return expr.nextDayOfMonth(t)
 | 
			
		||||
		return expr.nextActualDayOfMonth(t)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Date(
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +260,36 @@ func (expr *Expression) nextHour(t time.Time) time.Time {
 | 
			
		|||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) lastHour(t time.Time, acc bool) time.Time {
 | 
			
		||||
	// candidate hour
 | 
			
		||||
	v := t.Hour()
 | 
			
		||||
	if acc {
 | 
			
		||||
		v--
 | 
			
		||||
	}
 | 
			
		||||
	i := sort.SearchInts(expr.hourList, v)
 | 
			
		||||
 | 
			
		||||
	var hour int
 | 
			
		||||
	if i < len(expr.hourList) && v == expr.hourList[i] {
 | 
			
		||||
		hour = expr.hourList[i]
 | 
			
		||||
	} else if i == 0 {
 | 
			
		||||
		return expr.lastActualDayOfMonth(t, true)
 | 
			
		||||
	} else {
 | 
			
		||||
		hour = expr.hourList[i-1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Date(
 | 
			
		||||
		t.Year(),
 | 
			
		||||
		t.Month(),
 | 
			
		||||
		t.Day(),
 | 
			
		||||
		hour,
 | 
			
		||||
		expr.minuteList[len(expr.minuteList)-1],
 | 
			
		||||
		expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
		0,
 | 
			
		||||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) nextMinute(t time.Time) time.Time {
 | 
			
		||||
	// Find index at which item in list is greater or equal to
 | 
			
		||||
	// candidate minute
 | 
			
		||||
| 
						 | 
				
			
			@ -163,12 +311,42 @@ func (expr *Expression) nextMinute(t time.Time) time.Time {
 | 
			
		|||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) lastMinute(t time.Time, acc bool) time.Time {
 | 
			
		||||
	// candidate minute
 | 
			
		||||
	v := t.Minute()
 | 
			
		||||
	if !acc {
 | 
			
		||||
		v--
 | 
			
		||||
	}
 | 
			
		||||
	i := sort.SearchInts(expr.minuteList, v)
 | 
			
		||||
	var min int
 | 
			
		||||
	if i < len(expr.minuteList) && v == expr.minuteList[i] {
 | 
			
		||||
		min = expr.minuteList[i]
 | 
			
		||||
	} else if i == 0 {
 | 
			
		||||
		return expr.lastHour(t, true)
 | 
			
		||||
	} else {
 | 
			
		||||
		min = expr.minuteList[i-1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Date(
 | 
			
		||||
		t.Year(),
 | 
			
		||||
		t.Month(),
 | 
			
		||||
		t.Day(),
 | 
			
		||||
		t.Hour(),
 | 
			
		||||
		min,
 | 
			
		||||
		expr.secondList[len(expr.secondList)-1],
 | 
			
		||||
		0,
 | 
			
		||||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) nextSecond(t time.Time) time.Time {
 | 
			
		||||
	// nextSecond() assumes all other fields are exactly matched
 | 
			
		||||
	// to the cron expression
 | 
			
		||||
 | 
			
		||||
	// 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) {
 | 
			
		||||
		return expr.nextMinute(t)
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +363,27 @@ func (expr *Expression) nextSecond(t time.Time) time.Time {
 | 
			
		|||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lastSecond() assumes all other fields are exactly matched
 | 
			
		||||
// to the cron expression
 | 
			
		||||
func (expr *Expression) lastSecond(t time.Time) time.Time {
 | 
			
		||||
	// candidate second
 | 
			
		||||
	v := t.Second() - 1
 | 
			
		||||
	i := sort.SearchInts(expr.secondList, v)
 | 
			
		||||
	if i == len(expr.secondList) || expr.secondList[i] != v {
 | 
			
		||||
		return expr.lastMinute(t, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Date(
 | 
			
		||||
		t.Year(),
 | 
			
		||||
		t.Month(),
 | 
			
		||||
		t.Day(),
 | 
			
		||||
		t.Hour(),
 | 
			
		||||
		t.Minute(),
 | 
			
		||||
		expr.secondList[i],
 | 
			
		||||
		0,
 | 
			
		||||
		t.Location())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										317
									
								
								cronexpr_test.go
								
								
								
								
							
							
						
						
									
										317
									
								
								cronexpr_test.go
								
								
								
								
							| 
						 | 
				
			
			@ -199,6 +199,248 @@ var crontests = []crontest{
 | 
			
		|||
	// TODO: more tests
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var cronbackwardtests = []crontest{
 | 
			
		||||
	// Seconds
 | 
			
		||||
	{
 | 
			
		||||
		"* * * * * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2013-01-01 00:00:01", "2013-01-01 00:00:00"},
 | 
			
		||||
			{"2013-01-01 00:01:00", "2013-01-01 00:00:59"},
 | 
			
		||||
			{"2013-01-01 01:00:00", "2013-01-01 00:59:59"},
 | 
			
		||||
			{"2013-01-02 00:00:00", "2013-01-01 23:59:59"},
 | 
			
		||||
			{"2013-03-01 00:00:00", "2013-02-28 23:59:59"},
 | 
			
		||||
			{"2016-02-29 00:00:00", "2016-02-28 23:59:59"},
 | 
			
		||||
			{"2013-01-01 00:00:00", "2012-12-31 23:59:59"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// every 5 Second
 | 
			
		||||
	{
 | 
			
		||||
		"*/5 * * * * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2013-01-01 00:00:06", "2013-01-01 00:00:05"},
 | 
			
		||||
			{"2013-01-01 00:01:00", "2013-01-01 00:00:55"},
 | 
			
		||||
			{"2013-01-01 01:00:00", "2013-01-01 00:59:55"},
 | 
			
		||||
			{"2013-01-02 00:00:00", "2013-01-01 23:59:55"},
 | 
			
		||||
			{"2013-03-01 00:00:00", "2013-02-28 23:59:55"},
 | 
			
		||||
			{"2016-02-29 00:00:00", "2016-02-28 23:59:55"},
 | 
			
		||||
			{"2013-01-01 00:00:00", "2012-12-31 23:59:55"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Minutes
 | 
			
		||||
	{
 | 
			
		||||
		"* * * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2013-01-01 00:00:58", "2013-01-01 00:00:00"},
 | 
			
		||||
			{"2013-01-01 00:01:00", "2013-01-01 00:00:00"},
 | 
			
		||||
			{"2013-01-01 01:00:00", "2013-01-01 00:59:00"},
 | 
			
		||||
			{"2013-01-02 00:00:00", "2013-01-01 23:59:00"},
 | 
			
		||||
			{"2013-03-01 00:00:00", "2013-02-28 23:59:00"},
 | 
			
		||||
			{"2016-02-29 00:00:00", "2016-02-28 23:59:00"},
 | 
			
		||||
			{"2013-01-01 00:00:00", "2012-12-31 23:59:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// // Minutes with interval
 | 
			
		||||
	{
 | 
			
		||||
		"17-43/5 * * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2013-01-01 00:17:01", "2013-01-01 00:17:00"},
 | 
			
		||||
			{"2013-01-01 00:33:00", "2013-01-01 00:32:00"},
 | 
			
		||||
			{"2013-01-01 01:00:00", "2013-01-01 00:42:00"},
 | 
			
		||||
			{"2013-01-02 00:01:00", "2013-01-01 23:42:00"},
 | 
			
		||||
			{"2013-03-01 00:01:00", "2013-02-28 23:42:00"},
 | 
			
		||||
			{"2016-02-29 00:01:00", "2016-02-28 23:42:00"},
 | 
			
		||||
			{"2013-01-01 00:01:00", "2012-12-31 23:42:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Minutes interval, list
 | 
			
		||||
	{
 | 
			
		||||
		"15-30/4,55 * * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2013-01-01 00:16:00", "2013-01-01 00:15:00"},
 | 
			
		||||
			{"2013-01-01 00:18:59", "2013-01-01 00:15:00"},
 | 
			
		||||
			{"2013-01-01 00:19:00", "2013-01-01 00:15:00"},
 | 
			
		||||
			{"2013-01-01 00:56:00", "2013-01-01 00:55:00"},
 | 
			
		||||
			{"2013-01-01 01:15:00", "2013-01-01 00:55:00"},
 | 
			
		||||
			{"2013-01-02 00:15:00", "2013-01-01 23:55:00"},
 | 
			
		||||
			{"2013-03-01 00:15:00", "2013-02-28 23:55:00"},
 | 
			
		||||
			{"2016-02-29 00:15:00", "2016-02-28 23:55:00"},
 | 
			
		||||
			{"2012-12-31 23:54:00", "2012-12-31 23:27:00"},
 | 
			
		||||
			{"2013-01-01 00:15:00", "2012-12-31 23:55:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Hour interval
 | 
			
		||||
	{
 | 
			
		||||
		"* 9-19/3 * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-01-01 00:10:00", "2016-12-31 18:59:00"},
 | 
			
		||||
			{"2017-02-01 00:10:00", "2017-01-31 18:59:00"},
 | 
			
		||||
			{"2017-02-12 00:10:00", "2017-02-11 18:59:00"},
 | 
			
		||||
			{"2017-02-12 19:10:00", "2017-02-12 18:59:00"},
 | 
			
		||||
			{"2017-02-12 12:15:00", "2017-02-12 12:14:00"},
 | 
			
		||||
			{"2017-02-12 13:00:00", "2017-02-12 12:59:00"},
 | 
			
		||||
			{"2017-02-12 11:00:00", "2017-02-12 09:59:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Hour interval, list
 | 
			
		||||
	{
 | 
			
		||||
		"5 12-21/3,23 * * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-01-01 00:10:00", "2016-12-31 23:05:00"},
 | 
			
		||||
			{"2017-02-01 00:10:00", "2017-01-31 23:05:00"},
 | 
			
		||||
			{"2017-02-12 00:10:00", "2017-02-11 23:05:00"},
 | 
			
		||||
			{"2017-02-12 19:10:00", "2017-02-12 18:05:00"},
 | 
			
		||||
			{"2017-02-12 12:15:00", "2017-02-12 12:05:00"},
 | 
			
		||||
			{"2017-02-12 22:00:00", "2017-02-12 21:05:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Day interval
 | 
			
		||||
	{
 | 
			
		||||
		"5 10-17 12-25/4 * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-01-01 00:10:00", "2016-12-24 17:05:00"},
 | 
			
		||||
			{"2017-02-01 10:10:00", "2017-01-24 17:05:00"},
 | 
			
		||||
			{"2017-02-27 13:10:00", "2017-02-24 17:05:00"},
 | 
			
		||||
			{"2017-02-23 13:10:00", "2017-02-20 17:05:00"},
 | 
			
		||||
			{"2017-02-11 13:10:00", "2017-01-24 17:05:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Day interval, list
 | 
			
		||||
	{
 | 
			
		||||
		"* * 12-15,20-22 * *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-01-01 00:20:00", "2016-12-22 23:59:00"},
 | 
			
		||||
			{"2017-02-01 10:30:00", "2017-01-22 23:59:00"},
 | 
			
		||||
			{"2017-02-27 13:40:00", "2017-02-22 23:59:00"},
 | 
			
		||||
			{"2017-02-17 16:10:00", "2017-02-15 23:59:00"},
 | 
			
		||||
			{"2017-02-11 13:10:00", "2017-01-22 23:59:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Month
 | 
			
		||||
	{
 | 
			
		||||
		"5 10 1 4-6 *",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-01-01 00:10:00", "2016-06-01 10:05:00"},
 | 
			
		||||
			{"2017-07-01 10:01:00", "2017-06-01 10:05:00"},
 | 
			
		||||
			{"2017-06-03 00:10:00", "2017-06-01 10:05:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Month
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 0 12 * * 2017-2020",
 | 
			
		||||
		"2006-01-02 15:04:05",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-12-11 00:10:00", "2017-11-12 00:00:00"},
 | 
			
		||||
			{"2023-01-11 00:10:00", "2020-12-12 00:00:00"},
 | 
			
		||||
			{"2021-01-11 00:10:00", "2020-12-12 00:00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Days of week
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 * * MON",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2013-01-10 00:00:00", "Mon 2013-01-07 00:00"},
 | 
			
		||||
			{"2017-08-07 00:00:00", "Mon 2017-07-31 00:00"},
 | 
			
		||||
			{"2017-01-01 00:30:00", "Mon 2016-12-26 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 * * friday",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-08-14 00:00:00", "Fri 2017-08-11 00:00"},
 | 
			
		||||
			{"2017-08-02 00:00:00", "Fri 2017-07-28 00:00"},
 | 
			
		||||
			{"2018-01-02 00:30:00", "Fri 2017-12-29 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 * * 6,7",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-09-04 00:00:00", "Sun 2017-09-03 00:00"},
 | 
			
		||||
			{"2017-08-02 00:00:00", "Sun 2017-07-30 00:00"},
 | 
			
		||||
			{"2018-01-03 00:30:00", "Sun 2017-12-31 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// // Specific days of week
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 * * 6#5",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-03-03 00:00:00", "Sat 2016-12-31 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// // Work day of month
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 18W * *",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-12-02 00:00:00", "Fri 2017-11-17 00:00"},
 | 
			
		||||
			{"2017-10-12 00:00:00", "Mon 2017-09-18 00:00"},
 | 
			
		||||
			{"2017-08-30 00:00:00", "Fri 2017-08-18 00:00"},
 | 
			
		||||
			{"2017-06-21 00:00:00", "Mon 2017-06-19 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// // Work day of month -- end of month
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 30W * *",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-03-02 00:00:00", "Mon 2017-01-30 00:00"},
 | 
			
		||||
			{"2017-06-02 00:00:00", "Tue 2017-05-30 00:00"},
 | 
			
		||||
			{"2017-08-02 00:00:00", "Mon 2017-07-31 00:00"},
 | 
			
		||||
			{"2017-11-02 00:00:00", "Mon 2017-10-30 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// // Last day of month
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 L * *",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2017-01-02 00:00:00", "Sat 2016-12-31 00:00"},
 | 
			
		||||
			{"2017-02-01 00:00:00", "Tue 2017-01-31 00:00"},
 | 
			
		||||
			{"2017-03-01 00:00:00", "Tue 2017-02-28 00:00"},
 | 
			
		||||
			{"2016-03-15 00:00:00", "Mon 2016-02-29 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// // Last work day of month
 | 
			
		||||
	{
 | 
			
		||||
		"0 0 LW * *",
 | 
			
		||||
		"Mon 2006-01-02 15:04",
 | 
			
		||||
		[]crontimes{
 | 
			
		||||
			{"2016-03-02 00:00:00", "Mon 2016-02-29 00:00"},
 | 
			
		||||
			{"2017-11-02 00:00:00", "Tue 2017-10-31 00:00"},
 | 
			
		||||
			{"2017-08-15 00:00:00", "Mon 2017-07-31 00:00"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExpressions(t *testing.T) {
 | 
			
		||||
	for _, test := range crontests {
 | 
			
		||||
		for _, times := range test.times {
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +458,23 @@ func TestExpressions(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBackwardExpressions(t *testing.T) {
 | 
			
		||||
	for _, test := range cronbackwardtests {
 | 
			
		||||
		for _, times := range test.times {
 | 
			
		||||
			from, _ := time.Parse("2006-01-02 15:04:05", times.from)
 | 
			
		||||
			expr, err := cronexpr.Parse(test.expr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf(`cronexpr.Parse("%s") returned "%s"`, test.expr, err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			last := expr.Last(from)
 | 
			
		||||
			laststr := last.Format(test.layout)
 | 
			
		||||
			if laststr != times.next {
 | 
			
		||||
				t.Errorf(`("%s").Last("%s") = "%s", got "%s"`, test.expr, times.from, times.next, laststr)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
func TestZero(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -261,6 +520,33 @@ func TestNextN(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLastN(t *testing.T) {
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"Sat, 29 Nov 2014 00:00:00",
 | 
			
		||||
		"Sat, 30 Aug 2014 00:00:00",
 | 
			
		||||
		"Sat, 31 May 2014 00:00:00",
 | 
			
		||||
		"Sat, 29 Mar 2014 00:00:00",
 | 
			
		||||
		"Sat, 30 Nov 2013 00:00:00",
 | 
			
		||||
	}
 | 
			
		||||
	layout := "2006-01-02 15:04:05"
 | 
			
		||||
	ts := "2015-01-02 08:44:30"
 | 
			
		||||
	expr := "0 0 * * 6#5"
 | 
			
		||||
	from, _ := time.Parse(layout, ts)
 | 
			
		||||
	result := cronexpr.MustParse(expr).
 | 
			
		||||
		LastN(from, uint(len(expected)))
 | 
			
		||||
	if len(result) != len(expected) {
 | 
			
		||||
		t.Errorf(`MustParse("%s").LastN("%s", 5):\n"`, expr, ts)
 | 
			
		||||
		t.Errorf(`  Expected %d returned time values but got %d instead`, len(expected), len(result))
 | 
			
		||||
	}
 | 
			
		||||
	for i, next := range result {
 | 
			
		||||
		nextStr := next.Format("Mon, 2 Jan 2006 15:04:15")
 | 
			
		||||
		if nextStr != expected[i] {
 | 
			
		||||
			t.Errorf(`MustParse("%s").LastN("%s", 5):\n"`, expr, ts)
 | 
			
		||||
			t.Errorf(`  result[%d]: expected "%s" but got "%s"`, i, expected[i], nextStr)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNextN_every5min(t *testing.T) {
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"Mon, 2 Sep 2013 08:45:00",
 | 
			
		||||
| 
						 | 
				
			
			@ -278,14 +564,41 @@ func TestNextN_every5min(t *testing.T) {
 | 
			
		|||
	for i, next := range result {
 | 
			
		||||
		nextStr := next.Format("Mon, 2 Jan 2006 15:04:05")
 | 
			
		||||
		if nextStr != expected[i] {
 | 
			
		||||
		t.Errorf(`MustParse("*/5 * * * *").NextN("2013-09-02 08:44:30", 5):\n"`)
 | 
			
		||||
			t.Errorf(`MustParse("*/5 * * * *").NextN("2013-09-02 08:44:30", 5):\n"`)
 | 
			
		||||
			t.Errorf(`  result[%d]: expected "%s" but got "%s"`, i, expected[i], nextStr)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLastN_every5min(t *testing.T) {
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"Mon, 2 Sep 2013 09:05:00",
 | 
			
		||||
		"Mon, 2 Sep 2013 09:00:00",
 | 
			
		||||
		"Mon, 2 Sep 2013 08:55:00",
 | 
			
		||||
		"Mon, 2 Sep 2013 08:50:00",
 | 
			
		||||
		"Mon, 2 Sep 2013 08:45:00",
 | 
			
		||||
	}
 | 
			
		||||
	layout := "2006-01-02 15:04:05"
 | 
			
		||||
	ts := "2013-09-02 09:08:32"
 | 
			
		||||
	cron := "*/5 * * * *"
 | 
			
		||||
	from, _ := time.Parse(layout, ts)
 | 
			
		||||
	result := cronexpr.MustParse(cron).
 | 
			
		||||
		LastN(from, uint(len(expected)))
 | 
			
		||||
	if len(result) != len(expected) {
 | 
			
		||||
		t.Errorf(`MustParse("%s").LastN("%s", 5):\n"`, cron, ts)
 | 
			
		||||
		t.Errorf(`  Expected %d returned time values but got %d instead`, len(expected), len(result))
 | 
			
		||||
	}
 | 
			
		||||
	for i, next := range result {
 | 
			
		||||
		nextStr := next.Format("Mon, 2 Jan 2006 15:04:05")
 | 
			
		||||
		if nextStr != expected[i] {
 | 
			
		||||
			t.Errorf(`MustParse("%s").LastN("%s", 5):\n"`, cron, ts)
 | 
			
		||||
			t.Errorf(`  result[%d]: expected "%s" but got "%s"`, i, expected[i], nextStr)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Issue: https://github.com/gorhill/cronexpr/issues/16
 | 
			
		||||
func TestInterval_Interval60Issue(t *testing.T){
 | 
			
		||||
func TestInterval_Interval60Issue(t *testing.T) {
 | 
			
		||||
	_, err := cronexpr.Parse("*/60 * * * * *")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("parsing with interval 60 should return err")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue