multiple days in weekly recur

This commit is contained in:
Erik Winter 2021-02-02 08:47:58 +01:00
parent aef4dc1af2
commit 835683e1c8
4 changed files with 233 additions and 14 deletions

View File

@ -2,6 +2,7 @@ package task
import ( import (
"fmt" "fmt"
"sort"
"strings" "strings"
"time" "time"
) )
@ -17,6 +18,35 @@ func init() {
Today = NewDate(year, int(month), day) Today = NewDate(year, int(month), day)
} }
type Weekdays []time.Weekday
func (wds Weekdays) Len() int { return len(wds) }
func (wds Weekdays) Swap(i, j int) { wds[j], wds[i] = wds[i], wds[j] }
func (wds Weekdays) Less(i, j int) bool {
if wds[i] == time.Sunday {
return false
}
if wds[j] == time.Sunday {
return true
}
return int(wds[i]) < int(wds[j])
}
func (wds Weekdays) Unique() Weekdays {
mwds := map[time.Weekday]bool{}
for _, wd := range wds {
mwds[wd] = true
}
newWds := Weekdays{}
for wd := range mwds {
newWds = append(newWds, wd)
}
sort.Sort(newWds)
return newWds
}
type Date struct { type Date struct {
t time.Time t time.Time
} }

View File

@ -1,12 +1,79 @@
package task_test package task_test
import ( import (
"sort"
"testing" "testing"
"time"
"git.sr.ht/~ewintr/go-kit/test" "git.sr.ht/~ewintr/go-kit/test"
"git.sr.ht/~ewintr/gte/internal/task" "git.sr.ht/~ewintr/gte/internal/task"
) )
func TestWeekdaysSort(t *testing.T) {
for _, tc := range []struct {
name string
input task.Weekdays
exp task.Weekdays
}{
{
name: "empty",
},
{
name: "one",
input: task.Weekdays{time.Tuesday},
exp: task.Weekdays{time.Tuesday},
},
{
name: "multiple",
input: task.Weekdays{time.Wednesday, time.Tuesday, time.Monday},
exp: task.Weekdays{time.Monday, time.Tuesday, time.Wednesday},
},
{
name: "sunday is last",
input: task.Weekdays{time.Saturday, time.Sunday, time.Monday},
exp: task.Weekdays{time.Monday, time.Saturday, time.Sunday},
},
} {
t.Run(tc.name, func(t *testing.T) {
sort.Sort(tc.input)
test.Equals(t, tc.exp, tc.input)
})
}
}
func TestWeekdaysUnique(t *testing.T) {
for _, tc := range []struct {
name string
input task.Weekdays
exp task.Weekdays
}{
{
name: "empty",
input: task.Weekdays{},
exp: task.Weekdays{},
},
{
name: "single",
input: task.Weekdays{time.Monday},
exp: task.Weekdays{time.Monday},
},
{
name: "no doubles",
input: task.Weekdays{time.Monday, time.Tuesday, time.Wednesday},
exp: task.Weekdays{time.Monday, time.Tuesday, time.Wednesday},
},
{
name: "doubles",
input: task.Weekdays{time.Monday, time.Monday, time.Wednesday, time.Monday},
exp: task.Weekdays{time.Monday, time.Wednesday},
},
} {
t.Run(tc.name, func(t *testing.T) {
test.Equals(t, tc.exp, tc.input.Unique())
})
}
}
func TestNewDateFromString(t *testing.T) { func TestNewDateFromString(t *testing.T) {
task.Today = task.NewDate(2021, 1, 30) task.Today = task.NewDate(2021, 1, 30)
for _, tc := range []struct { for _, tc := range []struct {

View File

@ -64,11 +64,11 @@ func (d Daily) String() string {
} }
type Weekly struct { type Weekly struct {
Start Date Start Date
Weekday time.Weekday Weekdays Weekdays
} }
// yyyy-mm-dd, weekly, wednesday // yyyy-mm-dd, weekly, wednesday & saturday & sunday
func ParseWeekly(start Date, terms []string) (Recurrer, bool) { func ParseWeekly(start Date, terms []string) (Recurrer, bool) {
if len(terms) < 2 { if len(terms) < 2 {
return nil, false return nil, false
@ -78,14 +78,21 @@ func ParseWeekly(start Date, terms []string) (Recurrer, bool) {
return nil, false return nil, false
} }
wd, ok := ParseWeekday(terms[1]) wds := Weekdays{}
if !ok { for _, wdStr := range strings.Split(terms[1], "&") {
wd, ok := ParseWeekday(wdStr)
if !ok {
continue
}
wds = append(wds, wd)
}
if len(wds) == 0 {
return nil, false return nil, false
} }
return Weekly{ return Weekly{
Start: start, Start: start,
Weekday: wd, Weekdays: wds.Unique(),
}, true }, true
} }
@ -94,11 +101,23 @@ func (w Weekly) RecursOn(date Date) bool {
return false return false
} }
return w.Weekday == date.Weekday() for _, wd := range w.Weekdays {
if wd == date.Weekday() {
return true
}
}
return false
} }
func (w Weekly) String() string { func (w Weekly) String() string {
return fmt.Sprintf("%s, weekly, %s", w.Start.String(), strings.ToLower(w.Weekday.String())) weekdayStrs := []string{}
for _, wd := range w.Weekdays {
weekdayStrs = append(weekdayStrs, wd.String())
}
weekdayStr := strings.Join(weekdayStrs, " & ")
return fmt.Sprintf("%s, weekly, %s", w.Start.String(), strings.ToLower(weekdayStr))
} }
type Biweekly struct { type Biweekly struct {

View File

@ -50,12 +50,110 @@ func TestDaily(t *testing.T) {
}) })
} }
func TestParseWeekly(t *testing.T) {
start := task.NewDate(2021, 2, 7)
for _, tc := range []struct {
name string
input []string
expOk bool
expWeekly task.Weekly
}{
{
name: "empty",
},
{
name: "wrong type",
input: []string{"daily"},
},
{
name: "wrong count",
input: []string{"weeekly"},
},
{
name: "unknown day",
input: []string{"weekly", "festivus"},
},
{
name: "one day",
input: []string{"weekly", "monday"},
expOk: true,
expWeekly: task.Weekly{
Start: start,
Weekdays: task.Weekdays{
time.Monday,
},
},
},
{
name: "multiple days",
input: []string{"weekly", "monday & thursday & saturday"},
expOk: true,
expWeekly: task.Weekly{
Start: start,
Weekdays: task.Weekdays{
time.Monday,
time.Thursday,
time.Saturday,
},
},
},
{
name: "wrong order",
input: []string{"weekly", "sunday & thursday & wednesday"},
expOk: true,
expWeekly: task.Weekly{
Start: start,
Weekdays: task.Weekdays{
time.Wednesday,
time.Thursday,
time.Sunday,
},
},
},
{
name: "doubles",
input: []string{"weekly", "sunday & sunday & monday"},
expOk: true,
expWeekly: task.Weekly{
Start: start,
Weekdays: task.Weekdays{
time.Monday,
time.Sunday,
},
},
},
{
name: "one unknown",
input: []string{"weekly", "sunday & someday"},
expOk: true,
expWeekly: task.Weekly{
Start: start,
Weekdays: task.Weekdays{
time.Sunday,
},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
weekly, ok := task.ParseWeekly(start, tc.input)
test.Equals(t, tc.expOk, ok)
if tc.expOk {
test.Equals(t, tc.expWeekly, weekly)
}
})
}
}
func TestWeekly(t *testing.T) { func TestWeekly(t *testing.T) {
weekly := task.Weekly{ weekly := task.Weekly{
Start: task.NewDate(2021, 1, 31), // a sunday Start: task.NewDate(2021, 1, 31), // a sunday
Weekday: time.Wednesday, Weekdays: task.Weekdays{
time.Monday,
time.Wednesday,
time.Thursday,
},
} }
weeklyStr := "2021-01-31 (sunday), weekly, wednesday" weeklyStr := "2021-01-31 (sunday), weekly, monday & wednesday & thursday"
t.Run("parse", func(t *testing.T) { t.Run("parse", func(t *testing.T) {
test.Equals(t, weekly, task.NewRecurrer(weeklyStr)) test.Equals(t, weekly, task.NewRecurrer(weeklyStr))
@ -76,14 +174,19 @@ func TestWeekly(t *testing.T) {
date: task.NewDate(2021, 1, 27), // a wednesday date: task.NewDate(2021, 1, 27), // a wednesday
}, },
{ {
name: "wrong weekday", name: "right weekday",
date: task.NewDate(2021, 2, 1), // a monday date: task.NewDate(2021, 2, 1), // a monday
exp: true,
}, },
{ {
name: "right day", name: "another right day",
date: task.NewDate(2021, 2, 3), // a wednesday date: task.NewDate(2021, 2, 3), // a wednesday
exp: true, exp: true,
}, },
{
name: "wrong weekday",
date: task.NewDate(2021, 2, 5), // a friday
},
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
test.Equals(t, tc.exp, weekly.RecursOn(tc.date)) test.Equals(t, tc.exp, weekly.RecursOn(tc.date))