-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathweekday.go
141 lines (122 loc) · 3.21 KB
/
weekday.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
package rrule
import (
"fmt"
"time"
)
// QualifiedWeekday can represent a day of the week, or a certain instance
// of that day of the week.
type QualifiedWeekday struct {
// N, when non-zero, says which instance of the weekday relative to
// some greater duration. -3 would be "third from the last".
N int
WD time.Weekday
}
func (wd QualifiedWeekday) String() string {
wdStr := WeekdayString(wd.WD)
if wd.N == 0 {
return wdStr
}
return fmt.Sprintf("%d%s", wd.N, wdStr)
}
// WeekdayString returns a weekday formatted as the two-letter string used in RFC5545.
func WeekdayString(wd time.Weekday) string {
var wdStr string
switch wd {
case time.Sunday:
wdStr = "SU"
case time.Monday:
wdStr = "MO"
case time.Tuesday:
wdStr = "TU"
case time.Wednesday:
wdStr = "WE"
case time.Thursday:
wdStr = "TH"
case time.Friday:
wdStr = "FR"
case time.Saturday:
wdStr = "SA"
}
return wdStr
}
// weekdaysInyear returns either all the weekdays in a year, or a slice with one time, which is
// the nth weekday specified by wd. If wd.N is invalid (e.g. no 53rd week in a year), ib defines
// whether to return an empty set (OmitInvalid), the last weekday of the given year, or the first
// weekday of the following year.
func weekdaysInYear(t time.Time, wd QualifiedWeekday, ib InvalidBehavior) []time.Time {
allWDs := make([]time.Time, 0, 5)
// start on first of year
day := time.Date(t.Year(), 1, 1, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
// scan til the first relevant weekday of the year
for day.Weekday() != wd.WD {
day = day.AddDate(0, 0, 1)
}
// scan over every week of the year
for {
allWDs = append(allWDs, day)
day = day.AddDate(0, 0, 7)
if day.Year() != t.Year() {
break
}
}
if wd.N == 0 {
// no index specified, return all.
return allWDs
}
if wd.N > 0 {
// positive index specified. count to the correct instance
if wd.N > len(allWDs) {
switch ib {
case OmitInvalid:
return nil
case PrevInvalid:
idx := len(allWDs) - 1
return allWDs[idx:idx]
case NextInvalid:
return []time.Time{allWDs[len(allWDs)-1].AddDate(0, 0, 7)}
}
}
return []time.Time{allWDs[wd.N-1]}
}
// negative index specified. count backwards to the correct instance
// an example of the following logic:
//
// -1 in a list of 4 ..
// - the index becomes 3, which is the last index
// which corresponds to "the last instance"
// -3 in a list of 4 ..
// - the index becomes 1, which is the third from last
// -7 in a list of 4 ..
// - the index becomes -3, which should trigger invalid behavior
idx := len(allWDs) + wd.N
if idx < 0 || idx > len(allWDs) {
switch ib {
case OmitInvalid:
return nil
case PrevInvalid:
return []time.Time{allWDs[0].AddDate(0, 0, -7)}
case NextInvalid:
return allWDs[0:0]
}
}
return []time.Time{allWDs[idx]}
}
func backToWeekday(t time.Time, day time.Weekday) time.Time {
for t.Weekday() != day {
t = t.AddDate(0, 0, -1)
}
return t
}
func forwardToWeekday(t time.Time, day time.Weekday) time.Time {
for t.Weekday() != day {
t = t.AddDate(0, 0, 1)
}
return t
}
func plainWeekdays(q []QualifiedWeekday) []time.Weekday {
wd := make([]time.Weekday, len(q))
for i, qwd := range q {
wd[i] = qwd.WD
}
return wd
}