Cron jobs & timezone conversions in PHP
Like most developers, I often use Linux crontab to schedule recurring tasks, it’s easy and straightforward. Currently, I’m working on an application where users have the possibility to schedule recurring tasks themselves via a slick UI.
There are several PHP libraries that allow you to run agnostic cron jobs.
This allows you for example to store several cron job records in your database and pass them to one of these libraries. The library will then do all the heavy lifting for you and decide what cron jobs to run:
I started testing my “schedule” functionality and everything ran as expected. Job well done… or not?
I decided to let some friends of mine beta test the application. To my surprise I received some unexpected feedback:
The scheduled tasks are not running when I configured them to run. They ran x hours too soon/too late
The problem
After some debugging, I figured out what was going on. The server the application is running on has the timezone configured as UTC and the user reporting the bug was located in New York, America.
Let’s consider the following example:
User “New York” adds a scheduled task that has to run every day at 1 pm. The corresponding cron job translates to
0 13 * * *
Because the server is configured to be UTC, this task will run at 1 pm UTC-time which is actually 8 am in New York (EDT). Ok, cool, let’s fix this.
The solution
The solution is fairly simple, “just” convert the hour part of the cron job from the timezone of the user configuring it to UTC. My first attempt was a little bit too straight forward:
This code snippet only takes care of “basic” cron jobs like
- 0 13 * * *
- * * * * *
- * */2 * * *
More advanced cron jobs were still failing to run at the appropriate time, so after some more coding, I came up with a simple yet effective routine:
This snippet uses the magic of regexes to filter out all hours and replaces them with the appropriate ones.
After deploying this, the feedback came back positive:
The scheduled tasks are now running when I configured them to run.
Maybe I’ll create a composer package so other people can use it as a library, don’t know yet 😉