Skip to content

Commit 3f15012

Browse files
committed
lib: support setting process.env.TZ on windows
Fixes: #4230 Signed-off-by: James M Snell <[email protected]>
1 parent 184e0f7 commit 3f15012

File tree

6 files changed

+76
-4
lines changed

6 files changed

+76
-4
lines changed

doc/api/cli.md

+20
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,24 @@ Be aware that unless the child environment is explicitly set, this environment
16671667
variable will be inherited by any child processes, and if they use OpenSSL, it
16681668
may cause them to trust the same CAs as node.
16691669

1670+
### `TZ`
1671+
<!-- YAML
1672+
added: REPLACEME
1673+
-->
1674+
1675+
The `TZ` environment variable is used to specify the timezone configuration.
1676+
1677+
While the Node.js support for `TZ` will not handle all of the various
1678+
[ways that `TZ` is handled in other environments][], it will support basic
1679+
[timezone IDs][] (such as `'Etc/UTC'`, `'Europe/Paris'` or `'America/New_York'`.
1680+
It may support a few other abbreviations or aliases, but these are strongly
1681+
discouraged and not guaranteed.
1682+
1683+
```console
1684+
$ TZ="Europe/Dublin" -pe "new Date().toString()"
1685+
Wed May 12 2021 20:30:48 GMT+0100 (Irish Standard Time)
1686+
```
1687+
16701688
### `UV_THREADPOOL_SIZE=size`
16711689

16721690
Set the number of threads used in libuv's threadpool to `size` threads.
@@ -1741,3 +1759,5 @@ $ node --max-old-space-size=1536 index.js
17411759
[jitless]: https://v8.dev/blog/jitless
17421760
[libuv threadpool documentation]: https://docs.libuv.org/en/latest/threadpool.html
17431761
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
1762+
[timezone IDs]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
1763+
[ways that `TZ` is handled in other environments]: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html

src/node.cc

+8
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,14 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
937937
return 9;
938938
}
939939
per_process::metadata.versions.InitializeIntlVersions();
940+
941+
# ifndef __POSIX__
942+
std::string tz;
943+
if (credentials::SafeGetenv("TZ", &tz) && !tz.empty()) {
944+
i18n::SetDefaultTimeZone(tz.c_str());
945+
}
946+
# endif
947+
940948
#endif
941949

942950
NativeModuleEnv::InitializeCodeCache();

src/node_env_var.cc

+22-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "env-inl.h"
33
#include "node_errors.h"
44
#include "node_external_reference.h"
5+
#include "node_i18n.h"
56
#include "node_process.h"
67

78
#include <time.h> // tzset(), _tzset()
@@ -69,15 +70,32 @@ std::shared_ptr<KVStore> system_environment = std::make_shared<RealEnvStore>();
6970
} // namespace per_process
7071

7172
template <typename T>
72-
void DateTimeConfigurationChangeNotification(Isolate* isolate, const T& key) {
73+
void DateTimeConfigurationChangeNotification(
74+
Isolate* isolate,
75+
const T& key,
76+
const char* val = nullptr) {
7377
if (key.length() == 2 && key[0] == 'T' && key[1] == 'Z') {
7478
#ifdef __POSIX__
7579
tzset();
80+
isolate->DateTimeConfigurationChangeNotification(
81+
Isolate::TimeZoneDetection::kRedetect);
7682
#else
7783
_tzset();
84+
85+
# if defined(NODE_HAVE_I18N_SUPPORT)
86+
isolate->DateTimeConfigurationChangeNotification(
87+
Isolate::TimeZoneDetection::kSkip);
88+
89+
// On windows, the TZ environment is not supported out of the box.
90+
// By default, v8 will only be able to detect the system configured
91+
// timezone. This supports using the TZ environment variable to set
92+
// the default timezone instead.
93+
if (val != nullptr) i18n::SetDefaultTimeZone(val);
94+
# else
95+
isolate->DateTimeConfigurationChangeNotification(
96+
Isolate::TimeZoneDetection::kRedetect);
97+
# endif
7898
#endif
79-
auto constexpr time_zone_detection = Isolate::TimeZoneDetection::kRedetect;
80-
isolate->DateTimeConfigurationChangeNotification(time_zone_detection);
8199
}
82100
}
83101

@@ -128,7 +146,7 @@ void RealEnvStore::Set(Isolate* isolate,
128146
if (key.length() > 0 && key[0] == '=') return;
129147
#endif
130148
uv_os_setenv(*key, *val);
131-
DateTimeConfigurationChangeNotification(isolate, key);
149+
DateTimeConfigurationChangeNotification(isolate, key, *val);
132150
}
133151

134152
int32_t RealEnvStore::Query(const char* key) const {

src/node_i18n.cc

+8
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,14 @@ bool InitializeICUDirectory(const std::string& path) {
542542
return status == U_ZERO_ERROR;
543543
}
544544

545+
void SetDefaultTimeZone(const char* tzid) {
546+
UChar lbl[255];
547+
UErrorCode status = U_ZERO_ERROR;
548+
u_charsToUChars(tzid, lbl, strlen(tzid) + 1);
549+
ucal_setDefaultTimeZone(lbl, &status);
550+
CHECK(U_SUCCESS(status));
551+
}
552+
545553
int32_t ToUnicode(MaybeStackBuffer<char>* buf,
546554
const char* input,
547555
size_t length) {

src/node_i18n.h

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ namespace i18n {
4040

4141
bool InitializeICUDirectory(const std::string& path);
4242

43+
void SetDefaultTimeZone(const char* tzid);
44+
4345
enum idna_mode {
4446
// Default mode for maximum compatibility.
4547
IDNA_DEFAULT,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
6+
process.env.TZ = 'Etc/UTC';
7+
assert.match(new Date().toString(), /GMT\+0000/);
8+
9+
process.env.TZ = 'America/New_York';
10+
assert.match(new Date().toString(), /Eastern (Standard|Daylight) Time/);
11+
12+
process.env.TZ = 'America/Los_Angeles';
13+
assert.match(new Date().toString(), /Pacific (Standard|Daylight) Time/);
14+
15+
process.env.TZ = 'Europe/Dublin';
16+
assert.match(new Date().toString(), /Irish/);

0 commit comments

Comments
 (0)