rrule
A JavaScript/TypeScript library for computing iCalendar (RFC 5545) recurrence rules for calendar dates.
Repository Health
Technical Analysis
rrule is a JavaScript/TypeScript port of Python’s python-dateutil recurrence engine, implementing the iCalendar RFC 5545 RRULE specification for computing recurring calendar dates. It exposes an RRule class for single recurrence rules, an RRuleSet class for combining multiple rules with explicit inclusion/exclusion dates, and an rrulestr parser that turns an iCalendar RRULE string directly into a usable rule object.
Beyond RFC compliance, the library adds natural-language conversion in both directions: toText()/fromText() translate a rule like “every 5 weeks on Monday, Friday until January 31, 2013” into RRule options and back, which is useful for building human-facing recurrence pickers without hand-rolling the parsing logic. It ships as pure TypeScript with a single runtime dependency (tslib), targeting both browser and Node environments, and is widely used as the recurrence engine behind calendar and scheduling UIs.
What You Get
- An RRule class covering the full RFC 5545 option set (freq, interval, count, until, byweekday, bymonth, bymonthday, byyearday, byweekno, bysetpos, byhour/minute/second, byeaster)
- An RRuleSet class for composing multiple RRULEs plus explicit RDATE/EXDATE inclusion and exclusion dates into one combined occurrence set
- An rrulestr() parser that converts an iCalendar RRULE/RRuleSet string directly into a working rule object, and toString()/optionsToString() for the reverse
- Natural-language conversion via toText()/fromText() and parseText(), translating rules to and from human-readable phrases like “every 5 weeks on Monday, Friday until January 31, 2013”
- IANA timezone support via the Intl API through the tzid option, plus a datetime() helper for constructing dates in the library’s expected UTC-based format
- Built-in occurrence caching so repeated all()/between() calls on the same rule don’t re-run the iteration algorithm
Common Use Cases
- Powering recurrence pickers in calendar and scheduling UIs, where a user picks “every 2 weeks on Tue/Thu” and the app needs the resulting occurrence dates
- Parsing and re-serializing iCalendar RRULE strings when importing/exporting .ics calendar data
- Computing the next N occurrences of a recurring task, billing cycle, or reminder for background job scheduling
- Converting recurrence rules to and from human-readable text for calendar UIs that display rules in plain language
Under The Hood
Architecture
Execution flows from the public RRule class (src/rrule.ts), which parses and normalizes constructor options via parseOptions/initializeOptions (src/parseoptions.ts) into a ParsedOptions object, then delegates all query methods (all, between, before, after) to a shared _iter call into the standalone iter() function (src/iter/index.ts). That iterator walks calendar periods year-by-month using an Iterinfo helper (src/iterinfo/index.ts, with easter/month/year-specific submodules) to build day masks and time sets, filtering days against every by* constraint before emitting accepted dates through an IterResult/CallbackIterResult accumulator (src/iterresult.ts, src/callbackiterresult.ts). RRuleSet (src/rruleset.ts) layers on top by merging multiple RRule/EXRULE instances with explicit RDATE/EXDATE arrays and de-duplicating the combined output, while rrulestr/parsestring/optionsToString (src/rrulestr.ts, src/parsestring.ts, src/optionstostring.ts) handle iCalendar string round-tripping and a separate src/nlp/ module handles natural-language conversion — a clean layered separation between option parsing, core iteration, set composition, string serialization, and natural-language translation, with a Cache class (src/cache.ts) memoizing repeated queries per rule instance. The core abstraction to protect is the day/time-mask iteration in iter() and Iterinfo; changing it touches every query method and both serialization paths at once.
Tech Stack
The library is authored entirely in TypeScript (98%+ of source bytes) with a single runtime dependency, tslib, keeping the published bundle dependency-free for consumers. The build pipeline compiles with tsc against tsconfig.build.json and bundles with Webpack 5 (webpack.config.js) plus terser-webpack-plugin for minification, publishing both an ES5 CommonJS build (dist/es5) and an ESM build (dist/esm) with generated .d.ts types. Linting runs through ESLint 8 with @typescript-eslint and eslint-plugin-jsdoc/eslint-plugin-import, formatting through Prettier 2, both wired into husky + lint-staged pre-commit hooks. There is no database or network layer — the library is pure computation over JS Date objects, with the Intl API used for IANA timezone resolution (tzid option) rather than a timezone library dependency.
Code Quality
Testing uses Jest with ts-jest (jest.config.js) plus mockdate for deterministic date-based assertions, and the test/ directory mirrors the src/ module layout with a dedicated test file per module (rrule, rruleset, rrulestr, parseoptions, parsestring, cache, datetime, helpers, nlp) plus shared fixtures in test/lib. CI (.github/workflows/nodejs.yml) runs the full suite across three Node versions and, notably, across five different LANG/TZ combinations (including Pacific/Kiritimati and Africa/Nairobi) specifically to catch timezone- and locale-sensitive date bugs, then reports coverage to Codecov via nyc. Source files carry substantial inline JSDoc-style comments on public methods (parameter/return documentation on every RRule query method), and the constructor/option-parsing path validates inputs and throws explicit Errors (e.g. RRule.between/before/after reject invalid Date arguments) rather than silently returning bad data.
API Design
The public surface is intentionally small and RFC-mirroring: a single RRule constructor takes an options object whose keys map directly to iCalendar RRULE parameters (freq, interval, byweekday, etc.), so anyone already familiar with the RFC or with Python’s dateutil.rrule (which this library is explicitly ported from) can use it with minimal ramp-up. Constants like RRule.MO/RRule.WEEKLY avoid magic numbers, and the datetime() helper sidesteps a documented JavaScript Date/timezone footgun that the README calls out at length. The main friction point for newcomers is exactly that UTC-handling caveat — the README devotes a long, explicit section to it because getting it wrong silently produces off-by-hours results — and the natural-language toText()/fromText() conversion is explicitly approximate rather than complete, which the library documents rather than hides.
Used by 10 apps in this directory
Cal.diy
Scheduling
The 100% MIT-licensed, community-driven scheduling platform — self-host your own booking infrastructure with no enterprise strings attached.
Discourse
Community
Open-source community platform with dynamic trust levels, real-time chat, and a powerful plugin architecture for self-hosted forums.
Kener
Monitoring · Devops
Stunning, self-hosted status pages with real-time uptime monitoring, incident management, and multi-channel notifications in a single Docker container.
Manage
Project Management · Collaboration
Self-hosted, open-source project management with tasks, calendars, docs, and real-time team collaboration.
n8n
Automation · No Code Platforms
Code when you need it, UI when you don't — the workflow automation platform built for technical teams who refuse to choose.
Novu
Developer Tools
Open-source communication infrastructure that connects your products and AI agents to every channel your users live on — Inbox, Email, SMS, Push, Chat, and more.
PostHog
Analytics · Monitoring · Developer Tools
The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.
SigNoz
Monitoring · Analytics
Self-host your entire observability stack — logs, metrics, traces, and LLM monitoring — in one OpenTelemetry-native platform, without the Datadog bill.
superset
AI Code Assistants · AI Development
Orchestrate an army of AI coding agents—Claude Code, Codex, Gemini CLI, and more—running simultaneously in isolated git worktrees from a single Electron desktop app.