How do I set a timezone in my Jest config?

ReactjsJestjs

Reactjs Problem Overview


✗ npx jest --version
24.5.0

Got a set of jest tests that are timezone sensitive. We typically run them with an npm script: "jest": "TZ=utc jest"

With the TZ set to utc I get values like this in snapshots:

modificationDate="2019-01-08T00:00:00.000Z" 

Without it I get:

modificationDate="2019-01-08T08:00:00.000Z"

Is there a way to set that in my jest config so I can run npx jest at the command line without having to go through the NPM script? There's nothing in config docs about this.

I tried adding these two to my jest.config.js. Neither one worked:

  TZ: 'utc',

  globals: {
    TZ: 'utc',
  },

Sure, it seems trivial to work around but I'm surprised Jest doesn't have a way to configure this for tests.

Reactjs Solutions


Solution 1 - Reactjs

This does not work on windows prior to node 16.2.0 (and prior to 17.0.1 in node 17) - see https://github.com/nodejs/node/issues/4230


The problem with process.env.TZ = 'UTC'; is, that if something runs before this line and uses Date, the value will be cached in Date. Therefore process.env is in general not suitable for setting the timezone. See https://github.com/nodejs/node/issues/3449

So a better way is to use an actual env variable, but for tests this will work:

1. Add this to your package.json

  "jest": {
     ...
     // depending on your paths it can also be './global-setup.js' 
    "globalSetup": "../global-setup.js"
  }
}

2. Put this file besides package.json as global-setup.js

module.exports = async () => {
    process.env.TZ = 'UTC';
};

3. Optional: Add a test that ensures UTC execution

describe('Timezones', () => {
    it('should always be UTC', () => {
        expect(new Date().getTimezoneOffset()).toBe(0);
    });
});

The normal setupFiles did not work for me, since they run too late (jest: ^23.5.0). So it is mandatory to use the globalSetup file.

Solution 2 - Reactjs

If you are running tests with npm scripts, ie: npm run test, you can pass in the timezone like so:

  "scripts": {
    "test": "TZ=UTC jest"
  },

I also personally feel that this (vs the process.env methods) is cleaner and easier to identify the timezone when debugging issues on remote CI servers.

Solution 3 - Reactjs

I just run into the same issue and I was able to resolve it by adding process.env.TZ = 'your/timezone'; to my jest.config.js.

Maybe this helps in your case as well :)

process.env.TZ = 'UTC';

module.exports = {
  ...
};

Solution 4 - Reactjs

Update: This doesn't produce deterministic results if you want to dynamically change the TZ but it will work if you only want one TZ. It could be an alternative to specifying at the script level, but I think that would be the better answer here.

The issue is that by setting process.env.TZ at runtime, it will bleed across tests creating non-deterministic behavior (side effects) during a regular Jest test run. It may work if you use --runInBand which runs tests serially but I wouldn't count on it.

I also found an old archived issue about dynamic timezones in Node and it looks like dynamically adjusting it won't work in general.

Instead I will probably end up with multiple scripts that each set TZ before launching jest.


For my use case, I actually wanted to run tests under different timezones for specific date-based edge cases. Sometimes users run into timezone-based bugs and we want to cover that easily within our test suites.

We run all tests in the project by default using one of the proposed answers here, by setting TZ=UTC in the npm script (e.g. TZ=UTC npm run jest. This runs all tests under the UTC timezone.

Then, we leverage the testEnvironment configuration which can be set at the test suite-level using the JSDoc pragma @jest-environment. Using this custom test environment, we can then read the suite's desired timezone using a "custom docblock pragma" like @timezone. This enables timezone customization per-test-suite which is not as ideal as per-test but good enough for our purposes.

jsdom-with-timezone.js

const JSDOMEnvironment = require('jest-environment-jsdom');

/**
 * Timezone-aware jsdom Jest environment. Supports `@timezone` JSDoc 
 * pragma within test suites to set timezone.
 *
 * You'd make another copy of this extending the Node environment, 
 * if needed for Node server environment-based tests.
 */
module.exports = class TimezoneAwareJSDOMEnvironment extends JSDOMEnvironment 
{
  constructor(config, context) {

    // Allow test suites to change timezone, even if TZ is passed in a script.
    // Falls back to existing TZ environment variable or UTC if no timezone is specified.
    // IMPORTANT: This must happen before super(config) is called, otherwise
    // it doesn't work.
    process.env.TZ = context.docblockPragmas.timezone || process.env.TZ || 'UTC';

    super(config);
  }
};

tz-eastern.test.js

/**
 * @timezone America/New_York
 */

describe('timezone: eastern', () => {
  it('should be America/New_York timezone', () => {
    expect(process.env.TZ).toBe('America/New_York');
    expect(new Date().getTimezoneOffset()).toBe(300 /* 5 hours */);
  });
});

jest.config.js

module.exports = {
  "testEnvironment": "<rootDir>/jsdom-with-timezone.js"
}

Using this with jest.useFakeTimers('modern'); and jest.setSystemTime() is sufficient for more robust date testing so I thought I'd share this approach for others to benefit from! Since the pragma handling is custom, you could customize this any way you like for your use case.

Sources:

Solution 5 - Reactjs

Up until recently I used the following to mock being in a different timezone:

  beforeEach(() => {
    // Temporarily allow us to alter timezone calculation for testing
    /*eslint no-extend-native: "off"*/
    Date.prototype.getTimezoneOffset = jest.fn(() => 73);
  });

  afterEach(() => {
    jest.resetAllMocks();
  });

This didn't place the testing code in a particular timezone, but made sure that any timezone offset calculations were correctly made. For example:

new Date("2010-10-01") would be 73 minutes earlier than new Date("2010-10-01T00:00:00"), with the former being equivalent to new Date("2010-10-01T00:00:00Z") (UTC timezone) and the latter being in the 'local timezone'

I say "up until recently" because a recent update to date-fns seems to no longer work

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjcollumView Question on Stackoverflow
Solution 1 - ReactjsCanView Answer on Stackoverflow
Solution 2 - ReactjsKFunkView Answer on Stackoverflow
Solution 3 - ReactjsthegnuuView Answer on Stackoverflow
Solution 4 - ReactjskamranicusView Answer on Stackoverflow
Solution 5 - ReactjsShirazView Answer on Stackoverflow