Time
Standard formats for datetimes, durations, and recurring schedules.
Time shows up everywhere. Getting it wrong causes subtle bugs that are painful to track down. These are the standard formats, and every example across these docs follows them.
Datetimes: RFC 3339
All datetimes use RFC 3339 format. Always include the timezone offset.
2025-03-14T14:23:45Z
Breaking it down:
2025-03-14is the date (year-month-day).Tseparates the date from the time.14:23:45is the time in 24-hour format.Zmeans UTC. You may also see offsets like+02:00or-05:00.
Store timestamps in UTC. Convert to local time on the client side.
Durations: ISO 8601
When measuring how long something lasts, use ISO 8601 duration format.
P1Y2M10DT2H30M
Breaking it down:
Pstarts the duration.1Y= 1 year,2M= 2 months,10D= 10 days.Tseparates the date portion from the time portion.2H= 2 hours,30M= 30 minutes.
Mix and match as needed. P5D means 5 days. PT3H means 3 hours.
Recurrence: iCalendar (RFC 5545)
The iCalendar specification (RFC 5545) defines several properties for modeling recurring events. RRULE is the most common, but it's not the only tool available.
RRULE
RRULE defines a repeating pattern.
FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR
FREQ=WEEKLY: repeats weekly.INTERVAL=2: every 2 weeks.BYDAY=MO,WE,FR: on Monday, Wednesday, and Friday.
RRULE handles everything from simple daily schedules to complex yearly patterns.
EXDATE
EXDATE excludes specific dates from a recurrence set. Use it to skip holidays, cancellations, or one-off exceptions without changing the underlying rule.
RRULE:FREQ=WEEKLY;BYDAY=TU
EXDATE:20250325T090000Z,20250401T090000Z
This creates a weekly Tuesday event but skips March 25 and April 1.
RDATE
RDATE adds individual dates to a recurrence set. Use it for irregular additions that don't fit a pattern.
RRULE:FREQ=MONTHLY;BYMONTHDAY=15
RDATE:20250420T090000Z
Monthly on the 15th, plus an extra occurrence on April 20.
Combining Properties
The real power comes from combining these properties. An RRULE defines the base pattern, EXDATE carves out exceptions, and RDATE adds one-offs. Together they can model schedules that would be impossible with a single rule.
RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR
EXDATE:20250324T090000Z
RDATE:20250322T090000Z
Every Monday, Wednesday, and Friday, except skip Monday March 24 and add Saturday March 22 instead. The recurrence engine evaluates the union of RRULE + RDATE, then subtracts EXDATE.
DTSTART
DTSTART anchors the recurrence. It defines the first occurrence and establishes the time-of-day and timezone for the entire series.
DTSTART:20250301T090000Z
RRULE:FREQ=WEEKLY;BYDAY=MO
Without DTSTART, recurrence rules lack a reference point. Always include it when persisting schedules.
In Practice
In practice, these show up as:
{
"created_at": "2025-03-14T14:23:45Z",
"trial_duration": "P14D",
"billing_schedule": "FREQ=MONTHLY;BYMONTHDAY=1"
}
Fields ending in _at are always RFC 3339 datetimes. Fields ending in _on are date-only values (2025-03-14). See Database Naming Conventions for the full suffix reference.