diff --git a/src/node_env_var.cc b/src/node_env_var.cc index 717c675d9a7d3c..ef2b78d664858e 100644 --- a/src/node_env_var.cc +++ b/src/node_env_var.cc @@ -2,6 +2,8 @@ #include "node_errors.h" #include "node_process.h" +#include // tzset(), _tzset() + #ifdef __APPLE__ #include #define environ (*_NSGetEnviron()) @@ -64,6 +66,19 @@ Mutex env_var_mutex; std::shared_ptr system_environment = std::make_shared(); } // namespace per_process +template +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 RealEnvStore::Get(Isolate* isolate, Local property) const { Mutex::ScopedLock lock(per_process::env_var_mutex); @@ -115,6 +130,7 @@ void RealEnvStore::Set(Isolate* isolate, SetEnvironmentVariableW(key_ptr, reinterpret_cast(*val)); } #endif + DateTimeConfigurationChangeNotification(isolate, key); } int32_t RealEnvStore::Query(Isolate* isolate, Local property) const { @@ -150,6 +166,7 @@ void RealEnvStore::Delete(Isolate* isolate, Local property) { WCHAR* key_ptr = reinterpret_cast(*key); SetEnvironmentVariableW(key_ptr, nullptr); #endif + DateTimeConfigurationChangeNotification(isolate, key); } Local RealEnvStore::Enumerate(Isolate* isolate) const { diff --git a/test/parallel/test-process-env-tz.js b/test/parallel/test-process-env-tz.js new file mode 100644 index 00000000000000..25152a9b6c6ce1 --- /dev/null +++ b/test/parallel/test-process-env-tz.js @@ -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();