// Package daterange resolves recurring calendar windows and normalized dates. package daterange import "time" // CalendarWindow resolves a recurring month/day range around now. A range whose // start month is after its end month crosses the year boundary. func CalendarWindow(now time.Time, monthFrom int, dayFrom *int, monthTo int, dayTo *int) (time.Time, time.Time) { location := now.Location() startYear := now.Year() if monthFrom > monthTo && int(now.Month()) <= monthTo { startYear-- } endYear := startYear if monthFrom > monthTo { endYear++ } startDay := 1 if dayFrom != nil { startDay = clampDay(startYear, time.Month(monthFrom), *dayFrom) } endDay := daysInMonth(endYear, time.Month(monthTo)) if dayTo != nil { endDay = clampDay(endYear, time.Month(monthTo), *dayTo) } return time.Date(startYear, time.Month(monthFrom), startDay, 0, 0, 0, 0, location), time.Date(endYear, time.Month(monthTo), endDay, 23, 59, 59, 0, location) } // Date returns value at midnight in its original location. func Date(value time.Time) time.Time { return time.Date(value.Year(), value.Month(), value.Day(), 0, 0, 0, 0, value.Location()) } func daysInMonth(year int, month time.Month) int { return time.Date(year, month+1, 0, 0, 0, 0, 0, time.UTC).Day() } func clampDay(year int, month time.Month, day int) int { if maximum := daysInMonth(year, month); day > maximum { return maximum } if day < 1 { return 1 } return day }