Files
Gardomatic/internal/daterange/daterange.go
T
kleiax 904d14b64c
CI / test (push) Canceled after 0s
Initial commit
2026-09-12 22:22:17 +02:00

47 lines
1.4 KiB
Go

// 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
}