-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdates.go
226 lines (191 loc) · 6.28 KB
/
dates.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package dates
import (
"log/slog"
"time"
)
// Default start and end dates for the week
// can be changed if the definition of a week is different
// for example in the US, the week starts on Sunday
// a helper library for date (no time) calculations
// for example, getting the start and end dates of the current week, previous week, etc.
const (
OneDay = time.Hour * 24 // duration for one day 24 hours
OneWeek = OneDay * 7 // duration for one week 7 days or 168 hours
StartDefault = time.Monday // default weekday start of the week
EndDefault = time.Sunday // default weekday end of the week
)
type Week struct {
weekStart time.Weekday // starting weekday of the week
weekEnd time.Weekday // ending weekday of the week
}
// New returns a new Week with the given start and end days.
// First value should be start of week, second value should be end of week.
// For example New(time.Monday, time.Sunday), extra values are ignored
// leave empty to use the default week start and end weekdays i.e., NewWeek()
func NewWeek(day ...time.Weekday) Week {
var day1, day2 time.Weekday
for i, d := range day {
switch i {
case 0:
day1 = d
case 1:
day2 = d
}
}
w := Week{
weekStart: StartDefault,
weekEnd: EndDefault,
}
if len(day) < 2 {
slog.Warn("not enough days given, using default")
return w
}
if day1 == time.Sunday && day1 != time.Saturday {
slog.Warn("there are not 7 days in given week, using default")
return w
}
test := day1 - 1
if day1 > time.Sunday && test != day2 {
slog.Warn("week start and end days are not consecutive, using default")
return w
}
w.weekStart = day1
w.weekEnd = day2
return w
}
// Date provides a shorthand for createing a time.Date truncating the time.
// timezone is ignored and UTC is used
func Date(year int, month time.Month, day int) time.Time {
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
}
// Day returns the tuncated date to the day of the given time t
func Day(t time.Time) time.Time {
return Date(t.Year(), t.Month(), t.Day())
}
// LastDayOfMonth from the given t time
func LastDayOfMonth(t time.Time) time.Time {
return Date(t.Year(), t.Month()+1, 0)
}
// WeekAdd returns t time with weeks added (use negative value to subtract)
func WeekAdd(t time.Time, weeks int) time.Time {
t = t.Add(OneWeek * time.Duration(weeks))
return t
}
// PriorLastFullWeek returns the start and end dates of the week prior to the last full week
// or two weeks ago
func (d Week) PriorLastFullWeek(t time.Time) (start, end time.Time) {
lfwStart, _ := d.LastFullWeek(t)
start = lfwStart.Add(-OneWeek)
end = start.Add(OneDay * 6)
return start, end
}
// StartOfWeek reutrns the date of the of the start of the week less than or equal to the given date t,
// which is the first day of the week back from the given time t
func (d Week) StartOfWeek(t time.Time) time.Time {
// time is already the start of the week
if t.Weekday() == d.weekStart {
return Date(t.Year(), t.Month(), t.Day())
}
// subtract days until we reach the start of the week
for t.Weekday() != d.weekStart {
t = t.Add(-OneDay)
}
return Date(t.Year(), t.Month(), t.Day())
}
// LastFullWeek returns the start and end dates of the last full week
func (d Week) LastFullWeek(t time.Time) (start, end time.Time) {
t = d.StartOfWeek(t)
start = t.Add(-OneWeek)
end = start.Add(OneDay * 6)
return start, end
}
// PrevYearLastFullWeek returns the start and end dates of the last full week of the previous year
func (d Week) PrevYearLastFullWeek(t time.Time) (start, end time.Time) {
startLfw, _ := d.LastFullWeek(t)
start = d.StartOfWeek(startLfw.Add(OneDay * -363)) // this is attempting to account for leap year
end = start.Add(OneDay * 6)
return start, end
}
// MonthToDate returns the start and end dates of the current month
func MonthToDate(t time.Time) (start, end time.Time) {
start = Date(t.Year(), t.Month(), 1)
end = t
return start, end
}
// FullMonth returns the start and end dates of the current month
func FullMonth(t time.Time) (start, end time.Time) {
start = Date(t.Year(), t.Month(), 1)
end = FirstOfNextMonth(t).Add(-OneDay)
return start, end
}
// FirstOfNextMonth returns the 1st of the next month from time t
func FirstOfNextMonth(t time.Time) time.Time {
year := t.Year()
nextMonth := t.Month() + 1
if nextMonth > time.December {
nextMonth = time.January
year = t.Year() + 1
}
return Date(year, nextMonth, 1)
}
// PrevMonth returns the start and end dates of the previous month
func PrevMonth(t time.Time) (start, end time.Time) {
prevMonth := t.Month() - 1
year := t.Year()
if prevMonth < 1 {
prevMonth = time.December
year = t.Year() - 1
}
t = Date(year, prevMonth, t.Day())
start = Date(year, prevMonth, 1)
end = FirstOfNextMonth(t).Add(-OneDay)
return start, end
}
// PrevMonthToDate returns the start and end dates of the previous month to the given date (t)
func PrevMonthToDate(t time.Time) (start, end time.Time) {
prevMonth := t.Month() - 1
year := t.Year()
if prevMonth < 1 {
prevMonth = time.December
year = t.Year() - 1
}
lm := Date(year, prevMonth, t.Day())
// if subtracking a month results in a date in the same month as t
// this means the day is greater than the last day of the previous month
if lm.Month() == t.Month() {
return PrevMonth(lm)
}
return MonthToDate(lm)
}
// PrevYearMtd returns the start and end dates up to t
// of the same month in the previous year
// if a leap day is given for t the previous year's last day will be feb 28th
func PrevYearMtd(t time.Time) (start, end time.Time) {
prevYear := t.Year() - 1
pEnd := Date(prevYear, t.Month(), t.Day())
if pEnd.Month() > t.Month() {
pEnd = pEnd.Add(-OneDay)
}
start = StartOfMonth(pEnd)
return start, pEnd
}
// YearToDate returns the start and end dates of the current year
func YearToDate(t time.Time) (start, end time.Time) {
start = Date(t.Year(), 1, 1)
end = t
return start, end
}
// PrevYearToDate returns the start and end dates of the previous year
func PrevYearToDate(t time.Time) (start, end time.Time) {
start = Date(t.Year()-1, 1, 1)
end = Date(t.Year()-1, t.Month(), t.Day())
// accounts for leap day
if end.Month() > t.Month() {
end = end.Add(-OneDay)
}
return start, end
}
// StartOfMonth returns 1st of current month @ midnight UTC
func StartOfMonth(t time.Time) time.Time {
return Date(t.Year(), t.Month(), 1)
}