From 74b6d40774fff2b4bac49d57f2e831682ab17107 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Sat, 3 May 2025 21:24:03 -0700 Subject: [PATCH] Refactor some on-spec logic into helper types --- core/cron.go | 79 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/core/cron.go b/core/cron.go index 2e3f289..4287093 100644 --- a/core/cron.go +++ b/core/cron.go @@ -80,17 +80,63 @@ var weekdays = map[string]time.Weekday{ "Sat": time.Saturday, } +type ymd struct { + Year int + Month time.Month + Day int +} + +type onSpecDate interface { + getDates(base time.Time) []ymd +} + +type onSpecWeekday struct { + weekday time.Weekday +} + +func (date *onSpecWeekday) getDates(base time.Time) (dates []ymd) { + // For a weekday, add the next of that weekday 0-6 days ahead and 7-13 days ahead. + // The first date ensures that we don't miss multiple updates on the same day of the week + // (e.g. "on Sun at 06:00,18:00") and the second date ensures that we don't get stuck with + // only a date in the past (e.g. "on Sun at 02:00" when base is Sun 03:00). + daysForward := (int(date.weekday) - int(base.Weekday()) + 7) % 7 // value in [0,6] + day0_6 := base.AddDate(0, 0, daysForward) + dates = append(dates, ymd{day0_6.Year(), day0_6.Month(), day0_6.Day()}) + day7_13 := base.AddDate(0, 0, daysForward+7) + dates = append(dates, ymd{day7_13.Year(), day7_13.Month(), day7_13.Day()}) + return +} + +type onSpecEveryMonth struct { + day int +} + +func (date *onSpecEveryMonth) getDates(base time.Time) (dates []ymd) { + // For every-month, add the date for the current month and the date for the next month. + dates = append(dates, ymd{base.Year(), base.Month(), date.day}) + nextMonth := base.AddDate(0, 1, 0) + dates = append(dates, ymd{nextMonth.Year(), nextMonth.Month(), date.day}) + return +} + +type onSpecMonthDay struct { + month int + day int +} + +func (date *onSpecMonthDay) getDates(base time.Time) (dates []ymd) { + // For month/day, add the date for the base year and the next year. + dates = append(dates, ymd{base.Year(), time.Month(date.month), date.day}) + dates = append(dates, ymd{base.Year() + 1, time.Month(date.month), date.day}) + return +} + // Get the next instances of the on-spec times after the base time. // An on-spec is in the pattern DOW[,DOW[...]] where DOW is an abbreviated weekday // or M/D[,M/D[...]] where M/D is a month and day. // As a special case, "*/N" matches the Nth day of every month. // An on-spec may be followed by an at-spec; otherwise, "at 00:00" is implied. func parseOnSpec(base time.Time, onSpec string) (nextUpdates []time.Time, err error) { - type Date struct { - Year int - Month time.Month - Day int - } type Time struct { Hour int Minute int @@ -111,38 +157,27 @@ func parseOnSpec(base time.Time, onSpec string) (nextUpdates []time.Time, err er atTimes = append(atTimes, Time{hour, minute}) } - var dates []Date + var dates []ymd for _, daySpec := range strings.Split(onSpec, ",") { + var date onSpecDate if weekday, ok := weekdays[daySpec]; ok { - // For a weekday, add the next of that weekday 0-6 days ahead and 7-13 days ahead. - // The first date ensures that we don't miss multiple updates on the same day of the week - // (e.g. "on Sun at 06:00,18:00") and the second date ensures that we don't get stuck with - // only a date in the past (e.g. "on Sun at 02:00" when base is Sun 03:00). - daysForward := (int(weekday) - int(base.Weekday()) + 7) % 7 // value in [0,6] - day0_6 := base.AddDate(0, 0, daysForward) - dates = append(dates, Date{day0_6.Year(), day0_6.Month(), day0_6.Day()}) - day7_13 := base.AddDate(0, 0, daysForward+7) - dates = append(dates, Date{day7_13.Year(), day7_13.Month(), day7_13.Day()}) + date = &onSpecWeekday{weekday} } else if strings.HasPrefix(daySpec, "*/") { - // For every-month, add the date for the current month and the date for the next month. var day int _, err := fmt.Sscanf(daySpec, "*/%d", &day) if err != nil { return nil, fmt.Errorf("could not parse month/day %s: %v", daySpec, err) } - dates = append(dates, Date{base.Year(), base.Month(), day}) - nextMonth := base.AddDate(0, 1, 0) - dates = append(dates, Date{nextMonth.Year(), nextMonth.Month(), day}) + date = &onSpecEveryMonth{day} } else { - // For month/day, add the date for the base year and the next year. var month, day int _, err := fmt.Sscanf(daySpec, "%d/%d", &month, &day) if err != nil { return nil, fmt.Errorf("could not parse month/day %s: %v", daySpec, err) } - dates = append(dates, Date{base.Year(), time.Month(month), day}) - dates = append(dates, Date{base.Year() + 1, time.Month(month), day}) + date = &onSpecMonthDay{month, day} } + dates = append(dates, date.getDates(base)...) } // Now, for each date, create a datetime based on the at-spec.