package main import ( "time" ) ////////////////////////////////////////////////////////////////////// type Date struct { Year int Month time.Month Day int } func DateOf(t time.Time) Date { y, m, d := t.Date() return Date{Year: y, Month: m, Day: d} } func (d Date) Time() time.Time { return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, time.Local) } func (d Date) AddDays(delta int) Date { return DateOf(d.Time().AddDate(0, 0, delta)) } func (d Date) Weekday() time.Weekday { return d.Time().Weekday() } func (a Date) Cmp(b Date) int { switch { case a.Year < b.Year: return -1 case a.Year > b.Year: return 1 } switch { case a.Month < b.Month: return -1 case a.Month > b.Month: return 1 } switch { case a.Day < b.Day: return -1 case a.Day > b.Day: return 1 } return 0 } ////////////////////////////////////////////////////////////////////// type CalendarDay[T any] struct { Date Data T } ////////////////////////////////////////////////////////////////////// // keyed by time.Weekday type CalendarWeek[T any] [7]CalendarDay[T] ////////////////////////////////////////////////////////////////////// // must be sorted, must be non-sparse type Calendar[T any] []CalendarWeek[T] func (c Calendar[T]) NumWeekdaysInMonth(weekday time.Weekday, target Date) int { num := 0 for _, w := range c { if w[weekday].Date == (Date{}) { continue } switch { case w[weekday].Year == target.Year: switch { case w[weekday].Month == target.Month: num++ case w[weekday].Month > target.Month: return num } case w[weekday].Year > target.Year: return num } } return num } ////////////////////////////////////////////////////////////////////// func BuildCalendar[T any](things []T, dateOfThing func(T) Date) Calendar[T] { if len(things) == 0 { return nil } newestDate := DateOf(time.Now().Local()) oldestDate := dateOfThing(things[0]) byDate := make(map[Date]T, len(things)) for _, thing := range things { date := dateOfThing(thing) if oldestDate.Cmp(date) > 0 { oldestDate = date } byDate[date] = thing } var ret Calendar[T] for date := oldestDate; date.Cmp(newestDate) <= 0; date = date.AddDays(1) { if len(ret) == 0 || date.Weekday() == 0 { ret = append(ret, CalendarWeek[T]{}) } ret[len(ret)-1][date.Weekday()] = CalendarDay[T]{ Date: date, Data: byDate[date], } } return ret }