forked from gorhill/cronexpr
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcronexpr_next.go
155 lines (139 loc) · 4.19 KB
/
cronexpr_next.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*!
* Copyright 2013 Raymond Hill
*
* Modifications 2020 - HashiCorp
*
* Project: github.com/gorhill/cronexpr
* File: cronexpr_next.go
* Version: 1.0
* License: pick the one which suits you :
* GPL v3 see <https://www.gnu.org/licenses/gpl.html>
* APL v2 see <http://www.apache.org/licenses/LICENSE-2.0>
*
*/
package cronexpr
/******************************************************************************/
import (
"sort"
"time"
)
/******************************************************************************/
var dowNormalizedOffsets = [][]int{
{1, 8, 15, 22, 29},
{2, 9, 16, 23, 30},
{3, 10, 17, 24, 31},
{4, 11, 18, 25},
{5, 12, 19, 26},
{6, 13, 20, 27},
{7, 14, 21, 28},
}
/******************************************************************************/
func (expr *Expression) calculateActualDaysOfMonth(year, month int) []int {
actualDaysOfMonthMap := make(map[int]bool)
firstDayOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)
lastDayOfMonth := firstDayOfMonth.AddDate(0, 1, -1)
// As per crontab man page (http://linux.die.net/man/5/crontab#):
// "The day of a command's execution can be specified by two
// "fields - day of month, and day of week. If both fields are
// "restricted (ie, aren't *), the command will be run when
// "either field matches the current time"
// If both fields are not restricted, all days of the month are a hit
if expr.daysOfMonthRestricted == false && expr.daysOfWeekRestricted == false {
return genericDefaultList[1 : lastDayOfMonth.Day()+1]
}
// day-of-month != `*`
if expr.daysOfMonthRestricted {
// Last day of month
if expr.lastDayOfMonth {
actualDaysOfMonthMap[lastDayOfMonth.Day()] = true
}
// Last work day of month
if expr.lastWorkdayOfMonth {
actualDaysOfMonthMap[workdayOfMonth(lastDayOfMonth, lastDayOfMonth)] = true
}
// Days of month
for v := range expr.daysOfMonth {
// Ignore days beyond end of month
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[v] = true
}
}
// Work days of month
// As per Wikipedia: month boundaries are not crossed.
for v := range expr.workdaysOfMonth {
// Ignore days beyond end of month
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[workdayOfMonth(firstDayOfMonth.AddDate(0, 0, v-1), lastDayOfMonth)] = true
}
}
}
// day-of-week != `*`
if expr.daysOfWeekRestricted {
// How far first sunday is from first day of month
offset := 7 - int(firstDayOfMonth.Weekday())
// days of week
// offset : (7 - day_of_week_of_1st_day_of_month)
// target : 1 + (7 * week_of_month) + (offset + day_of_week) % 7
for v := range expr.daysOfWeek {
w := dowNormalizedOffsets[(offset+v)%7]
actualDaysOfMonthMap[w[0]] = true
actualDaysOfMonthMap[w[1]] = true
actualDaysOfMonthMap[w[2]] = true
actualDaysOfMonthMap[w[3]] = true
if len(w) > 4 && w[4] <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[w[4]] = true
}
}
// days of week of specific week in the month
// offset : (7 - day_of_week_of_1st_day_of_month)
// target : 1 + (7 * week_of_month) + (offset + day_of_week) % 7
for v := range expr.specificWeekDaysOfWeek {
v = 1 + 7*(v/7) + (offset+v)%7
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[v] = true
}
}
// Last days of week of the month
lastWeekOrigin := firstDayOfMonth.AddDate(0, 1, -7)
offset = 7 - int(lastWeekOrigin.Weekday())
for v := range expr.lastWeekDaysOfWeek {
v = lastWeekOrigin.Day() + (offset+v)%7
if v <= lastDayOfMonth.Day() {
actualDaysOfMonthMap[v] = true
}
}
}
return toList(actualDaysOfMonthMap)
}
func workdayOfMonth(targetDom, lastDom time.Time) int {
// If saturday, then friday
// If sunday, then monday
dom := targetDom.Day()
dow := targetDom.Weekday()
if dow == time.Saturday {
if dom > 1 {
dom -= 1
} else {
dom += 2
}
} else if dow == time.Sunday {
if dom < lastDom.Day() {
dom += 1
} else {
dom -= 2
}
}
return dom
}
func sortContains(a []int, x int) bool {
i := sort.SearchInts(a, x)
return i < len(a) && a[i] == x
}
func timeZoneInDay(t time.Time) bool {
if t.Location() == time.UTC {
return false
}
_, off := t.AddDate(0, 0, -1).Zone()
_, ndoff := t.AddDate(0, 0, 1).Zone()
return off != ndoff
}