Skip to the content.

⬅ Back

Using Google Calendar for time-based rules

Back at TIER, we’ve been doing various product workarounds since we didn’t have time based rules in the system for years. Prohibited zones that are only active during the weekends, prompts in the app are only available in the evening, some other example were all the company wished very hard for, but engineers kept pushing it back, since it’s pretty complex as I’m going to show in a minute. We had a problem this time, because this time, we even more definitely wanted to have a prompt in the app, for a driving-under-influence prevention feature, and showing that only made sense in the evenings. This is how we solved it.


Pain

Driving under influence is a problem with electric scooters, so we wanted to introduce a way to raise some awareness. Our designers created multiple prototypes and we selected the best one, but after interviewing the our city ops and management teams, we found that they definitely didn’t want any kind of potentially churn-creating UI element to be shown during the day. For probably the hundredth time, an engineer someone in the company (me) started having panic attacks because the time based configs problem was still not solved, but now we had to find a way to do it.

Basically, we wanted to be able to answer the following question in any time, for any city, for any feature: “Is Feature enabled in City Right Now?” - and hopefully make setting up the answer to this as small a hassle as possible.

We sat down to the drawing board and came up with some rules our city operations teams might think of:

And I’ve created this list in the evening while writing this article, so you can imagine how large the imaginable space of these rules could be. From any time to any time, with any custom repetition and exclusions and extensions and YIKES.


It gets complicated

For the first few examples, a specification where you define a time and a set of days could make sense (excuse the pseudo-YAML):

timeStart: 5pm
timeEnd: 11pm
days: fri, sat, sun

But what do you do with rules reaching over midnight? You could have multiple rules, and just go over all of them and see if one applies:

rules:
  - timeStart: 10pm
    timeEnd: 12am
    days: fri, sat, sun
  - timeStart: 12am
    timeEnd: 3am
    days: sat, sun, mon

This almost covers everything within a single week. We still need to introduce exclusions and one-off extensions - with those, you can cover biweekly repetitions too:

rules:
  - timeStart: 10pm
    timeEnd: 12am
    days: fri, sat, sun
  - timeStart: 12am
    timeEnd: 3am
    days: sat, sun, mon
  - timeStart: 12am
    timeEnd: 3am
    days: mon
    exclude: true
  - timeStart: 12am
    timeEnd: 3am
    date: 2023-05-11

It gets messy, and it’s hard to understand what’s happening at a glance. We didn’t even discuss the implementation, but you think about configuring this every time, and changing that configuration. This is where you start to really panic and invoke upon the might of the team’s PM to ask cities to maybe please just be NORMAL, and let us introduce some constraints, but cities realllllly want to show a screen in the app for their one-off football game.


It gets… simple?

There’s one tool which handles this kind of time complexity really well though: calendars. Just picture Google Calendar: you can create any kind of event length, with a lot of options to repeat them, do one-off exclusions on the repeated occurrences, do one-off events, have overlapping rules - it’s all we ever wanted. Google Calendar provides a super nice UI to modify your events, with a recurrence editor too.

Recurrence

Google Calendar uses a standard format for storing these event details, called iCalendar (RFC5545), which comes along with a spec for recurrence rules (RRULE). Google Calendar (and most other calendar tools) gives you a way to export your events in this format (and maybe even stores the events in iCal in the first place). What’s even better, you can get a “Secret address in iCal format” under the calendar sharing settings, which is basically a secret, passwordless HTTP GET endpoint that you can download the iCal from.

I found a post while researching the subject that resonated a lot:

Starting with the most basic rules of repeating every day, and going into complete insanity of repeating every other Thursday, starting from next week and until the beginning of next year every other month, RRULEs can drive even the calmest of people completely insane.

What I get from this post though that it was probably written with the frustration of being on the implementing side of a calendar client. If you’re not looking to implement a calendar though, and have some lib to parse RRULES? You can connect your Google Calendar as a client, the “database” it edits is the list of all events,then create events that match a feature name, read the events, read their RRules, decide if the current time intersects a recurrence of an event, and you’re golden.

Modules


How it works

A feature then becomes an event. Any audience groups or cohorts, that you want to time the feature differently for (in our example, cities) go into the title or the description, or wherever (something like event name: Driving Under Influence;Budpapest).

I set up some 5 minute polling in the service that wanted to rely on this config, that downloads the iCal as text, and used ical to parse the file; this being a node service, but I’m sure other languages have iCal implementations too.

Whenever the big question arrives - asking whether Driving Under Influence should be enabled in Budapest, Right Now - the following happens:

Feature decision

The whole logic is not too complicated if you don’t think about it too hard - and you don’t have to, since Google Calendar, and iCal, and these wonderful libs abstract away all the gory details of how RRULEs need to be set up, and how you can generate all the occurrences.


Feels good

From a maintainability perspective, this is pretty good, since there’s only one dependency, which is the “database” of your events in Google Calendar, which is hosted by Google - and even if they go down, we have an in-memory cache that can serve all saved recurrences even two weeks later (if your service doesn’t restart).

If for some reason we want to change any components, it’s simple to do so:

From a usability perspective, it’s even better: we can just hand out the calendar to the city operations managers themselves so they can directly set up the events they want, and they can also see through what’s enabled at the moment, since your calendar gives you an accurate picture of what event(s) you have happening at any moment. No hassle in setting it up, no hassle in understanding it. You actually give the tool to people to configure their stuff ON AN INTERFACE THAT’S ALREADY FAMILIAR TO THEM. If there’s one major win in this whole idea, it’s this.

Calendar event