pttodo/pttodo/schedule.go

141 lines
2.8 KiB
Go

package pttodo
import (
"encoding/json"
"math"
"regexp"
"strconv"
"time"
cron "github.com/robfig/cron/v3"
)
type Schedule string
func (schedule Schedule) MarshalJSON() ([]byte, error) {
return json.Marshal(string(schedule))
}
func (schedule *Schedule) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, (*string)(schedule))
}
func (schedule Schedule) Next(t time.Time) (time.Time, error) {
scheduler := schedulerFactory(string(schedule))
return scheduler.next(t)
}
func (schedule Schedule) isFixedFuture() bool {
if !schedule.isFixed() {
return false
}
return schedule.isFuture()
}
func (schedule Schedule) isFixed() bool {
if schedule.empty() {
return false
}
scheduler := schedulerFactory(string(schedule))
switch scheduler.(type) {
case scheduleEZDate, scheduleDue:
default:
return false
}
return true
}
func (schedule Schedule) isFuture() bool {
if schedule.empty() {
return false
}
scheduler := schedulerFactory(string(schedule))
t, err := scheduler.next(time.Now())
if err != nil {
return false
}
return time.Now().Before(t)
}
func (schedule Schedule) empty() bool {
return string(schedule) == ""
}
type scheduler interface {
next(time.Time) (time.Time, error)
}
func schedulerFactory(s string) scheduler {
if s == "" {
return scheduleNever{}
} else if scheduleDurationPattern.MatchString(s) {
return scheduleDuration(s)
} else if scheduleDuePattern.MatchString(s) {
n, _ := strconv.Atoi(s)
return scheduleDue(n)
} else if scheduleEZDatePattern.MatchString(s) {
return scheduleEZDate(s)
}
return scheduleCron(s)
}
// * * * * *
type scheduleCron string
func (c scheduleCron) next(t time.Time) (time.Time, error) {
schedule, err := cron.ParseStandard(string(c))
if err != nil {
return time.Time{}, err
}
return schedule.Next(t), nil
}
// * * * * *
type scheduleNever struct{}
var never = time.Date(
math.MaxInt32,
1,
1,
1,
1,
1,
1,
time.UTC,
)
func (cron scheduleNever) next(time.Time) (time.Time, error) {
return never, nil
}
// 4h5m
type scheduleDuration string
var scheduleDurationPattern = regexp.MustCompile(`^([0-9]+[a-z])+$`)
func (dur scheduleDuration) next(t time.Time) (time.Time, error) {
d, err := time.ParseDuration(string(dur))
return t.Add(d), err
}
// 123
type scheduleDue int64
var scheduleDuePattern = regexp.MustCompile(`^[0-9]+$`)
func (due scheduleDue) next(time.Time) (time.Time, error) {
return TS(due).time(), nil
}
// 2022-01-01
type scheduleEZDate string
var scheduleEZDatePattern = regexp.MustCompile(`^[0-9]{4}-[0-9]{2}-[0-9]{2}(T[0-9]{2})?$`)
func (ezdate scheduleEZDate) next(time.Time) (time.Time, error) {
if len(ezdate) == len("20xx-xx-xxTxx") {
return time.ParseInLocation("2006-01-02T15", string(ezdate), time.Local)
}
return time.ParseInLocation("2006-01-02", string(ezdate), time.Local)
}