src: make process.env.TZ setter clear tz cache

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: https://github.com/nodejs/node/issues/19974
PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
Ben Noordhuis 2019-05-23 11:34:15 +02:00
parent 58fe440c0f
commit eba348b6ae
2 changed files with 64 additions and 0 deletions

View File

@ -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())
@ -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);
@ -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 {
@ -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 {

View File

@ -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();