gte/internal/task/recur.go

269 lines
4.5 KiB
Go
Raw Normal View History

2021-01-23 12:26:26 +01:00
package task
2021-01-31 10:01:03 +01:00
import (
2021-01-31 12:11:02 +01:00
"fmt"
2021-02-03 07:27:22 +01:00
"strconv"
2021-01-31 10:01:03 +01:00
"strings"
"time"
)
2021-01-23 13:43:28 +01:00
2021-01-23 12:26:26 +01:00
type Recurrer interface {
2021-01-31 10:01:03 +01:00
RecursOn(date Date) bool
String() string
}
func NewRecurrer(recurStr string) Recurrer {
terms := strings.Split(recurStr, ", ")
2021-01-31 12:11:02 +01:00
if len(terms) < 2 {
2021-01-31 10:01:03 +01:00
return nil
}
2021-01-31 12:11:02 +01:00
start := NewDateFromString(terms[0])
if start.IsZero() {
2021-01-31 10:01:03 +01:00
return nil
}
2021-01-31 12:11:02 +01:00
terms = terms[1:]
if recur, ok := ParseDaily(start, terms); ok {
return recur
}
if recur, ok := ParseWeekly(start, terms); ok {
return recur
}
if recur, ok := ParseBiweekly(start, terms); ok {
return recur
2021-01-31 10:01:03 +01:00
}
2021-02-03 07:27:22 +01:00
if recur, ok := ParseEveryNWeeks(start, terms); ok {
return recur
}
2021-02-03 10:25:25 +01:00
if recur, ok := ParseEveryNMonths(start, terms); ok {
return recur
}
2021-01-31 10:01:03 +01:00
2021-01-31 12:11:02 +01:00
return nil
}
type Daily struct {
Start Date
}
func ParseDaily(start Date, terms []string) (Recurrer, bool) {
if len(terms) < 1 {
return nil, false
2021-01-31 10:01:03 +01:00
}
2021-01-31 12:11:02 +01:00
if terms[0] != "daily" {
return nil, false
2021-01-31 10:01:03 +01:00
}
2021-01-31 12:11:02 +01:00
return Daily{
Start: start,
}, true
}
func (d Daily) RecursOn(date Date) bool {
return date.Equal(d.Start) || date.After(d.Start)
}
func (d Daily) String() string {
return fmt.Sprintf("%s, daily", d.Start.String())
2021-01-23 12:26:26 +01:00
}
type Weekly struct {
2021-02-02 08:47:58 +01:00
Start Date
Weekdays Weekdays
2021-01-31 10:01:03 +01:00
}
2021-02-02 08:47:58 +01:00
// yyyy-mm-dd, weekly, wednesday & saturday & sunday
2021-01-31 12:11:02 +01:00
func ParseWeekly(start Date, terms []string) (Recurrer, bool) {
if len(terms) < 2 {
return nil, false
}
if terms[0] != "weekly" {
return nil, false
}
2021-02-02 08:47:58 +01:00
wds := Weekdays{}
for _, wdStr := range strings.Split(terms[1], "&") {
wd, ok := ParseWeekday(wdStr)
if !ok {
continue
}
wds = append(wds, wd)
}
if len(wds) == 0 {
2021-01-31 12:11:02 +01:00
return nil, false
}
return Weekly{
2021-02-02 08:47:58 +01:00
Start: start,
Weekdays: wds.Unique(),
2021-01-31 12:11:02 +01:00
}, true
}
2021-01-31 10:01:03 +01:00
func (w Weekly) RecursOn(date Date) bool {
2021-01-31 12:11:02 +01:00
if w.Start.After(date) {
2021-01-31 10:01:03 +01:00
return false
}
2021-02-02 08:47:58 +01:00
for _, wd := range w.Weekdays {
if wd == date.Weekday() {
return true
}
}
return false
2021-01-23 12:26:26 +01:00
}
2021-01-31 12:11:02 +01:00
func (w Weekly) String() string {
2021-02-02 08:47:58 +01:00
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))
2021-01-31 12:11:02 +01:00
}
2021-01-23 13:43:28 +01:00
2021-01-31 12:11:02 +01:00
type Biweekly struct {
Start Date
Weekday time.Weekday
2021-01-23 12:26:26 +01:00
}
2021-01-31 12:11:02 +01:00
// yyyy-mm-dd, biweekly, wednesday
func ParseBiweekly(start Date, terms []string) (Recurrer, bool) {
if len(terms) < 2 {
return nil, false
}
if terms[0] != "biweekly" {
return nil, false
}
wd, ok := ParseWeekday(terms[1])
if !ok {
return nil, false
}
return Biweekly{
Start: start,
Weekday: wd,
}, true
2021-01-31 10:01:03 +01:00
}
2021-01-31 12:11:02 +01:00
func (b Biweekly) RecursOn(date Date) bool {
if b.Start.After(date) {
return false
}
if b.Weekday != date.Weekday() {
return false
}
// find first
tDate := b.Start
for {
if tDate.Weekday() == b.Weekday {
break
}
tDate = tDate.AddDays(1)
}
// add weeks
for {
switch {
case tDate.Equal(date):
return true
case tDate.After(date):
return false
}
tDate = tDate.AddDays(14)
}
2021-01-23 12:26:26 +01:00
}
2021-01-31 12:11:02 +01:00
func (b Biweekly) String() string {
return fmt.Sprintf("%s, biweekly, %s", b.Start.String(), strings.ToLower(b.Weekday.String()))
2021-01-23 12:26:26 +01:00
}
2021-02-03 07:27:22 +01:00
type EveryNWeeks struct {
Start Date
N int
}
// yyyy-mm-dd, every 3 weeks
func ParseEveryNWeeks(start Date, terms []string) (Recurrer, bool) {
2021-02-03 10:25:25 +01:00
if len(terms) != 1 {
2021-02-03 07:27:22 +01:00
return nil, false
}
terms = strings.Split(terms[0], " ")
if len(terms) != 3 || terms[0] != "every" || terms[2] != "weeks" {
return nil, false
}
n, err := strconv.Atoi(terms[1])
if err != nil || n < 1 {
return nil, false
}
return EveryNWeeks{
Start: start,
N: n,
}, true
}
func (enw EveryNWeeks) RecursOn(date Date) bool {
if enw.Start.After(date) {
return false
}
if enw.Start.Equal(date) {
return true
}
intervalDays := enw.N * 7
return enw.Start.DaysBetween(date)%intervalDays == 0
}
func (enw EveryNWeeks) String() string {
return fmt.Sprintf("%s, every %d weeks", enw.Start.String(), enw.N)
}
2021-02-03 10:25:25 +01:00
type EveryNMonths struct {
Start Date
N int
}
// yyyy-mm-dd, every 3 months
func ParseEveryNMonths(start Date, terms []string) (Recurrer, bool) {
if len(terms) != 1 {
return nil, false
}
terms = strings.Split(terms[0], " ")
if len(terms) != 3 || terms[0] != "every" || terms[2] != "months" {
return nil, false
}
n, err := strconv.Atoi(terms[1])
if err != nil {
return nil, false
}
return EveryNMonths{
Start: start,
N: n,
}, true
}
func (enm EveryNMonths) RecursOn(date Date) bool {
if enm.Start.After(date) {
return false
}
return enm.Start.Day() == date.Day()
}
func (enm EveryNMonths) String() string {
return fmt.Sprintf("%s, every %d months", enm.Start.String(), enm.N)
}