]> git.giacomolli.uk - onechat/blobdiff - schedule_cron.go
Separate multiline Go statements
[onechat] / schedule_cron.go
index 78ec90b49362b1c570d7568d565061a2267b2508..3255f99571fdbd968f08352c0c7cbd451012f8c0 100644 (file)
@@ -53,60 +53,6 @@ var scheduleCronFields = []struct {
        },
 }
 
-// scheduleParseCron parses a cron expression of five fields: minute, hour, day
-// of month, month, and day of week, where 0 and 7 are Sunday. A field is a
-// comma list of items, and an item is *, N, or N-M, where * and N-M can have a
-// step /S. Names, macros, the characters ? L W #, and N/S are errors.
-//
-// It refuses an expression whose runs can be less than scheduleCronGap minutes
-// apart. The check takes the wall times of one day from the hour and the minute
-// fields, and the gap across midnight as if the next day matches too. It also
-// refuses an expression that matches no day in scheduleCronYears years, such as
-// one on February 30.
-func scheduleParseCron(expression string) (ScheduleCron, error) {
-       fields := strings.Fields(expression)
-       if len(fields) != len(scheduleCronFields) {
-               return ScheduleCron{}, fmt.Errorf("the cron expression %q has %d fields, not 5: minute, hour, day of month, month, day of week", expression, len(fields))
-       }
-
-       var sets [5]uint64
-       for index, field := range fields {
-               set, err := scheduleCronField(field, scheduleCronFields[index].low, scheduleCronFields[index].high)
-               if err != nil {
-                       return ScheduleCron{}, fmt.Errorf("the %s field of %q: %w", scheduleCronFields[index].name, expression, err)
-               }
-
-               sets[index] = set
-       }
-
-       c := ScheduleCron{
-               minutes:     sets[0],
-               hours:       sets[1],
-               days:        sets[2],
-               months:      sets[3],
-               weekdays:    sets[4],
-               anyDay:      strings.HasPrefix(fields[2], "*"),
-               anyWeekday:  strings.HasPrefix(fields[4], "*"),
-               description: strings.Join(fields, " "),
-       }
-
-       if c.weekdays&(1<<7) != 0 {
-               c.weekdays |= 1
-       }
-
-       if c.gap() < scheduleCronGap {
-               return ScheduleCron{}, fmt.Errorf("the runs of %q can be less than %d minutes apart: the runs of a schedule must be at least %d minutes apart", expression, scheduleCronGap, scheduleCronGap)
-       }
-
-       start := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)
-       _, found := c.Next(start)
-       if !found {
-               return ScheduleCron{}, fmt.Errorf("the cron expression %q matches no day in %d years", expression, scheduleCronYears)
-       }
-
-       return c, nil
-}
-
 // ScheduleCron is a parsed cron expression. Each set holds a bit for each value
 // of its field. anyDay and anyWeekday tell that the day field or the weekday
 // field starts with *. When neither does, a day matches when either field
@@ -146,6 +92,7 @@ func (c ScheduleCron) Next(after time.Time) (time.Time, bool) {
                // one day are not always in order. The first time is the
                // smallest one.
                var next time.Time
+
                for hour := range 24 {
                        if c.hours&(1<<hour) == 0 {
                                continue
@@ -190,6 +137,7 @@ func (c ScheduleCron) day(date time.Time) bool {
 // and the minute fields, also across midnight.
 func (c ScheduleCron) gap() int {
        var times []int
+
        for hour := range 24 {
                for minute := range 60 {
                        if c.hours&(1<<hour) != 0 && c.minutes&(1<<minute) != 0 {
@@ -206,6 +154,61 @@ func (c ScheduleCron) gap() int {
        return gap
 }
 
+// scheduleParseCron parses a cron expression of five fields: minute, hour, day
+// of month, month, and day of week, where 0 and 7 are Sunday. A field is a
+// comma list of items, and an item is *, N, or N-M, where * and N-M can have a
+// step /S. Names, macros, the characters ? L W #, and N/S are errors.
+//
+// It refuses an expression whose runs can be less than scheduleCronGap minutes
+// apart. The check takes the wall times of one day from the hour and the minute
+// fields, and the gap across midnight as if the next day matches too. It also
+// refuses an expression that matches no day in scheduleCronYears years, such as
+// one on February 30.
+func scheduleParseCron(expression string) (ScheduleCron, error) {
+       fields := strings.Fields(expression)
+       if len(fields) != len(scheduleCronFields) {
+               return ScheduleCron{}, fmt.Errorf("the cron expression %q has %d fields, not 5: minute, hour, day of month, month, day of week", expression, len(fields))
+       }
+
+       var sets [5]uint64
+
+       for index, field := range fields {
+               set, err := scheduleCronField(field, scheduleCronFields[index].low, scheduleCronFields[index].high)
+               if err != nil {
+                       return ScheduleCron{}, fmt.Errorf("the %s field of %q: %w", scheduleCronFields[index].name, expression, err)
+               }
+
+               sets[index] = set
+       }
+
+       c := ScheduleCron{
+               minutes:     sets[0],
+               hours:       sets[1],
+               days:        sets[2],
+               months:      sets[3],
+               weekdays:    sets[4],
+               anyDay:      strings.HasPrefix(fields[2], "*"),
+               anyWeekday:  strings.HasPrefix(fields[4], "*"),
+               description: strings.Join(fields, " "),
+       }
+
+       if c.weekdays&(1<<7) != 0 {
+               c.weekdays |= 1
+       }
+
+       if c.gap() < scheduleCronGap {
+               return ScheduleCron{}, fmt.Errorf("the runs of %q can be less than %d minutes apart: the runs of a schedule must be at least %d minutes apart", expression, scheduleCronGap, scheduleCronGap)
+       }
+
+       start := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)
+       _, found := c.Next(start)
+       if !found {
+               return ScheduleCron{}, fmt.Errorf("the cron expression %q matches no day in %d years", expression, scheduleCronYears)
+       }
+
+       return c, nil
+}
+
 // scheduleWallTime returns the time of a wall time in location. A wall time
 // that the clock skips, at the start of daylight saving time, is later by the
 // length of the skip: 02:30 becomes 03:30 when the clock goes from 02:00 to
@@ -235,11 +238,13 @@ func scheduleWallTime(year int, month time.Month, day, hour, minute int, locatio
 // high.
 func scheduleCronField(field string, low, high int) (uint64, error) {
        var set uint64
+
        for item := range strings.SplitSeq(field, ",") {
                base, stepText, stepped := strings.Cut(item, "/")
                step := 1
                if stepped {
                        var err error
+
                        step, err = scheduleCronNumber(stepText, 1, high)
                        if err != nil {
                                return 0, fmt.Errorf("the step of %q: %w", item, err)
@@ -252,6 +257,7 @@ func scheduleCronField(field string, low, high int) (uint64, error) {
                case base == "*":
                case ranged:
                        var err error
+
                        first, err = scheduleCronNumber(startText, low, high)
                        if err == nil {
                                last, err = scheduleCronNumber(endText, low, high)
@@ -268,6 +274,7 @@ func scheduleCronField(field string, low, high int) (uint64, error) {
                        return 0, fmt.Errorf("%q has a step after a single value: write a range N-M/S or */S", item)
                default:
                        var err error
+
                        first, err = scheduleCronNumber(base, low, high)
                        if err != nil {
                                return 0, err