Pārlūkot izejas kodu

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

Rob Figueiredo 13 gadi atpakaļ
vecāks
revīzija
f6244e2084
2 mainītis faili ar 54 papildinājumiem un 5 dzēšanām
  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{})