浏览代码

Operate in Local timezone (whoops), sort zero times to the end (and ignore them), and add some tests for this.

Rob Figueiredo 13 年之前
父节点
当前提交
f6244e2084
共有 2 个文件被更改,包括 54 次插入5 次删除
  1. 18 5
      cron.go
  2. 36 0
      cron_test.go

+ 18 - 5
cron.go

@@ -25,9 +25,20 @@ type Entry struct {
 
 type byTime []*Entry
 
-func (s byTime) Len() int           { return len(s) }
-func (s byTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
-func (s byTime) Less(i, j int) bool { return s[i].Next.Before(s[j].Next) }
+func (s byTime) Len() int      { return len(s) }
+func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byTime) Less(i, j int) bool {
+	// Two zero times should return false.
+	// Otherwise, zero is "greater" than any other time.
+	// (To sort it at the end of the list.)
+	if s[i].Next.IsZero() {
+		return false
+	}
+	if s[j].Next.IsZero() {
+		return true
+	}
+	return s[i].Next.Before(s[j].Next)
+}
 
 func New() *Cron {
 	return &Cron{
@@ -46,6 +57,7 @@ func (c *Cron) Add(spec string, cmd func()) {
 	default:
 		// No one listening to that channel, so just add to the array.
 		c.Entries = append(c.Entries, entry)
+		entry.Next = entry.Schedule.Next(time.Now().Local()) // Just in case..
 	}
 }
 
@@ -55,7 +67,7 @@ func (c *Cron) Start() {
 
 func (c *Cron) Run() {
 	// Figure out the next activation times for each entry.
-	now := time.Now()
+	now := time.Now().Local()
 	for _, entry := range c.Entries {
 		entry.Next = entry.Schedule.Next(now)
 	}
@@ -65,7 +77,7 @@ func (c *Cron) Run() {
 		sort.Sort(byTime(c.Entries))
 
 		var effective time.Time
-		if len(c.Entries) == 0 {
+		if len(c.Entries) == 0 || c.Entries[0].Next.IsZero() {
 			// If there are no entries yet, just sleep - it still handles new entries
 			// and stop requests.
 			effective = now.AddDate(10, 0, 0)
@@ -86,6 +98,7 @@ func (c *Cron) Run() {
 
 		case newEntry := <-c.add:
 			c.Entries = append(c.Entries, newEntry)
+			newEntry.Next = newEntry.Schedule.Next(now)
 
 		case <-c.stop:
 			return

+ 36 - 0
cron_test.go

@@ -1,6 +1,7 @@
 package cron
 
 import (
+	"fmt"
 	"testing"
 	"time"
 )
@@ -51,6 +52,41 @@ func TestAddWhileRunning(t *testing.T) {
 	}
 }
 
+// Test that the entries are correctly sorted.
+// Add a bunch of long-in-the-future entries, and an immediate entry, and ensure
+// that the immediate entry runs immediately.
+func TestMultipleEntries(t *testing.T) {
+	cron := New()
+	cron.Add("0 0 0 1 1 ?", func() {})
+	cron.Add("* * * * * ?", func() {
+		cron.Stop()
+	})
+	cron.Add("0 0 0 31 12 ?", func() {})
+	done := startAndSignal(cron)
+
+	select {
+	case <-time.After(2 * time.Second):
+		t.FailNow()
+	case <-done:
+	}
+}
+
+// Test that the cron is run in the local time zone (as opposed to UTC).
+func TestLocalTimezone(t *testing.T) {
+	cron := New()
+	now := time.Now().Local()
+	spec := fmt.Sprintf("%d %d %d %d %d ?",
+		now.Second()+1, now.Minute(), now.Hour(), now.Day(), now.Month())
+	cron.Add(spec, func() { cron.Stop() })
+	done := startAndSignal(cron)
+
+	select {
+	case <-time.After(2 * time.Second):
+		t.FailNow()
+	case <-done:
+	}
+}
+
 // Return a channel that signals when the cron's Start() method returns.
 func startAndSignal(cron *Cron) <-chan struct{} {
 	ch := make(chan struct{})