Picture of clocks in London, New York, Tokyo and Moscow. Adjusted for their time zone
Image by Michal Jarmoluk

Testing dates and timezones using Jest

Ben Grynhaus
4 min readMay 24, 2021

--

Without setting a global TZ environment variable, and being able to fake the current system time to keep tests stable

Preface

There are already a lot of posts covering testing dates and timezones in JavaScript, but all the ones I encountered were either too basic, too overkill (running all tests with the TZ environment variable), or didn’t cover mocking both the date and the timezone.

Dates are hard, and time zones are harder, especially when done in JavaScript. There are multiple libraries to work around the difficulties of using Date, and a proposal to replace it with something more modern.

Testing these can also be difficult, since you may want to test edge cases (e.g. leap year), or multiple timezones. Most solutions to this issue in the JavaScript ecosystem focus on one of 3 things:

  1. Faking the system time and allowing you to manually control it and trigger timers (e.g. setTimeout, setInterval etc.). Without waiting for the actual time specified to pass.
  2. “Locking” the system time to some predetermined value, so you can perform your tests on it. For example, if you’re writing a date picker, you may want to “lock” the time to a given date, and then make sure that the picker behaves as expected, in that time. Otherwise, your expects become harder to reason about, since they’re dynamic by nature.
  3. Changing the timezone of the system, so you can test how your code behaves in different environments. This is especially relevant in web apps, where you don’t have control over the environment where the code runs, since it runs on users‘ machines.

Each of these issues has multiple, isolated solutions in the form of npm packages or ways of running your tests:

Manually controlling timers

Jest has pretty good built-in timer mocks, and Sinon.js’ fake timers are also very good (and easier to use, from my experience).

See their docs for examples, are there is a lot to cover, and they do a great job at it!

Locking system time

For locking the system time you can use mockdate, which lets you specify a date-time in a test, after which subsequent usages of Date would use that time:

Faking the timezone

There are two main approaches to solving the issue of faking a timezone in tests:

  1. Set the TZ environment variable before running Jest:
TZ=UTC jest

This will cause all tests to run under the UTC timezone. Regardless of whether or not the test is even timezone-related. This is even more of a problem if you want to test multiple timezones. You’ll have to resort to solutions like:

TZ=UTC jest && TZ=America/New_York jest

(or running them concurrently using something like concurrently), but again — even if only a few of your tests require timezone testing, you’re running all of them for all time zones, and also face the fact that the test code is not aware of the timezone it's running in.

2. Use a package like timezoned-date, which lets you create a new Dateconstructor, with a dynamic offset:

is works great, just remember to save the original Date before overriding it and restore it afterward:

Locking system time AND setting the timezone

Using mockdate and timezoned-date together does pose an issue that might not be obvious — since both (directly or indirectly) override the global Date, the implementation matters — especially in mockdate‘s case, which overrides the resets the original Date itself. At the time of this writing, the implementation saves Date when the module is first require -ed:

// mockdate.ts
const RealDate = Date;

This means that this code wouldn’t behave as expected:

As you can see — mockdate worked fine, but didn’t take the Date the constructor we just created & overrode using timezoned-date into account.

To fix this we can create a test utility function that works around the issue while abstracting the problematic implementation, as well as simplifies the test code:

Usage is then pretty simple:

Conclusion

I hope this post helped you get a better understanding of the various ways to test dates in JavaScript, especially when you use 3rd party modules, which rely on global variables and don’t get their dependencies injected (e.g. using Date).

The implementation of setupMockDate introduced above should be generic enough to cover different use-cases, and might benefit from being published to npm directly to save you from having to copy-paste this into your codebase. If I do publish it, I’ll be sure to update this blog post.

--

--