-
-
Save bbailey1024/1aa7205bd61220cba8b9661a6315d148 to your computer and use it in GitHub Desktop.
Get list of time.Time based on nth day of month logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"time" | |
) | |
func main() { | |
// TODO | |
// Add error checking and validation across the board (e.g., fifth occurrence of weekday). | |
// Consider implementing a ScheduleLastWeekdayOfMonth function, or... | |
// Consider assuming last weekday of month if user supplied time is the last week of the month. | |
// User supplies start date from timepicker. | |
// User supplies end time from number of years to run or from timepicker. | |
// Generate a slice of time.Time for the scheduler. | |
start := time.Date(2022, 1, 11, 14, 0, 0, 0, time.UTC) // Second Tuesday of every month at 14:00. | |
end := start.AddDate(3, 0, 0) // Run for 3 years. | |
schedule := ScheduleNthWeekdayOfMonth(start, end) | |
// Creating run once style jobs based on generated schedule. | |
s := gocron.NewScheduler(time.UTC) | |
for _, t := range schedule { | |
_, err := s.Every(1).Tag("some-guid").LimitRunsTo(1).StartAt(t).Do(func() {}) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
s.StartBlocking() | |
} | |
// ScheduleNthWeekdayOfMonth returns a slice of monthly time.Time based on nth weekday of the month logic. | |
// Day of week and week of month are derived from the supplied start time. | |
// Monthly time.Time structs are generated until end time is reached. | |
func ScheduleNthWeekdayOfMonth(start time.Time, end time.Time) []time.Time { | |
var schedule []time.Time | |
// Set initial day of week. | |
dayOfWeek := int(start.Weekday()) | |
// Derive the nth occurrence of the weekday in the month from the supplied start time. | |
weekOfMonth := getNthOccurrenceOfWeekday(start) | |
// Increment start time by one month until end time is reached. | |
// Generate time struct for each month based on nth weekday of month logic. | |
for start.Before(end) { | |
dayOfMonth := getNthWeekdayOfMonth(start.Year(), int(start.Month()), dayOfWeek, weekOfMonth) | |
schedule = append(schedule, time.Date(start.Year(), start.Month(), dayOfMonth, start.Hour(), start.Minute(), start.Second(), 0, start.Location())) | |
start = start.AddDate(0, 1, 0) | |
} | |
return schedule | |
} | |
// getNthOccurrenceOfWeekday returns the nth occurrence of the weekday based on the provided time struct. | |
// Iterates over getNthWeekdayOfMonth until supplied day matches. | |
// Returns -1 if supplied date occurs in fifth week or no match occurs. | |
func getNthOccurrenceOfWeekday(t time.Time) int { | |
for i := 0; i < 4; i++ { | |
if t.Day() == getNthWeekdayOfMonth(t.Year(), int(t.Month()), int(t.Weekday()), i) { | |
return i | |
} | |
} | |
return -1 | |
} | |
// getNthWeekdayOfMonth returns the day of the month based on nth weekday of the month logic. | |
// Takes the target year and month along with the weekday and desired week. | |
// First week of the month is 0. | |
func getNthWeekdayOfMonth(year, month, weekday, week int) int { | |
// Create time struct for the first day of the month. | |
t := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) | |
// Step from the first of the month to the target day. | |
t = t.AddDate(0, 0, -(int(t.Weekday()) - weekday)) | |
// If the step to the target day goes to the prior month, add 1 week to the multiplier | |
if t.Day() > 7 { | |
week++ | |
} | |
// Add the requisite number of weeks and return the day of the month. | |
return t.AddDate(0, 0, week*7).Day() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment