At Cucumbertown we recently changed our email scheduling mechanism so that our users around the world get their weekly morning emails actually in the morning – say Wednesday 9.30 AM. Starting this week our users in different time zones should start getting their emails in the morning, which most users would appreciate.

So taking advice from the wonderful Stride app blog we took a stab at implementing this. And during the execution we realized that the Django ecosystem makes this ridiculously simple to do.

There are basically two main tasks involved.

  • First we need to know the time zone to which users belong – We do this using plain javascript.
  • Second we need to schedule emails based on user’s time – this is the improvement with python

Determining the time zone of users is pretty straightforward and all we need is a few lines of JavaScript. We actually look only for the time zone difference which the browsers provide. Here is how ours look like:

This gives the timezone difference in minutes: for e.g., if the user is at – 8.00 UTC, the tz_offset would be +480 (yes, the sign is opposite here). In our case we actually check if the user’s current time zone that we already have is different from what the JavaScript just received, and make the POST call only if it has changed.

The field tz_offset is Integer type, which stores UTC timezone difference in minutes.

Users timezone populated. Timezone displayed in hour for readability

Now that we have user’s time zone, scheduling is as simple as a one liner, thanks to Celery’s succinct @periodic_tasks decorator notation.

All we have to do is run a celery cron task (in our case every 30 minutes) and check which user’s are at Wednesday 9.30 am. And then schedule the email for those users.

Figuring out users who are at Wednesday 9.30 is high school math. Our python function would look something like this:

What this function does is just find the difference between current server time (now) and the time email (email_time) have to be send, taking into account the difference in days if any (hour_diff). So timezones whose UTC time difference (in minutes) is equal to the calculated difference, would be at email_time.

If you noticed, we are returning a range of time zone offsets not just one value. Since we schedule our cron tasks every 30 minutes, we need all values which are within a 30 minute maximum range.

email_time is an object of a simple class which makes it easy to describe times to be scheduled, like Thursday 10.00 pm.

That’s it. Now that you have your time zones, you can query your Database, which users have these time zones and send emails to them.

We would love to hear about improvements and hacks on top of this.