Skip to content

Commit

Permalink
Merge pull request #2 from reugn/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
reugn authored Apr 16, 2019
2 parents 04c0dcd + 3ab16cb commit 4767bda
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 47 deletions.
2 changes: 2 additions & 0 deletions examples/go-quartz.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ func (pj PrintJob) Execute() {
//demo main
func main() {
sched := quartz.NewStdScheduler()
cronTrigger, _ := quartz.NewCronTrigger("1/3 * * * * *")
sched.Start()
sched.ScheduleJob(&PrintJob{"Ad hoc Job"}, quartz.NewRunOnceTrigger(time.Second*5))
sched.ScheduleJob(&PrintJob{"First job"}, quartz.NewSimpleTrigger(time.Second*12))
sched.ScheduleJob(&PrintJob{"Second job"}, quartz.NewSimpleTrigger(time.Second*6))
sched.ScheduleJob(&PrintJob{"Third job"}, quartz.NewSimpleTrigger(time.Second*3))
sched.ScheduleJob(&PrintJob{"Cron job"}, cronTrigger)
time.Sleep(time.Second * 15)
sched.Stop()
}
79 changes: 48 additions & 31 deletions quartz/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
)

type CronTrigger struct {
expression string
fields []*CronField
lastDefined int
}
Expand All @@ -29,7 +30,16 @@ func NewCronTrigger(expr string) (*CronTrigger, error) {
if lastDefined == -1 {
fields[0].values, _ = fillRange(0, 59)
}
return &CronTrigger{fields, lastDefined}, nil
return &CronTrigger{expr, fields, lastDefined}, nil
}

func (ct *CronTrigger) NextFireTime(prev int64) (int64, error) {
parser := NewCronExpressionParser(ct.lastDefined)
return parser.nextTime(prev, ct.fields)
}

func (st *CronTrigger) Description() string {
return fmt.Sprintf("CronTrigger %s", st.expression)
}

type CronExpressionParser struct {
Expand Down Expand Up @@ -62,9 +72,21 @@ func (cf *CronField) toString() string {
}

var (
months = []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
months = []string{"0", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
days = []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
daysInMonth = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
daysInMonth = []int{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

// pre-defined cron expressions
special = map[string]string{
"@yearly": "0 0 0 1 1 *",
"@monthly": "0 0 0 1 * *",
"@weekly": "0 0 0 * * 0",
"@daily": "0 0 0 * * *",
"@hourly": "0 0 * * * *",
}

readDateLayout = "Mon Jan 2 15:04:05 2006"
writeDateLayout = "Jan 2 15:04:05 2006"
)

//<second> <minute> <hour> <day-of-month> <month> <day-of-week> <year>
Expand All @@ -79,11 +101,6 @@ const (
yearIndex
)

func (ct *CronTrigger) NextFireTime(prev int64) (int64, error) {
parser := NewCronExpressionParser(ct.lastDefined)
return parser.nextTime(prev, ct.fields)
}

func (parser *CronExpressionParser) nextTime(prev int64, fields []*CronField) (nextTime int64, err error) {
defer func() {
if r := recover(); r != nil {
Expand All @@ -97,39 +114,41 @@ func (parser *CronExpressionParser) nextTime(prev int64, fields []*CronField) (n
}
}
}()
// UnixDate = "Mon Jan _2 15:04:05 MST 2006"
tfmt := time.Unix(prev, 0).Format(time.UnixDate)
tfmt := time.Unix(prev/int64(time.Second), 0).UTC().Format(readDateLayout)
ttok := strings.Split(strings.Replace(tfmt, " ", " ", 1), " ")
hms := strings.Split(ttok[3], ":")
parser.maxDays = maxDays(intVal(months, ttok[1]), atoi(ttok[5]))
parser.maxDays = maxDays(intVal(months, ttok[1]), atoi(ttok[4]))

second := parser.nextSeconds(atoi(hms[2]), fields[0])
minute := parser.nextMinutes(atoi(hms[1]), fields[1])
hour := parser.nextHours(atoi(hms[0]), fields[2])
dayOfWeek, dayOfMonth := parser.nextDay(intVal(days, ttok[0]), fields[5],
atoi(strings.Replace(ttok[2], "_", "", 1)), fields[3])
dayOfMonth := parser.nextDay(intVal(days, ttok[0]), fields[5], atoi(ttok[2]), fields[3])
month := parser.nextMonth(ttok[1], fields[4])
year := parser.nextYear(ttok[5], fields[6])
year := parser.nextYear(ttok[4], fields[6])

nstr := fmt.Sprintf("%s %s %s %s:%s:%s %s %s", dayOfWeek, month, dayOfMonth,
hour, minute, second, ttok[4], year)
ntime, err := time.Parse(time.UnixDate, nstr)
nextTime = ntime.Unix()
nstr := fmt.Sprintf("%s %s %s:%s:%s %s", month, strconv.Itoa(dayOfMonth),
hour, minute, second, year)
ntime, err := time.Parse(writeDateLayout, nstr)
nextTime = ntime.UnixNano()
return
}

//the ? wildcard is only used in the day of month and day of week fields
func validateCronExpression(expression string) ([]*CronField, error) {
var tokens []string
tokens = strings.Split(expression, " ")
if value, ok := special[expression]; ok {
tokens = strings.Split(value, " ")
} else {
tokens = strings.Split(expression, " ")
}
length := len(tokens)
if length < 6 || length > 7 {
return nil, cronError("length")
}
if length == 6 {
tokens = append(tokens, "*")
}
if (tokens[3] != "?" && tokens[3] != "*") && tokens[5] != "?" {
if (tokens[3] != "?" && tokens[3] != "*") && (tokens[5] != "?" && tokens[5] != "*") {
return nil, cronError("day field set twice")
}
if tokens[6] != "*" {
Expand Down Expand Up @@ -276,21 +295,19 @@ func (parser *CronExpressionParser) nextHours(prev int, field *CronField) string
}

func (parser *CronExpressionParser) nextDay(prevWeek int, weekField *CronField,
prevMonth int, monthField *CronField) (string, string) {
var nextMonth, nextWeek int
prevMonth int, monthField *CronField) int {
var nextMonth int
if weekField.isEmpty() && monthField.isEmpty() && parser.lastSet(dayOfWeekIndex) {
if parser.dayBump {
nextMonth, parser.monthBump = bumpValue(prevMonth, parser.maxDays, 1)
nextWeek, _ = bumpLiteral(prevWeek, 6, 1)
return days[nextWeek], alignDigit(nextMonth, " ")
return nextMonth
}
return days[prevWeek], alignDigit(prevMonth, " ")
return prevMonth
}
if len(monthField.values) > 0 {
nextMonth, parser.monthBump = parser.findNextValue(prevMonth, monthField.values)
parser.setDone(dayOfMonthIndex)
nextWeek, _ = bumpLiteral(prevWeek, 6, step(prevMonth, nextMonth, parser.maxDays))
return days[nextWeek], alignDigit(nextMonth, " ")
return nextMonth
} else if len(weekField.values) > 0 {
nextWeek, bumpDayOfMonth := parser.findNextValue(prevWeek, weekField.values)
parser.setDone(dayOfWeekIndex)
Expand All @@ -301,16 +318,16 @@ func (parser *CronExpressionParser) nextDay(prevWeek int, weekField *CronField,
_step = step(prevWeek, nextWeek, 7)
}
nextMonth, parser.monthBump = bumpValue(prevMonth, parser.maxDays, _step)
return days[nextWeek], alignDigit(nextMonth, " ")
return nextMonth
}
return days[prevWeek], alignDigit(prevMonth, " ")
return prevMonth
}

func (parser *CronExpressionParser) nextMonth(prev string, field *CronField) string {
var next int
if field.isEmpty() && parser.lastSet(dayOfWeekIndex) {
if parser.monthBump {
next, parser.yearBump = bumpLiteral(intVal(months, prev), 11, 1)
next, parser.yearBump = bumpLiteral(intVal(months, prev), 12, 1)
return months[next]
}
return prev
Expand Down Expand Up @@ -342,7 +359,7 @@ func bumpLiteral(iprev int, max int, step int) (int, bool) {
if bumped%max == 0 {
return iprev, true
}
return (bumped % max) - 1, true
return (bumped % max), true
}
return bumped, false
}
Expand Down
88 changes: 74 additions & 14 deletions quartz/cron_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,39 @@ import (
)

func TestCronExpression1(t *testing.T) {
prev := int64(1554120000)
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("10/20 15 14 5-10 * ? *")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Wed Nov 8 14:15:10 IST 2023")
assertEqual(t, result, "Fri Dec 8 14:15:10 2023")
}

func TestCronExpression2(t *testing.T) {
prev := int64(1554120000)
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("* 5,7,9 14-16 * * ? *")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Sun Jul 21 15:05:00 IDT 2019")
assertEqual(t, result, "Mon Aug 5 14:05:00 2019")
}

func TestCronExpression3(t *testing.T) {
prev := int64(1554120000)
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("* 5,7,9 14/2 * * Wed,Sat *")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Wed Nov 20 22:05:00 IST 2019")
assertEqual(t, result, "Sat Dec 7 14:05:00 2019")
}

func TestCronExpression4(t *testing.T) {
Expand All @@ -50,39 +50,99 @@ func TestCronExpression4(t *testing.T) {
}

func TestCronExpression5(t *testing.T) {
prev := int64(1554120000)
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("* * * * * ? *")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Mon Apr 1 15:16:40 IDT 2019")
assertEqual(t, result, "Mon Apr 15 18:16:40 2019")
}

func TestCronExpression6(t *testing.T) {
prev := int64(1554120000)
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("* * 14/2 * * Mon/3 *")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Thu Mar 4 20:00:00 IST 2021")
assertEqual(t, result, "Mon Mar 15 18:00:00 2021")
}

func TestCronExpression7(t *testing.T) {
prev := int64(1554120000)
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("* 5-9 14/2 * * 0-2 *")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Tue Jul 2 14:09:00 IDT 2019")
assertEqual(t, result, "Tue Jul 16 16:09:00 2019")
}

func TestCronYearly(t *testing.T) {
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("@yearly")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 100)
}
assertEqual(t, result, "Sun Jan 1 00:00:00 2119")
}

func TestCronMonthly(t *testing.T) {
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("@monthly")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 100)
}
assertEqual(t, result, "Sun Aug 1 00:00:00 2027")
}

func TestCronWeekly(t *testing.T) {
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("@weekly")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 100)
}
assertEqual(t, result, "Mon Mar 15 00:00:00 2021")
}

func TestCronDaily(t *testing.T) {
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("@daily")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Sun Jan 9 00:00:00 2022")
}

func TestCronHourly(t *testing.T) {
prev := int64(1555351200000000000)
result := ""
cronTrigger, err := NewCronTrigger("@hourly")
if err != nil {
t.Fatal(err)
} else {
result, _ = iterate(prev, cronTrigger, 1000)
}
assertEqual(t, result, "Wed May 29 06:00:00 2019")
}

func assertEqual(t *testing.T, a interface{}, b interface{}) {
Expand All @@ -95,10 +155,10 @@ func iterate(prev int64, cronTrigger *CronTrigger, iterations int) (string, erro
var err error
for i := 0; i < iterations; i++ {
prev, err = cronTrigger.NextFireTime(prev)
// fmt.Println(time.Unix(prev/int64(time.Second), 0).UTC().Format(readDateLayout))
if err != nil {
return "", err
}
// fmt.Println(time.Unix(prev, 0).Format(time.UnixDate))
}
return time.Unix(prev, 0).Format(time.UnixDate), nil
return time.Unix(prev/int64(time.Second), 0).UTC().Format(readDateLayout), nil
}
4 changes: 2 additions & 2 deletions quartz/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewStdScheduler() *StdScheduler {
}

func (sched *StdScheduler) ScheduleJob(job Job, trigger Trigger) error {
nextRunTime, err := trigger.NextFireTime(time.Now().UnixNano())
nextRunTime, err := trigger.NextFireTime(time.Now().UTC().UnixNano())
if err == nil {
sched.feeder <- &Item{
job,
Expand Down Expand Up @@ -132,7 +132,7 @@ func (sched *StdScheduler) startFeedReader() {
}

func parkTime(ts int64) int64 {
now := time.Now().UnixNano()
now := time.Now().UTC().UnixNano()
if ts > now {
return ts - now
}
Expand Down
8 changes: 8 additions & 0 deletions quartz/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,11 @@ func isLeapYear(year int) bool {
}
return true
}

func dayOfTheWeek(y int, m int, d int) string {
t := []int{0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}
if m < 3 {
y--
}
return days[((y + y/4 - y/100 + y/400 + t[m-1] + d) % 7)]
}

0 comments on commit 4767bda

Please sign in to comment.