Recurrence

A recurring event carries an RRULE. go-icalendar parses it into a typed RRule and expands it into concrete occurrences.

ev, _ := icalendar.ParseICS(data)
if ev.IsRecurring() {
    from := time.Now()
    to := from.AddDate(0, 1, 0) // next month
    for _, t := range ev.Occurrences(from, to, 0) {
        fmt.Println(t.Local())
    }
}

Occurrences(from, to, limit) returns occurrence start times in the half-open window [from, to), merging the RRULE expansion with any RDATE and removing any EXDATE. Pass limit > 0 to cap the count; 0 means unlimited (subject to an internal safety bound).

Parsing and emitting rules directly

r, err := icalendar.ParseRRule("FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE;COUNT=10")
fmt.Println(r.Freq)      // WEEKLY
fmt.Println(r.String())  // canonical RRULE text

times := r.Between(dtstart, from, to, 0)

Between enumerates a rule's occurrences anchored at dtstart; dtstart is always the first occurrence.

Supported rule parts

PartSupportedNotes
FREQSECONDLY → YEARLY
INTERVAL
COUNT / UNTIL
BYMONTH
BYMONTHDAYnegatives count from month end (-1 = last day)
BYDAYordinals too: 2MO (2nd Monday), -1FR (last Friday)
BYSETPOSe.g. last weekday of the month
WKSTdefaults to Monday
BYWEEKNO, BYYEARDAY⚠️parsed but ignored during expansion

Examples

// "Last Friday of every month"
icalendar.ParseRRule("FREQ=MONTHLY;BYDAY=-1FR")

// "Every other week on Mon/Wed/Fri, 10 times"
icalendar.ParseRRule("FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR;COUNT=10")

// "The last weekday of each month"
icalendar.ParseRRule("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1")
Note

Months that lack the requested day are skipped, per RFC 5545 — e.g. FREQ=MONTHLY;BYMONTHDAY=31 produces no occurrence in February or April.