Skip to content

Parsing

parse() converts an RRULE string into a typed RRuleOptions object. It returns a Result and never throws on user input, even for completely malformed strings.

parse() accepts four input forms:

Bare RRULE value (no prefix):

FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10

RRULE: prefixed string (most common):

RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10

Multi-line iCalendar block (with DTSTART content line):

DTSTART;TZID=America/New_York:20240101T090000
RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10

DTSTART with UTC marker:

DTSTART:20240101T090000Z
RRULE:FREQ=DAILY;COUNT=5
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }

parse() returns Result<RRuleOptions, string>. Check result.ok before accessing result.value.

All fields are optional except those implied by the input:

interface RRuleOptions {
freq: Frequency // 'SECONDLY' | 'MINUTELY' | 'HOURLY' | 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY'
interval?: number // default 1
count?: number // terminate after N occurrences
until?: RRuleUntil // terminate at date/datetime
wkst?: Weekday // week start day, default 'MO'
byMonth?: number[] // 1-12
byMonthDay?: number[] // 1-31, or -1 to -31 (negative = from end)
byDay?: WeekdayNum[] // e.g. [{ weekday: 'MO' }, { n: 2, weekday: 'TU' }]
byYearDay?: number[] // 1-366, or negative
byWeekNo?: number[] // 1-53 ISO week numbers, or negative
byHour?: number[] // 0-23
byMinute?: number[] // 0-59
bySecond?: number[] // 0-60
bySetPos?: number[] // filter within the set, e.g. -1 = last
dtstart?: RRuleDtstart // start date/time as a Temporal type
tzid?: string // IANA timezone identifier
}

Ordinal weekday values like 3MO (third Monday) and -1FR (last Friday) are parsed into a WeekdayNum object:

interface WeekdayNum {
weekday: Weekday // 'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'
ordinal: number | undefined // positive/negative ordinal; undefined means every occurrence
}

When ordinal is undefined, the entry selects every occurrence of that weekday in the period (for example BYDAY=MO in a WEEKLY rule selects every Monday). When ordinal is set, it selects the nth (or nth-from-last) occurrence within a MONTHLY or YEARLY period.

For example, BYDAY=2MO,-1FR parses to:

byDay: [
{ weekday: 'MO', ordinal: 2 },
{ weekday: 'FR', ordinal: -1 },
]

And plain weekdays BYDAY=MO,WE,FR parse to:

byDay: [
{ weekday: 'MO', ordinal: undefined },
{ weekday: 'WE', ordinal: undefined },
{ weekday: 'FR', ordinal: undefined },
]

The type of dtstart depends on the DTSTART form in the input:

Input formdtstart type
DTSTART:20240101 (date only)Temporal.PlainDate
DTSTART:20240101T090000 (floating)Temporal.PlainDateTime
DTSTART:20240101T090000Z (UTC)Temporal.Instant
DTSTART;TZID=America/New_York:20240101T090000Temporal.ZonedDateTime

RRuleUntil follows the same pattern:

UNTIL formType
UNTIL=20240101Temporal.PlainDate
UNTIL=20240101T090000Temporal.PlainDateTime
UNTIL=20240101T090000ZTemporal.Instant