-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcronrange.go
87 lines (80 loc) · 2.33 KB
/
cronrange.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// Package cronrange provides a crontab-like format for expressing time ranges.
// Unlike traditional crontab that defines specific moments in time, cronrange
// defines time periods when something should be active.
//
// Format: `time dow dom month`
//
// Where:
// - time: Time range in 24h format (HH:MM[:SS]-HH:MM[:SS]) or * for all day
// - dow: Day of week (0-6, where 0=Sunday)
// - dom: Day of month (1-31)
// - month: Month (1-12)
//
// Each field (except time) supports single values, lists (1,3,5), ranges (1-5)
// and asterisk (*) for any/all values. Multiple rules can be combined using semicolons.
//
// Examples:
//
// 17:20-21:35 1-5 * * # Weekdays from 5:20 PM to 9:35 PM
// 11:20:12-19:25:18 1-5 * * # Weekdays from 11:20:12 AM to 7:35:18 PM
// * 0,6 * * # All day on weekends
// 09:00-17:00 1-5 * 4-9 # Weekdays 9 AM to 5 PM, April through September
// 12:00-13:00 * 1,15 * # Noon-1 PM on 1st and 15th of every month
package cronrange
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
"time"
)
// Parse parses a cronrange expression and returns a Rule slice
func Parse(expr string) ([]Rule, error) {
rules := strings.Split(expr, ";")
result := make([]Rule, 0, len(rules))
for _, r := range rules {
rule, err := parseRule(strings.TrimSpace(r))
if err != nil {
return nil, fmt.Errorf("invalid rule %q: %w", r, err)
}
result = append(result, rule)
}
return result, nil
}
// ParseFromReader parses a cronrange expression from a reader and returns a Rule slice
func ParseFromReader(rdr io.Reader) ([]Rule, error) {
buf, err := io.ReadAll(rdr)
if err != nil {
return nil, fmt.Errorf("can't read from reader: %w", err)
}
if len(buf) == 0 {
return []Rule{}, nil
}
var res []Rule
scanner := bufio.NewScanner(bytes.NewReader(buf))
for scanner.Scan() {
rule := strings.TrimSpace(scanner.Text())
if rule == "" {
continue
}
r, err := Parse(rule)
if err != nil {
return nil, fmt.Errorf("invalid rule %q: %w", rule, err)
}
res = append(res, r...)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading input: %w", err)
}
return res, nil
}
// Match checks if the given time matches any of the rules
func Match(rules []Rule, t time.Time) bool {
for _, rule := range rules {
if rule.matches(t) {
return true
}
}
return false
}