Skip to main content

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

MicroSkills Reference

Recurrence Validator

time.validate_recurrence

ProductionEngine wrapper (Class A)Deterministicstrictfastv1.0.0
No network

Overview

Parses an RFC 5545 recurrence rule with ical4j and reports whether it is valid, what it means in a fixed English rendering, which parts it declares, and up to 10 preview occurrences. Structural observations are returned as findings: a BYMONTHDAY that skips short months, a BYYEARDAY=366 that skips common years, a BYWEEKNO whose meaning depends on an undeclared WKST, and a rule with no COUNT or UNTIL. A malformed rule is a finding with valid: false, not an error, because evaluating the rule succeeded. This validates rule syntax and structure — it does not judge whether the schedule is the one you intended.

A valid RRULE can still skip months or repeat forever — we parse with ical4j, surface structural findings, and preview a few occurrences before you materialize the full series.

Reliability

Same rule, same verdict — for a given ical4j and IANA time-zone database version, both reported in _meta. The explanation comes from a fixed template rather than generated prose, so it is identical across locales and releases, and we read no clock. A rule that does not parse, or that is too expensive to expand, comes back as an ordinary success with valid false and an error-severity finding — so branch on valid rather than on your error path, and remember that valid true means the syntax holds, not that the schedule is the one you meant. Only two conditions stop us evaluating at all. 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, the same text always fails; next: the message names the part — drop it or correct the spelling, and move a DTSTART into startDateTime. Note that this one is a failure rather than a finding, so an unsupported part and a malformed value reach you by different routes. 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 rule-reading exercise and returns a short verdict, a sentence, and a handful of instants. A successful call costs 1 execution credit.

When to use it

When a rule arrives from a user or upstream system and you need parse validity, a fixed-template explanation, and a short preview.

Do not use to materialize a long series (use time.generate_recurrence), parse a .ics file, or author a rule from natural language.

Powered by ical4j, the reference-grade RFC 5545 iCalendar library for recurrence-rule parsing and validation.

Scope

Note
Parses one RFC 5545 recurrence rule, reports structural findings, renders a fixed-template explanation, and previews at most 10 occurrences.

Non-goals

  • Does not expand a full series (use time.generate_recurrence)
  • Does not author a rule from a natural-language description
  • Does not correct an invalid rule or suggest a replacement
  • Does not judge whether the schedule matches the caller's intent — only its syntax and structure
  • Does not read the system clock

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

parsingvalidation

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.
previewCountintegerNoPreview cap; omit it for the published default.

Output schema

FieldTypeRequiredDescription
validbooleanYesWhether the rule parses and expands safely.
findingsarrayYesFrom 0 through 16 bounded recurrence findings, each a closed code, message, path, and severity.
explanationstringYesFixed-template English explanation.
partsobjectYesPurpose-bound bounded map of declared RFC 5545 part names to parsed values.
normalizedstringNoCanonical normalized RRULE.
nextOccurrencesarrayYesFrom 0 through the requested preview cap of ISO offset occurrences in ascending order.
infinitebooleanYesWhether the valid rule is unbounded.
_metaobjectNoClosed recurrence-engine version receipt with tzdbVersion, engine identity, and engineVersion.

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.validate_recurrence","input":{"rrule":"FREQ=MONTHLY;BYMONTHDAY=31","startDateTime":"2026-01-31T09:00:00","zone":"UTC"}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("time.validate_recurrence", {"zone":"UTC","startDateTime":"2026-01-31T09:00:00","rrule":"FREQ=MONTHLY;BYMONTHDAY=31","previewCount":3});

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_UNSUPPORTED_PART400NoThe skill does not support the requested variant.
TIMEZONE_UNKNOWN400NoThe request payload failed this skill's input validation.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not expand a full series (use time.generate_recurrence)
  • Does not author a rule from a natural-language description
  • Does not correct an invalid rule or suggest a replacement
  • Does not judge whether the schedule matches the caller's intent — only its syntax and structure
  • Does not read the system clock
  • Materializing a long series (use time.generate_recurrence)
  • Parsing an .ics file
  • Turning prose into a rule

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "time.validate_recurrence",
  "input": {
    "zone": "UTC",
    "startDateTime": "2026-01-31T09:00:00",
    "rrule": "FREQ=MONTHLY;BYMONTHDAY=31",
    "previewCount": 3
  }
}

Examples

A monthly rule that skips short months

Inputjson
{
  "zone": "UTC",
  "startDateTime": "2026-01-31T09:00:00",
  "rrule": "FREQ=MONTHLY;BYMONTHDAY=31",
  "previewCount": 3
}
Expected outputjson
{
  "explanation": "Every month on day 31",
  "findings": [
    {
      "code": "RRULE_SKIPS_SHORT_MONTHS",
      "message": "BYMONTHDAY=31 yields no occurrence in months that are too short.",
      "path": "/rrule",
      "severity": "warning",
      "details": {
        "monthDays": [
          "31"
        ]
      }
    },
    {
      "code": "RRULE_UNBOUNDED",
      "message": "The rule declares neither COUNT nor UNTIL and therefore repeats indefinitely.",
      "path": "/rrule",
      "severity": "info",
      "details": {}
    }
  ],
  "valid": true,
  "nextOccurrences": [
    "2026-01-31T09:00:00Z",
    "2026-03-31T09:00:00Z",
    "2026-05-31T09:00:00Z"
  ],
  "parts": {
    "BYMONTHDAY": "31",
    "FREQ": "MONTHLY"
  },
  "infinite": true,
  "_meta": {
    "engine": "ical4j",
    "engineVersion": "4.3.0",
    "tzdbVersion": "2026b"
  },
  "normalized": "FREQ=MONTHLY;BYMONTHDAY=31"
}
Tip
February and April are absent from the preview, and a finding says why

A malformed rule is reported, not thrown

Inputjson
{
  "startDateTime": "2026-03-02T09:00:00",
  "zone": "UTC",
  "rrule": "FREQ=WEEKLY;BYDAY=XX"
}
Expected outputjson
{
  "findings": [
    {
      "code": "RRULE_MALFORMED",
      "message": "Part 'BYDAY' has a value the RFC 5545 grammar does not accept.",
      "path": "/rrule",
      "severity": "error",
      "details": {}
    }
  ],
  "_meta": {
    "engine": "ical4j",
    "engineVersion": "4.3.0",
    "tzdbVersion": "2026b"
  },
  "valid": false,
  "explanation": "",
  "nextOccurrences": [],
  "infinite": false,
  "parts": {}
}
Tip
The request was evaluable, so the answer is a finding with valid: false
  • Check a user-entered recurrence rule before storing it
  • Show a human-readable summary of a stored rule in a UI
  • Discover that BYMONTHDAY=31 silently skips February, April, June, September, and November

Avoid for

  • Materializing a long series (use time.generate_recurrence)
  • Parsing an .ics file
  • Turning prose into a rule

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

validationdate-timeparsing