package actionssummerwindnet import ( "fmt" "time" "github.com/teambition/rrule-go" ) type RecurrenceRule struct { Frequency string UntilTime time.Time } type Period struct { StartTime time.Time EndTime time.Time } func (r *Period) String() string { if r == nil { return "" } return r.StartTime.Format(time.RFC3339) + "-" + r.EndTime.Format(time.RFC3339) } func MatchSchedule(now time.Time, startTime, endTime time.Time, recurrenceRule RecurrenceRule) (*Period, *Period, error) { return calculateActiveAndUpcomingRecurringPeriods( now, startTime, endTime, recurrenceRule.Frequency, recurrenceRule.UntilTime, ) } func calculateActiveAndUpcomingRecurringPeriods(now, startTime, endTime time.Time, frequency string, untilTime time.Time) (*Period, *Period, error) { var freqValue rrule.Frequency var freqDurationDay int var freqDurationMonth int var freqDurationYear int switch frequency { case "Daily": freqValue = rrule.DAILY freqDurationDay = 1 case "Weekly": freqValue = rrule.WEEKLY freqDurationDay = 7 case "Monthly": freqValue = rrule.MONTHLY freqDurationMonth = 1 case "Yearly": freqValue = rrule.YEARLY freqDurationYear = 1 case "": if now.Before(startTime) { return nil, &Period{StartTime: startTime, EndTime: endTime}, nil } if now.Before(endTime) { return &Period{StartTime: startTime, EndTime: endTime}, nil, nil } return nil, nil, nil default: return nil, nil, fmt.Errorf(`invalid freq %q: It must be one of "Daily", "Weekly", "Monthly", and "Yearly"`, frequency) } freqDurationLater := time.Date( now.Year()+freqDurationYear, time.Month(int(now.Month())+freqDurationMonth), now.Day()+freqDurationDay, now.Hour(), now.Minute(), now.Second(), now.Nanosecond(), now.Location(), ) freqDuration := freqDurationLater.Sub(now) overrideDuration := endTime.Sub(startTime) if overrideDuration > freqDuration { return nil, nil, fmt.Errorf("override's duration %s must be equal to sor shorter than the duration implied by freq %q (%s)", overrideDuration, frequency, freqDuration) } rrule, err := rrule.NewRRule(rrule.ROption{ Freq: freqValue, Dtstart: startTime, Until: untilTime, }) if err != nil { return nil, nil, err } overrideDurationBefore := now.Add(-overrideDuration + 1) activeOverrideStarts := rrule.Between(overrideDurationBefore, now, true) var active *Period if len(activeOverrideStarts) > 1 { return nil, nil, fmt.Errorf("[bug] unexpted number of active overrides found: %v", activeOverrideStarts) } else if len(activeOverrideStarts) == 1 { active = &Period{ StartTime: activeOverrideStarts[0], EndTime: activeOverrideStarts[0].Add(overrideDuration), } } oneSecondLater := now.Add(1) upcomingOverrideStarts := rrule.Between(oneSecondLater, freqDurationLater, true) var next *Period if len(upcomingOverrideStarts) > 0 { next = &Period{ StartTime: upcomingOverrideStarts[0], EndTime: upcomingOverrideStarts[0].Add(overrideDuration), } } return active, next, nil }