My App
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

ComponentExampleDescription
FREQWEEKLY, DAILY, MONTHLYRecurrence frequency
INTERVAL2Every Nth frequency (e.g., bi-weekly)
BYDAYMO,WE,FRSpecific days of the week
UNTIL20260731T235959ZEnd date for the series
COUNT15Maximum number of occurrences
BYMONTHDAY1,15Specific days of the month
BYSETPOS1, -1Position within a set (e.g., first/last)

On this page