CourseKitGuides
RRULE Patterns
Common RFC 5545 recurrence patterns for academic scheduling
Overview
CourseKit uses RFC 5545 RRULE strings to define recurring events. The RecurrenceService parses these using the rrule library.
Basic Patterns
Weekly Lecture
// Every Monday
'FREQ=WEEKLY;BYDAY=MO'
// Every Tuesday and Thursday
'FREQ=WEEKLY;BYDAY=TU,TH'
// Monday, Wednesday, Friday
'FREQ=WEEKLY;BYDAY=MO,WE,FR'Bi-Weekly
// Every other Tuesday and Thursday
'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH'Until a Specific Date
// Every Monday until end of semester
'FREQ=WEEKLY;BYDAY=MO;UNTIL=20260731T235959Z'Limited Count
// Daily for 14 weeks (weekdays only, 70 occurrences)
'FREQ=DAILY;COUNT=70;BYDAY=MO,TU,WE,TH,FR'
// 15 weekly sessions
'FREQ=WEEKLY;COUNT=15;BYDAY=WE'Academic Patterns
Standard Lecture Schedule
// 2x per week, 15-week semester
const weeklyLecture = {
title: 'CS 101 - Intro to Programming',
startTime: new Date('2026-03-02T09:00:00'), // Monday 9 AM
durationMin: 90,
recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,WE;UNTIL=20260619T235959Z',
};Lab Sessions
// Weekly lab on Friday
const labSession = {
title: 'CS 101 - Lab',
startTime: new Date('2026-03-06T14:00:00'), // Friday 2 PM
durationMin: 120,
recurrenceRule: 'FREQ=WEEKLY;BYDAY=FR;UNTIL=20260619T235959Z',
};Monthly Faculty Meeting
// First Wednesday of each month
'FREQ=MONTHLY;BYDAY=1WE'
// Last Friday of each month
'FREQ=MONTHLY;BYDAY=-1FR'Exam Schedule
// One-time event (no RRULE)
const finalExam = {
title: 'CS 101 - Final Exam',
startTime: new Date('2026-06-22T09:00:00'),
durationMin: 180,
recurrenceRule: null, // null = one-time event
};Exception Patterns
Cancel a Holiday
When a recurring lecture falls on a holiday, create a cancelled exception:
// Cancel the March 30 occurrence (spring break)
await eventStorage.createException({
eventId: lecture.id,
originalDate: new Date('2026-03-30'),
type: 'cancelled',
newStartTime: null,
newDurationMin: null,
newRoomId: null,
metadata: { reason: 'Spring Break' },
});Move to a Different Room
// Room change for April 6 occurrence
await eventStorage.createException({
eventId: lecture.id,
originalDate: new Date('2026-04-06'),
type: 'modified',
newStartTime: null, // keep same time
newDurationMin: null, // keep same duration
newRoomId: 'large-auditorium', // different room
metadata: { reason: 'Guest lecturer' },
});Add a Makeup Session
// Extra session on Saturday to make up for cancelled class
await eventStorage.createException({
eventId: lecture.id,
originalDate: new Date('2026-04-04'), // Saturday
type: 'added',
newStartTime: new Date('2026-04-04T10:00:00'),
newDurationMin: 90,
newRoomId: null, // same room as parent event
metadata: { reason: 'Makeup session' },
});Validation
Always validate RRULE strings before storing them:
import { RecurrenceService } from '@hfu.digital/coursekit-nestjs';
const error = recurrence.validateRule('FREQ=WEEKLY;BYDAY=MO');
if (error !== null) {
throw new Error(`Invalid RRULE: ${error}`);
}RRULE Quick Reference
| Component | Example | Description |
|---|---|---|
FREQ | WEEKLY, DAILY, MONTHLY | Recurrence frequency |
INTERVAL | 2 | Every Nth frequency (e.g., bi-weekly) |
BYDAY | MO,WE,FR | Specific days of the week |
UNTIL | 20260731T235959Z | End date for the series |
COUNT | 15 | Maximum number of occurrences |
BYMONTHDAY | 1,15 | Specific days of the month |
BYSETPOS | 1, -1 | Position within a set (e.g., first/last) |