Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Recurrence Generator

time.generate_recurrence

ProductionEngine wrapper (Class A)Deterministicstrictfastv1.0.0
No network

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

Note
Expands one RFC 5545 recurrence rule into occurrence instants in a named zone, bounded by an occurrence limit and a window.

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

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

generationparsing

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input8 KB

How we handle your data

Sensitive output possible
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.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
rrulestringYesSupported RFC 5545 RRULE body.
startDateTimestringYesZone-less recurrence seed.
zonestringYesIANA recurrence zone or UTC.
windowEndstringNoZone-less expansion window end; omit it to stop at the occurrence cap alone.
limitintegerNoOccurrence cap; omit it for the published default.
exceptionDatesarrayNoFrom 0 through the published exception-date cap of zone-less local occurrences to exclude.

Output schema

FieldTypeRequiredDescription
occurrencesarrayYesFrom 0 through the requested occurrence cap in ascending order.
countintegerYesReturned occurrence count.
truncatedbooleanYesWhether the series continues beyond the response.
infinitebooleanYesWhether the rule has no COUNT or UNTIL.
normalizedstringYesCanonical normalized RRULE.
excludedCountintegerYesMatched exception count.
_metaobjectNoClosed recurrence engine and window receipt with tzdbVersion, engine identity, and applied windowEnd.

API contract

POST /v1/execute
curl
curl -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"}}'
Integrations
TypeScript SDK
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

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
RRULE_INVALID400NoThe request payload failed this skill's input validation.
RRULE_UNSUPPORTED_PART400NoThe skill does not support the requested variant.
RRULE_EXPANSION_UNBOUNDED429NoA size, quota, rate, or time limit was exceeded.
TIMEZONE_UNKNOWN400NoThe request payload failed this skill's input validation.

Other limits

LimitValue
Recommended inline bytes32 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)

Request bodyjson
{
  "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

Inputjson
{
  "startDateTime": "2026-03-02T09:00:00",
  "zone": "Europe/Brussels",
  "rrule": "FREQ=WEEKLY;BYDAY=MO,WE;COUNT=4"
}
Expected outputjson
{
  "_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
}
Tip
COUNT bounds the series, so nothing is truncated

An infinite rule is bounded by the limit

Inputjson
{
  "zone": "Europe/Brussels",
  "startDateTime": "2026-03-28T09:00:00",
  "rrule": "FREQ=DAILY",
  "limit": 3
}
Expected outputjson
{
  "_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
}
Tip
09:00 stays 09:00 across the clock change, so the offset moves and the wall-clock time does not
  • 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)

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.

Tags

date-timegenerationparsing