Development

How to Write and Test Time-Dependent Code

By June 10, 2014 No Comments

Lately I’ve been writing a lot of code that deals with time—from time zones and scheduling tasks to scheduling tasks from different time zones. Time can be really tricky. It’s notoriously hard to debug and its gotchas are easy to overlook. Here are a few tips for writing and testing time-dependent code that I wish I could send back in time to myself:

Be careful of what assumptions your code makes about time

There’s a great blog post from Noah Sussman called “Falsehoods programmers believe about time“. If you have the time, it’s definitely worth a read, but I’ll summarize a few of the key takeaways here:

  • Years don’t always have 365 (or even 366) days.
  • Minutes don’t always have 60 seconds. (See this fascinating post about how Google handles leap seconds.)
  • February doesn’t always have 28 days.
  • The server’s clock may be fast or slow. Sometimes wildly.
  • The client’s clock may be fast or slow. Sometimes wildly.
  • If the server and client are out of sync, they might not even be consistently out of sync.
  • The offsets between two time zones don’t always remain constant (e.g., one observes DST, the other doesn’t). They might even change while your program is running.
  • Time zone offsets are not always a whole number of hours.
  • The standard library’s sleep function doesn’t necessarily sleep as long as you tell it to.

Depending on the problem domain and requirements, these won’t all be important, but it’s good to at least be aware of false assumptions when writing code.

Use UTC when possible

If your requirements allow it, storing times in UTC will save you a lot of headaches down the road. For display, it’s easy to convert a UTC timestamp to local time in a particular zone. UTC doesn’t observe Daylight Savings Time, so you don’t have to worry about bugs caused by the clock moving forward or backward an hour. If you later add or move servers into another time zone (say, new instances in another AWS region for international users), with UTC you won’t have to worry about server clocks being set differently.

Test your app from a VM

An easy way to test time zone support is to launch the application in a virtual machine that’s set to another zone. For applications with client and server components, this lets you run the server in one time zone on your host machine, while testing your client in another time zone on the guest machine.

If your application runs in a web browser and uses JavaScript-generated timestamps, don’t forget to make sure that running new Date() in the console outputs the date and time you expect.

Test your app outside normal business hours

If any part of your application behaves differently based on the date, day, or time of day, be sure to test it outside of normal development hours. If you only test your app Monday through Friday, 9 am to 5 pm, how can you be sure that it functions correctly Saturday night at 11 pm?

Now I don’t mean to say that you should spend Saturday night testing software. Instead, just set your system’s clock forward or use a VM as described above.

Mock time in your test suite

In addition to testing it manually, it’s a great idea to test different edge cases in your unit tests. For example, what happens if the code runs as the clocks are adjusted for Daylight Savings Time? Does the code work on February 29th? How does it recover when a sleep(1) call actually takes 5 seconds?

Setting up these scenarios by mocking the current time is usually easier than you’d think. If you’re working in Ruby, the Timecopgem provides the ability to set the time, freeze time and even scale time. If that last one sounds strange, imagine you wanted to simulate a 1 month billing cycle in 30 seconds. In Java, the Joda library has a method for setting the current time. In JavaScript, mocking time is so easy you can do it yourself:

// Freeze time
var frozenTime = new Date();
Date = function() { return frozenTime; };

// Or offset it. 
var offsetMs = 1000;
var _Date = Date;
Date = function() { return new _Date(_Date.now() + offsetMs); };

Wrapping Up

Hopefully one of these tips saves someone from a time-related bug. If I missed anything, feel free to share in the comments below.

Web Application Startup Guide

A 30-page ebook that covers positioning, marketing, pricing, and building your startup product, plus more.