HFU Digital Docs
RoomKitGuides

Recurrence Patterns

Work with weekly, biweekly, and custom calendar-week recurrence patterns

Overview

RoomKit supports three recurrence frequencies for creating recurring booking series. The RecurrenceService expands rules into concrete dates and manages the lifecycle of recurring bookings.

Frequency Types

Weekly

Repeats every week on the specified days.

const rule = {
    frequency: 'weekly',
    daysOfWeek: [1, 3, 5],  // Monday, Wednesday, Friday
    seriesStartsAt: new Date('2026-03-02'),
    seriesEndsAt: new Date('2026-07-17'),
    exceptionDates: [],
};

const dates = recurrenceService.expandSeries(rule);
// → Every Mon/Wed/Fri from Mar 2 to Jul 17

Biweekly

Repeats every other week, starting from the first week of the series.

const rule = {
    frequency: 'biweekly',
    daysOfWeek: [2, 4],  // Tuesday, Thursday
    seriesStartsAt: new Date('2026-03-03'),
    seriesEndsAt: new Date('2026-07-16'),
    exceptionDates: [],
};

const dates = recurrenceService.expandSeries(rule);
// → Every other Tue/Thu from Mar 3 to Jul 16

Custom (Calendar Weeks)

Repeats only on specified ISO calendar weeks. Useful for university schedules where lectures only happen in specific weeks.

const rule = {
    frequency: 'custom',
    daysOfWeek: [1, 3],  // Monday, Wednesday
    calendarWeeks: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
    seriesStartsAt: new Date('2026-03-02'),
    seriesEndsAt: new Date('2026-05-15'),
    exceptionDates: [],
};

const dates = recurrenceService.expandSeries(rule);
// → Mon/Wed only in ISO weeks 10-20

Day-of-Week Values

ValueDay
0Sunday
1Monday
2Tuesday
3Wednesday
4Thursday
5Friday
6Saturday

Exception Dates

Exclude specific dates from a series (e.g., holidays):

const rule = {
    frequency: 'weekly',
    daysOfWeek: [1, 3, 5],
    seriesStartsAt: new Date('2026-03-02'),
    seriesEndsAt: new Date('2026-07-17'),
    exceptionDates: [
        new Date('2026-04-03'),  // Good Friday
        new Date('2026-04-06'),  // Easter Monday
        new Date('2026-05-01'),  // May Day
        new Date('2026-05-14'),  // Ascension Day
    ],
};

Exception dates are compared by date only (time component is ignored).

Creating Recurring Bookings

Use createRecurringBooking() to create a full series. Each expanded date gets its own booking instance linked to the recurrence rule:

const result = await recurrenceService.createRecurringBooking(
    {
        roomId: 'room-101',
        requesterId: 'prof-mueller',
        title: 'Software Engineering',
        startsAt: new Date('2026-03-02T10:00:00Z'),
        endsAt: new Date('2026-03-02T11:30:00Z'),
        purposeType: 'LECTURE',
    },
    {
        frequency: 'weekly',
        daysOfWeek: [1],
        seriesStartsAt: new Date('2026-03-02'),
        seriesEndsAt: new Date('2026-07-13'),
        exceptionDates: [],
    },
);

console.log(result.rule.id);              // Created recurrence rule
console.log(result.bookings.length);      // Successfully created bookings
console.log(result.conflicts.length);     // Dates that had conflicts

The time-of-day is taken from the template booking (startsAt/endsAt) and applied to each expanded date. Conflicts are collected but do not abort the entire series.

Modifying Recurring Bookings

Modify a Single Instance

Changes one instance without affecting the rest of the series. The instance is marked as MODIFIED:

await recurrenceService.modifySingle(
    bookingId,
    { startsAt: new Date('2026-04-13T14:00:00Z') },
    booking.version,
);

Modify This and Future Instances

Splits the series at the target date:

  1. The original rule is truncated to end before the target date
  2. A new rule is created from the target date onward
  3. Future ORIGINAL instances from the old rule are deleted
  4. New instances are expanded from the new rule
const result = await recurrenceService.modifyThisAndFuture(
    bookingId,
    { roomId: 'room-202' },  // Move to a different room from this date on
);

console.log(result.newRule.id);       // The new rule
console.log(result.bookings.length);  // Newly created instances

Modify All Instances

Deletes all ORIGINAL instances and re-creates them with changes applied. MODIFIED and DETACHED instances are left untouched and returned as skipped:

const result = await recurrenceService.modifyAll(
    ruleId,
    { title: 'Advanced Software Engineering' },
);

console.log(result.bookings.length);  // Re-created instances
console.log(result.skipped.length);   // Untouched modified/detached instances

Recurrence Modification Types

TypeDescription
originalInstance matches the series pattern, untouched
modifiedInstance was individually changed via modifySingle
detachedInstance was fully detached from the series

When using modifyAll, only original instances are affected. modified and detached instances preserve their individual changes.

On this page