141 lines
2.8 KiB
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)
|
|
}
|