Skip to content

Commit eba348b

Browse files
committed
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: #19974 PR-URL: #20026 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Steven R Loomis <[email protected]> Reviewed-By: Timothy Gu <[email protected]>
1 parent 58fe440 commit eba348b

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

src/node_env_var.cc

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include "node_errors.h"
33
#include "node_process.h"
44

5+
#include <time.h> // tzset(), _tzset()
6+
57
#ifdef __APPLE__
68
#include <crt_externs.h>
79
#define environ (*_NSGetEnviron())
@@ -64,6 +66,19 @@ Mutex env_var_mutex;
6466
std::shared_ptr<KVStore> system_environment = std::make_shared<RealEnvStore>();
6567
} // namespace per_process
6668

69+
template <typename T>
70+
void DateTimeConfigurationChangeNotification(Isolate* isolate, const T& key) {
71+
if (key.length() == 2 && key[0] == 'T' && key[1] == 'Z') {
72+
#ifdef __POSIX__
73+
tzset();
74+
#else
75+
_tzset();
76+
#endif
77+
auto constexpr time_zone_detection = Isolate::TimeZoneDetection::kRedetect;
78+
isolate->DateTimeConfigurationChangeNotification(time_zone_detection);
79+
}
80+
}
81+
6782
Local<String> RealEnvStore::Get(Isolate* isolate,
6883
Local<String> property) const {
6984
Mutex::ScopedLock lock(per_process::env_var_mutex);
@@ -115,6 +130,7 @@ void RealEnvStore::Set(Isolate* isolate,
115130
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
116131
}
117132
#endif
133+
DateTimeConfigurationChangeNotification(isolate, key);
118134
}
119135

120136
int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
@@ -150,6 +166,7 @@ void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
150166
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
151167
SetEnvironmentVariableW(key_ptr, nullptr);
152168
#endif
169+
DateTimeConfigurationChangeNotification(isolate, key);
153170
}
154171

155172
Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {

test/parallel/test-process-env-tz.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
6+
if (!common.isMainThread)
7+
common.skip('process.env.TZ is not intercepted in Workers');
8+
9+
if (common.isWindows) // Using a different TZ format.
10+
common.skip('todo: test on Windows');
11+
12+
const date = new Date('2018-04-14T12:34:56.789Z');
13+
14+
process.env.TZ = 'Europe/Amsterdam';
15+
16+
if (date.toString().includes('(Europe)'))
17+
common.skip('not using bundled ICU'); // Shared library or --with-intl=none.
18+
19+
if ('Sat Apr 14 2018 12:34:56 GMT+0000 (GMT)' === date.toString())
20+
common.skip('missing tzdata'); // Alpine buildbots lack Europe/Amsterdam.
21+
22+
if (date.toString().includes('(Central European Time)') ||
23+
date.toString().includes('(CET)')) {
24+
// The AIX and SmartOS buildbots report 2018 CEST as CET
25+
// because apparently for them that's still the deep future.
26+
common.skip('tzdata too old');
27+
}
28+
29+
assert.strictEqual(
30+
date.toString().replace('Central European Summer Time', 'CEST'),
31+
'Sat Apr 14 2018 14:34:56 GMT+0200 (CEST)');
32+
33+
process.env.TZ = 'Europe/London';
34+
assert.strictEqual(
35+
date.toString().replace('British Summer Time', 'BST'),
36+
'Sat Apr 14 2018 13:34:56 GMT+0100 (BST)');
37+
38+
process.env.TZ = 'Etc/UTC';
39+
assert.strictEqual(
40+
date.toString().replace('Coordinated Universal Time', 'UTC'),
41+
'Sat Apr 14 2018 12:34:56 GMT+0000 (UTC)');
42+
43+
// Just check that deleting the environment variable doesn't crash the process.
44+
// We can't really check the result of date.toString() because we don't know
45+
// the default time zone.
46+
delete process.env.TZ;
47+
date.toString();

0 commit comments

Comments
 (0)