(RFC reading for fun and pragmatism!)
My girlfriend is a nurse, and around these parts that means she works shift work. Her schedule is a block of shifts (sometimes day shifts, sometimes evening shifts) followed by a block of days off (which also varies as to the number of days), and the entire schedule itself repeats every so many weeks. Basically, picture something like this:
...and repeat.
While there is a pattern to it, it essentially means that without access to a copy of her schedule readily at hand, it is literally impossible to know if we are free on some random day next month. So obviously, I want an electronic version at the ready for these types of inquiries.
Unfortunately, it is also incredibly tedious to enter all of these shifts one by one, by hand, into something like Google Calendar, because it requires manually creating dozens of events (“repeats every other Monday” unfortunately does not cut it here :P), and on each of them, manually selecting “Repeats,” manually selecting “Custom…”, manually selecting “every X weeks”, all without introducing any errors. Ugh.
Fortunately, here to save the day… the iCalendar specification!
The iCalendar specification (as in RFC 5545, not to be confused with good ol’ iCal, now known as Apple’s Calendar app) is a “data format for representing and exchanging calendaring and scheduling information such as events, to-dos, journal entries, and free/busy information, independent of any particular calendar service or protocol.”
Basically, it’s a list of rules about how to describe events in computer-friendly language, and if you follow those rules, users can import your events consistently in whatever various calendaring programs they might be using. Glancing through the specification, you’ll see it talks about how to specify all kinds of features you may have seen in various calendaring apps, such as:
Specify a location
Specify a time zone
Repeat an event with a certain frequency
Note whether someone’s an optional or required attendee
Denote whether it shows up as busy or free time on someone’s calendar
Send an alert a few minutes before an event happens
...and much, much more.
If you’ve ever received an email with a .ics
file attached, and when you click it you’re prompted to add an event to your calendar, such as below… Congratulations! You officially have experience with iCalendar! :D
Let’s talk about what iCalendar looks like under the hood.
The “simplest thing that can possibly work” in terms of iCalendar is a text file, with an .ics
extension, that contains the following properties:
<html>…</html>
wraps around a web page).“-//Google Inc//Google Calendar 70.9054//EN”
for Google Calendar and “-//Apple Inc.//Mac OS X 10.14.6//EN”
for Apple Calendar.[buncha-random-chars]
and [buncha-random-chars]@[yourdomain.com]
are both common patterns.
Here’s a simple example tying all of that together:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp//Example Calendar App 1.0//EN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20210310T001345Z
DTSTART:19991231T235959
DTEND:20000101T000000
SUMMARY:Party like it’s 1999!
END:VEVENT
END:VCALENDAR
If you save this as “y2k.ics
” and import it, you should see a new event appear at the very tail end 1999 that lasts from 11:59:59PM on December 31, 1999 until midnight on January 1, 2020!
The RFC gets into this at length, but the basic gist is:
If it’s a date only, it’s specified in the format of YYYYMMDD
.
If it’s a date and time, it’s specified in the format of YYYYMMDDTHHMMSS
(with HH
in 24-hour time). If it’s a UTC time (see below) it also has a Z
at the end.
There are three different ways to specify a date and time such as 11:59:59PM on December 31, 1999:
Date with local time: Used when you want an event to be at the same time, regardless of a person’s location.
DTSTART:19991231T235959
Date with UTC time: Used when you want an event to be at an absolute time across all geographic locations, and disregarding Daylight Savings Time. (Note: DTSTAMP is always in this format.)
DTSTART:19991231T235959Z
DTSTART;TZID=America/Vancouver:19991231T235959
In the above example, we went with the “date with local time” format so that the event would be at the same time regardless of location, since exactly when the “last minute of 1999” is differs across the globe. But for tracking shift schedules, “Date with local time and timezone reference” makes more sense, because if I fly to Europe for work, I don’t want the start times to shift by several hours.
Nope, that semi-colon after DTSTART
in the third example is not a typo. “Property parameters” such as TZID
are passed in with a semicolon rather than a colon. (See List and Field Separators) This will also come into play with...
Ok, so we already have enough information to create each shift schedule the first time, but note that it repeats every X weeks. How do we handle that?
Enter Recurrence Rules (RRULE
)! This property defines a rule or repeating pattern for recurring events. Here are just a few of the options that it supports:
FREQ: How often the event occurs. Some examples are WEEKLY
, DAILY
, and even SECONDLY
.
INTERVAL: How many FREQ
s should there be in between events?
COUNT: Repeat the event X times. (Useful if you have a class that runs for 4 weeks, for example)
UNTIL: Alternatively, this will repeat the event until a certain date.
BYDAY: Allows you to say something only happens on certain days of the week.
The RFC has all sorts of examples.
For our purposes, we need events that:
FREQ
of WEEKLY
and an INTERVAL
of 8 since the schedule repeats every 8 weeks.UNTIL
date of 12/31/2021
because the schedule gets reset every calendar year, and next year will be something different! (Hence my very strong desire to write this process down. ;))
Put it all together, and you get:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Webchick Inc//Webchick’s Crappy Bash Script 1.0//EN
BEGIN:VEVENT
UID:3231CB09-1E10-4910-BB24-B358629890B7
DTSTAMP:20210314T090733Z
SUMMARY:Day Shift
DTSTART;TZID=America/Vancouver:20210111T080000
DTEND;TZID=America/Vancouver:20210111T180000
RRULE:FREQ=WEEKLY;INTERVAL=8;UNTIL=20211231
END:VEVENT
... (repeat x many more events) ...
BEGIN:VEVENT
UID:F20376E5-FA0F-429D-AB1F-3F0155EE10BA
DTSTAMP:20210314T090734Z
SUMMARY:Evening Shift
DTSTART;TZID=America/Vancouver:20210208T173000
DTEND;TZID=America/Vancouver:20210208T233000
RRULE:FREQ=WEEKLY;INTERVAL=8;UNTIL=20211231
END:VEVENT
... (repeat x many more events) ...
END:VCALENDAR
And when imported into Apple Calendar:
And there you have it!
So. The next time you get one of those .ics files, try popping it open in a text editor and see how much of it makes sense now!
PS: If you want to see the bash script I used to generate this, it’s at https://github.com/webchick/shiftycal. If you want to go it alone, this handy validator is your friend!
Also published on: https://dev.to/webchick/using-icalendar-for-semi-predictable-but-oddball-events-20nk