Skip to content

Instantly share code, notes, and snippets.

@bbailey1024
Last active December 17, 2021 19:15
Show Gist options
  • Save bbailey1024/1aa7205bd61220cba8b9661a6315d148 to your computer and use it in GitHub Desktop.
Save bbailey1024/1aa7205bd61220cba8b9661a6315d148 to your computer and use it in GitHub Desktop.
Get list of time.Time based on nth day of month logic
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