Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parsing begin time flag #268

Merged
merged 6 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
93 changes: 51 additions & 42 deletions pkg/core/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,49 +59,10 @@ func Timer(opts TimerOptions) (<-chan Update, error) {
}
}
if opts.Begin != "" {
// calculate how long to wait
minsRe, err := regexp.Compile(`^\+([0-9]+)$`)
// calculate delay based on begin time
delay, err = waitForBeginTime(opts.Begin, now)
if err != nil {
return nil, fmt.Errorf("invalid matcher for checking begin delay options: %v", err)
}
timeRe, err := regexp.Compile(`([0-9][0-9])([0-9][0-9])`)
if err != nil {
return nil, fmt.Errorf("invalid matcher for checking begin delay options: %v", err)
}

// first look for +MM, which means delay MM minutes
delayMinsParts := minsRe.FindStringSubmatch(opts.Begin)
startTimeParts := timeRe.FindStringSubmatch(opts.Begin)

switch {
case len(delayMinsParts) > 1:
delayMins, err := strconv.Atoi(delayMinsParts[1])
if err != nil {
return nil, fmt.Errorf("invalid format for begin delay '%s': %v", opts.Begin, err)
}
delay = time.Duration(delayMins) * time.Minute
case len(startTimeParts) > 3:
hour, err := strconv.Atoi(startTimeParts[1])
if err != nil {
return nil, fmt.Errorf("invalid format for begin delay '%s': %v", opts.Begin, err)
}
minute, err := strconv.Atoi(startTimeParts[2])
if err != nil {
return nil, fmt.Errorf("invalid format for begin delay '%s': %v", opts.Begin, err)
}

// convert that start time into a Duration to wait
now := time.Now()

today := time.Date(now.Year(), now.Month(), now.Day(), hour, minute, now.Second(), now.Nanosecond(), time.UTC)
if today.After(now) {
delay = today.Sub(now)
} else {
// add one day
delay = today.Add(24 * time.Hour).Sub(now)
}
default:
return nil, fmt.Errorf("invalid format for begin delay '%s': %v", opts.Begin, err)
return nil, fmt.Errorf("invalid begin option '%s': %v", opts.Begin, err)
}
}

Expand Down Expand Up @@ -150,6 +111,54 @@ func Timer(opts TimerOptions) (<-chan Update, error) {
return c, nil
}

func waitForBeginTime(begin string, from time.Time) (time.Duration, error) {

// calculate how long to wait
minsRe, err := regexp.Compile(`^\+([0-9]+)$`)
if err != nil {
return time.Duration(0), fmt.Errorf("invalid matcher for checking begin delay options: %v", err)
}
timeRe, err := regexp.Compile(`^([0-9][0-9])([0-9][0-9])$`)
if err != nil {
return time.Duration(0), fmt.Errorf("invalid matcher for checking begin delay options: %v", err)
}

// first look for +MM, which means delay MM minutes
delayMinsParts := minsRe.FindStringSubmatch(begin)
startTimeParts := timeRe.FindStringSubmatch(begin)

var delay time.Duration
switch {
case len(delayMinsParts) > 1:
delayMins, err := strconv.Atoi(delayMinsParts[1])
if err != nil {
return time.Duration(0), fmt.Errorf("invalid format for begin delay '%s': %v", begin, err)
}
delay = time.Duration(delayMins) * time.Minute
case len(startTimeParts) > 2:
hour, err := strconv.Atoi(startTimeParts[1])
if err != nil {
return time.Duration(0), fmt.Errorf("invalid format for begin delay '%s': %v", begin, err)
}
minute, err := strconv.Atoi(startTimeParts[2])
if err != nil {
return time.Duration(0), fmt.Errorf("invalid format for begin delay '%s': %v", begin, err)
}

// convert that start time into a Duration to wait
today := time.Date(from.Year(), from.Month(), from.Day(), hour, minute, from.Second(), from.Nanosecond(), time.UTC)
if today.After(from) {
delay = today.Sub(from)
} else {
// add one day
delay = today.Add(24 * time.Hour).Sub(from)
}
default:
return time.Duration(0), fmt.Errorf("invalid format for begin delay '%s'", begin)
}
return delay, nil
}

// waitForCron given the current time and a cron string, calculate the Duration
// until the next time we will match the cron
func waitForCron(cronExpr string, from time.Time) (time.Duration, error) {
Expand Down
37 changes: 37 additions & 0 deletions pkg/core/timer_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package core

import (
"fmt"
"testing"
"time"
)
Expand Down Expand Up @@ -36,3 +37,39 @@ func TestWaitForCron(t *testing.T) {
})
}
}

func TestWaitForBeginTime(t *testing.T) {
tests := []struct {
name string
begin string
from string
wait time.Duration
err error
}{
{"wait one minute", "+1", "2018-10-10T10:00:00Z", 1 * time.Minute, nil},
{"wait 999999 minutes", "+999999", "2018-10-10T10:00:00Z", 999999 * time.Minute, nil},
{"wait until 10:23", "1023", "2018-10-10T10:00:00Z", 23 * time.Minute, nil},
{"wait until 23:59", "2359", "2018-10-10T10:00:00Z", 13*time.Hour + 59*time.Minute, nil},
{"wait until 9:59", "0959", "2018-10-10T10:00:00Z", 23*time.Hour + 59*time.Minute, nil},
{"pass time over 24h", "2401", "2018-10-10T10:00:00Z", 14*time.Hour + time.Minute, nil}, //time.Time accepts values outside the usual ranges
{"fail text", "today", "2018-10-10T10:00:00Z", time.Duration(0), fmt.Errorf("invalid format for begin delay 'today'")},
{"fail number", "1", "2018-10-10T10:00:00Z", time.Duration(0), fmt.Errorf("invalid format for begin delay '1'")},
{"fail +hour", "+1h", "2018-10-10T10:00:00Z", time.Duration(0), fmt.Errorf("invalid format for begin delay '+1h'")},
{"fail too long time", "12345", "2018-10-10T10:00:00Z", time.Duration(0), fmt.Errorf("invalid format for begin delay '12345'")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
from, err := time.Parse(time.RFC3339, tt.from)
if err != nil {
t.Fatalf("unable to parse from %s: %v", tt.from, err)
}
result, err := waitForBeginTime(tt.begin, from)
switch {
case (err != nil && tt.err == nil) || (err == nil && tt.err != nil) || (err != nil && tt.err != nil && err.Error() != tt.err.Error()):
t.Errorf("waitForBeginTime(%s, %s) error = %v, wantErr %v", tt.begin, tt.from, err, tt.err)
case result != tt.wait:
t.Errorf("waitForBeginTime(%s, %s) = %v, want %v", tt.begin, tt.from, result, tt.wait)
}
})
}
}
Loading