Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions cron/constantdelay.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,23 @@ You can check the original license at:
https://github.com/robfig/cron/blob/master/LICENSE
*/

//nolint
package cron

import "time"

// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
// It does not support jobs more frequent than once a second.
type ConstantDelaySchedule struct {
Delay time.Duration
}

// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
if duration < time.Second {
duration = time.Second
}
return ConstantDelaySchedule{
Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
Delay: duration,
}
}

// Next returns the next time this should be run.
// This rounds so that the next activation time will be on the second.
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
return t.Add(schedule.Delay)
}
18 changes: 4 additions & 14 deletions cron/constantdelay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ You can check the original license at:
https://github.com/robfig/cron/blob/master/LICENSE
*/

//nolint
package cron

import (
Expand All @@ -29,9 +28,12 @@ func TestConstantDelayNext(t *testing.T) {
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00:00.00000005 2012"},
{"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"},
{"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"},
{"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:00.015 2012"},
{"Mon Jul 9 14:45:00.015 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:00.030 2012"},
{"Mon Jul 9 14:45:00.000000050 2012", 15 * time.Nanosecond, "Mon Jul 9 14:45:00.000000065 2012"},

// Wrap around hours
{"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"},
Expand All @@ -47,18 +49,6 @@ func TestConstantDelayNext(t *testing.T) {

// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"},

// Round to nearest second on the delay
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},

// Round up to 1 second if the duration is less.
{"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"},

// Round to nearest second when calculating the next time.
{"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"},

// Round to nearest second for both.
{"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
}

for _, c := range tests {
Expand Down
55 changes: 55 additions & 0 deletions cron/cron_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,61 @@ func TestMockClock(t *testing.T) {
assert.Equal(t, int64(10), counter.Load())
}

func TestMillisecond(t *testing.T) {
clk := clocktesting.NewFakeClock(time.Now())
cron := New(WithClock(clk))
counter1ms := atomic.Int64{}
counter15ms := atomic.Int64{}
counter100ms := atomic.Int64{}

cron.AddFunc("@every 1ms", func() {
counter1ms.Add(1)
})
cron.AddFunc("@every 15ms", func() {
counter15ms.Add(1)
})
cron.AddFunc("@every 100ms", func() {
counter100ms.Add(1)
})

cron.Start()
defer cron.Stop()
for range 1000 {
assert.Eventually(t, clk.HasWaiters, OneSecond, 1*time.Millisecond)
clk.Step(1 * time.Millisecond)
}
ctx := cron.Stop()
<-ctx.Done()

assert.Equal(t, int64(1000), counter1ms.Load())
assert.Equal(t, int64(66), counter15ms.Load())
assert.Equal(t, int64(10), counter100ms.Load())
}

func TestNanoseconds(t *testing.T) {
clk := clocktesting.NewFakeClock(time.Now())
cron := New(WithClock(clk))

counter100ns := atomic.Int64{}
cron.AddFunc("@every 100ns", func() {
counter100ns.Add(1)
})

cron.Start()
defer cron.Stop()

for range 500 {
assert.Eventually(t, clk.HasWaiters, OneSecond, 1*time.Millisecond)
clk.Step(5 * time.Nanosecond)
}
ctx := cron.Stop()
<-ctx.Done()

// 500 * 5 ns = 2500 ns
// 2500 every 100ns = 25
assert.Equal(t, int64(25), counter100ns.Load())
}

func TestMultiThreadedStartAndStop(*testing.T) {
cron := New()
go cron.Run()
Expand Down
3 changes: 2 additions & 1 deletion cron/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ You can check the original license at:
https://github.com/robfig/cron/blob/master/LICENSE
*/

//nolint
package cron

import (
Expand Down Expand Up @@ -167,6 +166,8 @@ func TestParseSchedule(t *testing.T) {
{standardParser, "CRON_TZ=UTC 5 * * * *", every5min(time.UTC)},
{secondParser, "CRON_TZ=Asia/Tokyo 0 5 * * * *", every5min(tokyo)},
{secondParser, "@every 5m", ConstantDelaySchedule{5 * time.Minute}},
{secondParser, "@every 5ms", ConstantDelaySchedule{5 * time.Millisecond}},
{secondParser, "@every 5ns", ConstantDelaySchedule{5 * time.Nanosecond}},
{secondParser, "@midnight", midnight(time.Local)},
{secondParser, "TZ=UTC @midnight", midnight(time.UTC)},
{secondParser, "TZ=Asia/Tokyo @midnight", midnight(tokyo)},
Expand Down
Loading