Dealing with dates and times in computer programs is difficult. There are so many things to consider — you have to think about different calendars, time zones, and even leap seconds (What is it? 🤔 Check the video below!).

The Problem with Time & Timezones

Recently, I have experienced such a problem on my own while setting up a remote Continuous Integration pipeline.

Scene

I work on a project written in React. We write tests in Jest and as a time library use Moment.js (there are other interesting alternatives, e.g. date-fns or Day.js).

For the CI we use Jenkins. It has some drawbacks — the thing that annoyed me the most was that we did not have an indicator of a branch pipeline status directly in Merge Request in GitLab. So I created a .gitlab-ci.yaml pipeline definition and was excited to test it.

Problem

It turned out that there were some problems with setting up the CI (for example it is not possible to run node in docker alpine images) but they were fairly easy to fix. The main trouble caused a test in Jest.

After a short investigation, it turned out that the problematic test is connected with a Unix timestamp. We have a React component that takes a timestamp as a prop and then computes some values and outputs them as time in the H:MM format. Somehow on the remote GitLab runner all the times were shifted by one hour! 😕

Investigation

Generally, tests should be time-agnostic. For that reason, we use MockDate package to set a constant time for times in a given test file. But the problem here was of a different nature.

Locally and on our Jenkins server all the tests were passing. The only place where the code did not work, was the GitLab CI server.

The creation of the failing component looks like:

<Component timestamp={timestamp} />

In the test the timestamp was passed in and in the render function moment(timestamp).format('H:mm') was called. For example, for timestamp=1510248240000 (so 9 November 2017 18:24:00 GMT+01:00) the expected output was 18:24 , but the actual result was 17:24 …

Maybe you can already spot the root cause of the problem? 🧐

Solution

As the title of the article suggests, the problem was with the location of the GitLab server — it was in a different time zone!

To fix the tests, I had to change the way we passed a timestamp in the test. Instead of providing a raw value ( timestamp=1510248240000 ), I let the code to calculate the timestamp value on its own. A new helper function was introduced:

function getTimestamp(time: string): number {

return moment(`2017-11-09_${time}`, 'YYYY-MM-DD_HH:mm').unix()

}

and invoked:

const time = '15:54'

const timestamp = getTimestamp(time)

...

expect(wrapper.find('.time')).toBe(time)

Why does it work?

As mentioned earlier, in the component’s code there is a line:

moment(timestamp).format('H:mm')

The tricky part is moment(timestamp) . If we pass a timestamp , moment is going to transform it into a moment object. The transformation is made for the current time zone of the machine where the code is executed.

In the failing version of the test, a raw Unix timestamp was passed and the output was expected to be in GMT+02:00. As long as the code was executed locally and on Jenkins server in the same time zone, everything worked as expected. But as soon as the test was executed on a machine in GMT+01:00, the problem occurred.

The proposed fix introduces an extra step for obtaining the Unix timestamp:

moment(`2017-11-09_${time}`, 'YYYY-MM-DD_HH:mm').unix()

Because the timestamps both in the test and component are calculated for the same time zone, the problem is fixed! 😎

Conclusion

Always remember that time zone related problems can be tricky. And use a good date-time library! 🗺️