...
1
2
3
4
5
6
7
8
9
10
11
12
13
14 package scheduler
15
16 import (
17 "fmt"
18 "time"
19
20 "github.com/robfig/cron/v3"
21
22 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
23 )
24
25 const (
26
27 starBit = 1 << 63
28 )
29
30
31 func LastTime(spec v1alpha1.SchedulerSpec, now time.Time) (*time.Time, error) {
32 scheduler, err := cron.ParseStandard(spec.Cron)
33 if err != nil {
34 return nil, fmt.Errorf("fail to parse runner rule %s, %v", spec.Cron, err)
35 }
36 var last time.Time
37 if cronSpec, ok := scheduler.(*cron.SpecSchedule); ok {
38 scheduleLast := &cusSchedule{cronSpec}
39 last = scheduleLast.Last(now)
40 } else if cronSpec, ok := scheduler.(cron.ConstantDelaySchedule); ok {
41 scheduleLast := &cusConstantDelaySchedule{cronSpec}
42 last = scheduleLast.Last(now)
43 } else {
44 return nil, fmt.Errorf("assert cron spec failed")
45 }
46 return &last, nil
47 }
48
49 type cusConstantDelaySchedule struct {
50 cron.ConstantDelaySchedule
51 }
52
53
54
55 func (s cusConstantDelaySchedule) Last(t time.Time) time.Time {
56 return t
57 }
58
59 type cusSchedule struct {
60 *cron.SpecSchedule
61 }
62
63
64
65
66 func (s *cusSchedule) Last(t time.Time) time.Time {
67
68
69
70
71
72
73
74
75
76
77
78
79 origLocation := t.Location()
80 loc := s.Location
81 if loc == time.Local {
82 loc = t.Location()
83 }
84 if s.Location != time.Local {
85 t = t.In(s.Location)
86 }
87
88
89 yearLimit := t.Year() - 5
90
91 WRAP:
92 if t.Year() < yearLimit {
93 return time.Time{}
94 }
95
96
97
98 for 1<<uint(t.Month())&s.Month == 0 {
99 t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc).Add(-1 * time.Second)
100 if t.Month() == time.December {
101 goto WRAP
102 }
103 }
104
105
106 for !dayMatches(s, t) {
107 finalDay := time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc).Add(-1 * time.Second).Day()
108 t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc).Add(-1 * time.Second)
109 if t.Day() == finalDay {
110 goto WRAP
111 }
112 }
113
114 for 1<<uint(t.Hour())&s.Hour == 0 {
115 t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc).Add(-1 * time.Second)
116 if t.Hour() == 23 {
117 goto WRAP
118 }
119 }
120
121 for 1<<uint(t.Minute())&s.Minute == 0 {
122 t = t.Truncate(time.Minute).Add(-1 * time.Second)
123 if t.Minute() == 59 {
124 goto WRAP
125 }
126 }
127
128 for 1<<uint(t.Second())&s.Second == 0 {
129 t = t.Add(-1 * time.Second)
130 if t.Second() == 59 {
131 goto WRAP
132 }
133 }
134
135 return t.In(origLocation)
136 }
137
138
139
140 func dayMatches(s *cusSchedule, t time.Time) bool {
141 var (
142 domMatch bool = 1<<uint(t.Day())&s.Dom > 0
143 dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
144 )
145 if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
146 return domMatch && dowMatch
147 }
148 return domMatch || dowMatch
149 }
150