Skip to content

Commit

Permalink
src: make process.env.TZ setter clear tz cache
Browse files Browse the repository at this point in the history
Since the presence of the libc and V8 timezone caches seem to be
a perennial source of confusion to users ("why doesn't it work?!"),
let's try to support that pattern by intercepting assignments to
the TZ environment variable and reset the caches as a side effect.

Fixes: #19974
PR-URL: #20026
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Steven R Loomis <srloomis@us.ibm.com>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
  • Loading branch information
bnoordhuis committed May 23, 2019
1 parent 58fe440 commit eba348b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/node_env_var.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "node_errors.h"
#include "node_process.h"

#include <time.h> // tzset(), _tzset()

#ifdef __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
Expand Down Expand Up @@ -64,6 +66,19 @@ Mutex env_var_mutex;
std::shared_ptr<KVStore> system_environment = std::make_shared<RealEnvStore>();
} // namespace per_process

template <typename T>
void DateTimeConfigurationChangeNotification(Isolate* isolate, const T& key) {
if (key.length() == 2 && key[0] == 'T' && key[1] == 'Z') {
#ifdef __POSIX__
tzset();
#else
_tzset();
#endif
auto constexpr time_zone_detection = Isolate::TimeZoneDetection::kRedetect;
isolate->DateTimeConfigurationChangeNotification(time_zone_detection);
}
}

Local<String> RealEnvStore::Get(Isolate* isolate,
Local<String> property) const {
Mutex::ScopedLock lock(per_process::env_var_mutex);
Expand Down Expand Up @@ -115,6 +130,7 @@ void RealEnvStore::Set(Isolate* isolate,
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
}
#endif
DateTimeConfigurationChangeNotification(isolate, key);
}

int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
Expand Down Expand Up @@ -150,6 +166,7 @@ void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
SetEnvironmentVariableW(key_ptr, nullptr);
#endif
DateTimeConfigurationChangeNotification(isolate, key);
}

Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {
Expand Down
47 changes: 47 additions & 0 deletions test/parallel/test-process-env-tz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

const common = require('../common');
const assert = require('assert');

if (!common.isMainThread)
common.skip('process.env.TZ is not intercepted in Workers');

if (common.isWindows) // Using a different TZ format.
common.skip('todo: test on Windows');

const date = new Date('2018-04-14T12:34:56.789Z');

process.env.TZ = 'Europe/Amsterdam';

if (date.toString().includes('(Europe)'))
common.skip('not using bundled ICU'); // Shared library or --with-intl=none.

if ('Sat Apr 14 2018 12:34:56 GMT+0000 (GMT)' === date.toString())
common.skip('missing tzdata'); // Alpine buildbots lack Europe/Amsterdam.

if (date.toString().includes('(Central European Time)') ||
date.toString().includes('(CET)')) {
// The AIX and SmartOS buildbots report 2018 CEST as CET
// because apparently for them that's still the deep future.
common.skip('tzdata too old');
}

assert.strictEqual(
date.toString().replace('Central European Summer Time', 'CEST'),
'Sat Apr 14 2018 14:34:56 GMT+0200 (CEST)');

process.env.TZ = 'Europe/London';
assert.strictEqual(
date.toString().replace('British Summer Time', 'BST'),
'Sat Apr 14 2018 13:34:56 GMT+0100 (BST)');

process.env.TZ = 'Etc/UTC';
assert.strictEqual(
date.toString().replace('Coordinated Universal Time', 'UTC'),
'Sat Apr 14 2018 12:34:56 GMT+0000 (UTC)');

// Just check that deleting the environment variable doesn't crash the process.
// We can't really check the result of date.toString() because we don't know
// the default time zone.
delete process.env.TZ;
date.toString();

0 comments on commit eba348b

Please sign in to comment.