Recurrence Generator
time.generate_recurrence
Overview
Expands an RFC 5545 recurrence rule (RRULE) into occurrence instants in a named IANA zone, using ical4j for the grammar. Two bounds apply at once — an occurrence limit of at most 1000 and a window of at most 10 years — so an infinite or far-future rule always terminates and reports truncated: true. Occurrences keep their local wall-clock time across daylight-saving changes, which is the RFC 5545 semantic. This expands a rule, not a calendar file: there is no VCALENDAR, VEVENT, VTIMEZONE, attendee, or alarm handling.
RFC 5545 recurrence is easy to get subtly wrong — we expand rules with ical4j under bounds you control so infinite series always terminate safely.
Reliability
Same rule, same seed, same zone, same dates — for a given ical4j and IANA time-zone database version, both reported in _meta, so pin them if you diff stored schedules. Occurrences keep their local wall-clock time across a daylight-saving change, which is the RFC 5545 semantic: 09:00 stays 09:00 and the offset moves. A rule that is expensive to expand is refused from its shape before any expansion starts, rather than discovered halfway through. RRULE_INVALID — trigger: the rule does not parse — it is empty, longer than 1024 characters, not a semicolon-separated list of NAME=VALUE parts, missing FREQ, repeats a part, or gives a part a value the grammar rejects; retryable: no, the same text always fails; next: read the message, which names the offending part without echoing your rule back, and fix that part — running the rule through time.validate_recurrence first gives you the same diagnosis with a preview. RRULE_UNSUPPORTED_PART — trigger: the rule carries a part name outside the supported subset, most often a calendar-file property pasted in alongside the rule such as DTSTART, RDATE or EXDATE, or a misspelling like BYWEEKDAY; retryable: no; next: the message names the part — drop it, correct the spelling, move a DTSTART into startDateTime, and move EXDATE values into exceptionDates. RRULE_EXPANSION_UNBOUNDED — trigger: the rule parses but its shape cannot be expanded safely — INTERVAL below 1, more than 500 BY* values in total, BY* parts that multiply out to more than 10000 candidate instants per period, a BYSETPOS with no other BY* part to select from, or FREQ=SECONDLY or MINUTELY with no COUNT, no UNTIL and a window longer than a day; retryable: no; next: add COUNT or UNTIL, or set a windowEnd within 24 hours of the start for a sub-daily rule, and thin out the BY* lists — asking for every second of every minute is almost always a mistake. TIMEZONE_UNKNOWN — trigger: the zone field is not a zone we can resolve, which covers a typo, a three-letter abbreviation such as CST, and a fixed offset such as +02:00; retryable: no; next: send a region-based identifier like Europe/Brussels, or UTC.
Context footprint
One call replaces a recurrence expansion an agent cannot perform reliably; the response is a bounded list of ISO instants. A successful call costs 1 execution credit.
When to use it
When you hold an RRULE and need concrete occurrence instants inside a bounded window.
Do not use to validate or explain a rule without full expansion (use time.validate_recurrence), parse a .ics calendar file, or detect interval clashes.
Powered by ical4j, the reference-grade RFC 5545 iCalendar library for recurrence-rule parsing and expansion.
Scope
Non-goals
- Does not parse a VCALENDAR or VEVENT — the input is a rule, not a calendar file
- Does not expand an infinite rule without a bound; the limit always applies
- Does not handle attendees, alarms, or VTIMEZONE definitions
- Does not accept RDATE or EXDATE properties beyond the exceptionDates field
- Does not read the system clock; the series seed is always supplied
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response may contain submitted or transformed content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| rrule | string | Yes | Supported RFC 5545 RRULE body. |
| startDateTime | string | Yes | Zone-less recurrence seed. |
| zone | string | Yes | IANA recurrence zone or UTC. |
| windowEnd | string | No | Zone-less expansion window end; omit it to stop at the occurrence cap alone. |
| limit | integer | No | Occurrence cap; omit it for the published default. |
| exceptionDates | array | No | From 0 through the published exception-date cap of zone-less local occurrences to exclude. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| occurrences | array | Yes | From 0 through the requested occurrence cap in ascending order. |
| count | integer | Yes | Returned occurrence count. |
| truncated | boolean | Yes | Whether the series continues beyond the response. |
| infinite | boolean | Yes | Whether the rule has no COUNT or UNTIL. |
| normalized | string | Yes | Canonical normalized RRULE. |
| excludedCount | integer | Yes | Matched exception count. |
| _meta | object | No | Closed recurrence engine and window receipt with tzdbVersion, engine identity, and applied windowEnd. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"time.generate_recurrence","input":{"rrule":"FREQ=WEEKLY;BYDAY=MO,WE;COUNT=4","startDateTime":"2026-03-02T09:00:00","zone":"Europe/Brussels"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("time.generate_recurrence", {"startDateTime":"2026-03-02T09:00:00","zone":"Europe/Brussels","rrule":"FREQ=WEEKLY;BYDAY=MO,WE;COUNT=4"});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| RRULE_INVALID | 400 | No | The request payload failed this skill's input validation. |
| RRULE_UNSUPPORTED_PART | 400 | No | The skill does not support the requested variant. |
| RRULE_EXPANSION_UNBOUNDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| TIMEZONE_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not parse a VCALENDAR or VEVENT — the input is a rule, not a calendar file
- Does not expand an infinite rule without a bound; the limit always applies
- Does not handle attendees, alarms, or VTIMEZONE definitions
- Does not accept RDATE or EXDATE properties beyond the exceptionDates field
- Does not read the system clock; the series seed is always supplied
- Parsing a full .ics calendar file
- Validating and explaining a rule without expanding it (use time.validate_recurrence)
- Detecting clashes between concrete intervals (use time.detect_date_conflicts)
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "time.generate_recurrence",
"input": {
"startDateTime": "2026-03-02T09:00:00",
"zone": "Europe/Brussels",
"rrule": "FREQ=WEEKLY;BYDAY=MO,WE;COUNT=4"
}
}Examples
Expand a twice-weekly rule
{
"startDateTime": "2026-03-02T09:00:00",
"zone": "Europe/Brussels",
"rrule": "FREQ=WEEKLY;BYDAY=MO,WE;COUNT=4"
}{
"_meta": {
"tzdbVersion": "2026b",
"engineVersion": "4.3.0",
"engine": "ical4j",
"windowEnd": "2036-03-02T09:00:00+01:00"
},
"occurrences": [
"2026-03-02T09:00:00+01:00",
"2026-03-04T09:00:00+01:00",
"2026-03-09T09:00:00+01:00",
"2026-03-11T09:00:00+01:00"
],
"truncated": false,
"count": 4,
"normalized": "FREQ=WEEKLY;COUNT=4;BYDAY=MO,WE",
"infinite": false,
"excludedCount": 0
}An infinite rule is bounded by the limit
{
"zone": "Europe/Brussels",
"startDateTime": "2026-03-28T09:00:00",
"rrule": "FREQ=DAILY",
"limit": 3
}{
"_meta": {
"tzdbVersion": "2026b",
"engineVersion": "4.3.0",
"engine": "ical4j",
"windowEnd": "2036-03-28T09:00:00+01:00"
},
"occurrences": [
"2026-03-28T09:00:00+01:00",
"2026-03-29T09:00:00+02:00",
"2026-03-30T09:00:00+02:00"
],
"truncated": true,
"count": 3,
"normalized": "FREQ=DAILY",
"infinite": true,
"excludedCount": 0
}Recommended use cases
- Turn a stored recurrence rule into the next N concrete meeting times
- Materialize a billing or reminder schedule for a fixed window
- Check what a rule actually produces across a daylight-saving change
Avoid for
- Parsing a full .ics calendar file
- Validating and explaining a rule without expanding it (use time.validate_recurrence)
- Detecting clashes between concrete intervals (use time.detect_date_conflicts)
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.