cronkit.org
Cron expressions, explained and converted — in your browser

Cron tools › Convert

Cron to iCal RRULE

RFC 5545 RRULE · .ics with CRLF and folding · checked by expansion

Put a cron job on a calendar. The converter writes the RRULE (or two, when cron's day fields combine with OR), builds a valid .ics file around it, and proves it by parsing the rule back and expanding it from its DTSTART — the occurrences must be the cron line's runs. A second download lists the next runs as plain events for calendars that struggle with complex rules.

RRULE

Next runs as single events

Each run becomes its own event with a UTC start time, so every calendar shows the right moment. Nothing repeats, so re-export when the list runs out.

Try one

How to turn a cron schedule into a calendar event

  1. Paste the cron expression. Quartz, Spring and AWS expressions work too, including L, # and nL.
  2. Choose Floating to show the event at the same clock time wherever the calendar is, or UTC to pin it to an instant.
  3. Copy the RRULE into your own code, or download the .ics and import it into a calendar.
  4. For a calendar that mishandles rules, pick the zone the job runs in and download the next runs as single events.

How cron maps to RRULE

CronRRULEWhy
0 9 * * 1-5FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=0with DAILY, BYDAY limits the days and BYHOUR/BYMINUTE expand the times
0 0 1,15 * *FREQ=DAILY;BYMONTHDAY=1,15;BYHOUR=0;BYMINUTE=0BYMONTHDAY also limits
*/20 * * * *FREQ=HOURLY;BYMINUTE=0,20,40minutes listed, not INTERVAL (see below)
0 0 12 ? * 3#2 (Quartz)FREQ=MONTHLY;BYDAY=2TU;BYHOUR=12;BYMINUTE=0numbered BYDAY needs MONTHLY or YEARLY
6L (Quartz dow)BYDAY=-1FRlast Friday of the month
L (Quartz dom)BYMONTHDAY=-1BYMONTHDAY takes -31 to -1 from the end
LW (Quartz dom)FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1last weekday; one event per time of day
0 0 1 * 1two eventscron ORs the day fields; an RRULE's parts all have to match

Why */7 is not INTERVAL=7

It is tempting to write cron's */7 as FREQ=MINUTELY;INTERVAL=7. They are different schedules. Cron's step restarts every hour — :00, :07 … :56, then :00 again, four minutes later — while INTERVAL counts from DTSTART and keeps going across the hour: :00, :07 … :56, 1:03, 1:10. This converter always lists the values in BYMINUTE / BYHOUR, which match cron exactly, and the check would catch the difference if it did not.

Floating time or UTC?

Floating (no zone) means "09:00 on whatever clock the calendar uses". It suits a personal reminder, and it follows daylight saving automatically. A colleague in another zone sees it at their 09:00, though. UTC pins every occurrence to one instant for everyone, but the cron fields are then read as UTC, so a job meant for 09:00 local time drifts by an hour when the clocks change. A TZID would be the third option; it needs a VTIMEZONE definition in the same file, which this page does not generate, so it never writes one.

FAQ

Why did I get two events?

Because the cron line restricts both day-of-month and day-of-week, which in cron means "either". Every part of one RRULE has to match, and RFC 5545 says RRULE SHOULD NOT occur more than once in an event, so the file holds one event per half. Together they cover exactly the cron runs.

Is the .ics file valid?

It uses CRLF line endings, folds lines longer than 75 octets, escapes commas, semicolons and backslashes in text, and gives every event a UID, DTSTAMP and DTSTART that is itself the first occurrence of the rule.

What cannot be converted?

Quartz's "nearest weekday" W has no RRULE form, a year list or step cannot be written (a single range of years becomes DTSTART plus UNTIL), and @reboot has no time at all. The "next runs" download still works for all of them.

What happens to a run in a skipped DST hour?

RFC 5545 says an occurrence at a local time that does not exist must be ignored. Linux cron (cronie) instead runs a fixed-time job right after the clock jumps, so on that one day a floating calendar entry and the real job disagree.