package publishschedule import ( "sort" "strings" "time" ) type Slot struct { Weekday int Time string } func BuildSchedule(startAt int64, timezone string, slots []Slot, count int) []int64 { if count <= 0 { return nil } loc := time.UTC if strings.TrimSpace(timezone) != "" { if loaded, err := time.LoadLocation(strings.TrimSpace(timezone)); err == nil { loc = loaded } } start := time.Now().In(loc) if startAt > 0 { start = time.Unix(0, startAt).In(loc) } normalized := normalizeSlots(slots) if len(normalized) == 0 { out := make([]int64, 0, count) for i := 0; i < count; i++ { out = append(out, start.Add(time.Duration(i)*2*time.Hour).UTC().UnixNano()) } return out } out := make([]int64, 0, count) day := time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, loc) for len(out) < count { for _, slot := range normalized { if int(day.Weekday()) != slot.Weekday { continue } hour, minute, ok := parseHHMM(slot.Time) if !ok { continue } candidate := time.Date(day.Year(), day.Month(), day.Day(), hour, minute, 0, 0, loc) if candidate.Before(start) { continue } out = append(out, candidate.UTC().UnixNano()) if len(out) >= count { break } } day = day.AddDate(0, 0, 1) } return out } func normalizeSlots(slots []Slot) []Slot { out := make([]Slot, 0, len(slots)) for _, slot := range slots { if strings.TrimSpace(slot.Time) == "" { continue } weekday := slot.Weekday if weekday < 0 || weekday > 6 { weekday = ((weekday % 7) + 7) % 7 } out = append(out, Slot{Weekday: weekday, Time: strings.TrimSpace(slot.Time)}) } sort.Slice(out, func(i, j int) bool { if out[i].Weekday == out[j].Weekday { return out[i].Time < out[j].Time } return out[i].Weekday < out[j].Weekday }) return out } func parseHHMM(value string) (int, int, bool) { parsed, err := time.Parse("15:04", strings.TrimSpace(value)) if err != nil { return 0, 0, false } return parsed.Hour(), parsed.Minute(), true }