Pure PL/pgSQL implementation of iCalendar RRULE (RFC 5545) for PostgreSQL. No C extensions, no compilation, works everywhere.
This library provides complete RRULE recurrence calculation functionality using pure PL/pgSQL. Use this implementation for all environments (local, development, staging, production) to ensure consistency.
Key Features:
- ✅ No C extensions required - Pure PL/pgSQL, works on any PostgreSQL
- ✅ Full timezone support with DST handling - Wall-clock time preserved ("10 AM stays 10 AM" across DST transitions)
- ✅ Consistent everywhere - Same implementation across all environments
- ✅ Production-ready - Comprehensive test suite with 187 tests (including table operation integration tests)
- ✅ RFC 5545 & RFC 7529 compliant - Supports standard RRULE patterns plus SKIP/RSCALE
- ✅ 50-75x faster than Node.js - Excellent performance without compilation
- ✅ Works on managed services - AlloyDB, RDS, Azure Database for PostgreSQL
Computation where your data lives delivers performance impossible with external processing:
- Set-based operations: JOIN recurrence rules against events, bookings, or any table without data transfer
- Native WHERE/aggregation: Filter by occurrence dates, COUNT occurrences, GROUP BY - all in SQL
- Batch processing: Process 100+ schedules in a single query without round trips
- Memory-efficient streaming: SETOF returns results incrementally, not all at once
- 50-75x faster than Node.js for single-schedule operations, infinitely faster for multi-schedule batch queries
See Example Usage for practical patterns including subscription billing, batch updates, and conflict detection.
Pure PL/pgSQL means:
- ✅ No C compiler or build tools required
- ✅ Install with a single SQL file - no configuration
- ✅ Consistent behavior across all environments (dev, staging, production)
Universal compatibility:
- ✅ Google AlloyDB - High-performance PostgreSQL with AI integration
- ✅ Google Cloud SQL - Fully managed PostgreSQL service
- ✅ Amazon Aurora PostgreSQL - Serverless PostgreSQL with automatic scaling
- ✅ Amazon RDS PostgreSQL - Managed PostgreSQL without custom extensions
- ✅ Azure Database for PostgreSQL - Fully supported managed service
- ✅ Self-hosted PostgreSQL - No special configuration required
- ✅ Docker PostgreSQL - Standard postgres image compatibility
npm install rrule-plpgsqlThen install in your database using your ORM/client. See Installation Guide for TypeScript/ORM integration.
# Install via psql
psql -d your_database -f src/install.sql
# Or use curl for one-line install
curl -sL https://raw.githubusercontent.com/sirrodgepodge/rrule_plpgsql/main/src/install.sql | psql -d your_databaseNext Steps: See Example Usage for practical patterns.
✅ Production-Ready (Always Enabled):
FREQ=DAILY- Daily recurrence with date/time filtersFREQ=WEEKLY- Weekly recurrence with day-of-week selectionFREQ=MONTHLY- Monthly recurrence with day/week-of-month selectionFREQ=YEARLY- Yearly recurrence with month/day/week selection
FREQ=HOURLY- Hourly recurrence (8,760/year max)FREQ=MINUTELY- Minutely recurrence (525,600/year max)FREQ=SECONDLY- Secondly recurrence (31M/year max)
See Sub-Day Operations Guide for enabling these safely.
COUNT- Limit number of occurrencesUNTIL- End date for recurrenceINTERVAL- Frequency multiplier (every N days/weeks/months)BYDAY- Filter by day of week (MO, TU, WE, TH, FR, SA, SU)BYDAYwith ordinals - Positioned weekdays (2MO = 2nd Monday, -1FR = last Friday)BYMONTHDAY- Filter by day of month (1-31, -1 = last day)BYMONTH- Filter by month (1-12)BYYEARDAY- Filter by day of year (1-366, negative supported)BYWEEKNO- Filter by ISO week number (1-53, YEARLY only)BYSETPOS- Select specific positions from generated setWKST- Week start day (SU, MO, TU, WE, TH, FR, SA)SKIP- Invalid date handling (OMIT, BACKWARD, FORWARD)TZID- Timezone specification with automatic DST handlingRSCALE- Calendar system (GREGORIAN supported)
See RFC Spec Compliance for complete feature support matrix and examples.
Full RFC 5545 timezone support with automatic DST handling:
-- Meeting stays at 10 AM wall-clock time across DST boundary
SELECT * FROM rrule.all(
'FREQ=DAILY;COUNT=3;TZID=America/New_York',
'2025-03-08 10:00:00'::TIMESTAMP
);
-- Returns: March 8 (EST), March 9 (EDT), March 10 (EDT)
-- All at 10:00 AM wall-clock timeSee API Reference for TIMESTAMPTZ API details.
psql -d your_database -f src/install.sqlIncludes:
- DAILY, WEEKLY, MONTHLY, YEARLY frequencies
- All standard modifiers and filters
- Timezone support with DST handling
- Secure by default (no DoS vectors)
psql -d your_database -f src/install_with_subday.sqlIncludes standard PLUS:
- HOURLY, MINUTELY, SECONDLY frequencies
⚠️ Security warnings during installation- Requires application-level validation
See Sub-Day Operations Guide before using this installation.
Error: "Invalid RRULE: FREQ parameter is required"
- Every RRULE must start with FREQ
- Fix: Add
FREQ=DAILY(or WEEKLY/MONTHLY/YEARLY)
Error: "COUNT and UNTIL are mutually exclusive"
- Cannot use both COUNT and UNTIL in same RRULE
- Fix: Use either
COUNT=10ORUNTIL=20251231T235959
Error: "BYWEEKNO can only be used with FREQ=YEARLY"
- BYWEEKNO requires YEARLY frequency
- Fix: Change to
FREQ=YEARLY;BYWEEKNO=10
See Validation Rules for complete error reference.
All functions support both TIMESTAMP and TIMESTAMPTZ with automatic timezone handling:
-- Generate occurrences
rrule.all(rrule, dtstart) → SETOF TIMESTAMP
rrule.between(rrule, dtstart, start, end) → SETOF TIMESTAMP
-- Query occurrences
rrule.after(rrule, dtstart, after_date) → TIMESTAMP
rrule.before(rrule, dtstart, before_date) → TIMESTAMP
rrule.next(rrule, dtstart) → TIMESTAMP
rrule.most_recent(rrule, dtstart) → TIMESTAMP
-- Utilities
rrule.count(rrule, dtstart) → INTEGER
rrule.overlaps(dtstart, dtend, rrule, mindate, maxdate) → BOOLEAN-- Every Monday for 4 weeks
SELECT * FROM rrule.all(
'FREQ=WEEKLY;BYDAY=MO;COUNT=4',
'2025-01-06 10:00:00'::TIMESTAMP
);
-- With timezone support
SELECT * FROM rrule.all(
'FREQ=DAILY;COUNT=5;TZID=America/New_York',
'2025-03-08 10:00:00'::TIMESTAMP -- DST handled automatically
);See API Reference for complete function signatures, timezone handling details, and advanced examples.
Contributions welcome! Please:
- Run all tests (all 10 test suites must pass)
- Add test coverage for new features
- Update documentation
- Follow RFC 5545/7529 specifications
- Submit pull request
See Development Guide for contribution guidelines and test suite details.
Single schedule: 50-75x faster than Node.js rrule.js Multi-schedule queries: Eliminates application-database round trips entirely
Scalability advantages:
- Set-based batch operations: Query 100+ schedules in parallel without loops
- Streaming results: SETOF returns incrementally, not all at once (constant memory)
- Query planner optimization: PostgreSQL optimizes JOINs and filters with occurrence expansion
- Early-exit optimizations: Stops computation when COUNT/UNTIL limits reached
See Performance Guide for optimization strategies and Development Guide for benchmarking details.
- PostgreSQL 12 or higher
- No C extensions required
- No external dependencies
- Works on all PostgreSQL-compatible platforms
See Development Guide for development setup instructions.