Skip to content

Commit cc783d2

Browse files
committedMay 12, 2021
lib: support setting process.env.TZ on windows
Fixes: #4230 Signed-off-by: James M Snell <[email protected]>
1 parent aefc621 commit cc783d2

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
@@ -1669,6 +1669,24 @@ Be aware that unless the child environment is explicitly set, this environment
16691669
variable will be inherited by any child processes, and if they use OpenSSL, it
16701670
may cause them to trust the same CAs as node.
16711671

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

16741692
Set the number of threads used in libuv's threadpool to `size` threads.
@@ -1743,3 +1761,5 @@ $ node --max-old-space-size=1536 index.js
17431761
[jitless]: https://v8.dev/blog/jitless
17441762
[libuv threadpool documentation]: https://docs.libuv.org/en/latest/threadpool.html
17451763
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
1764+
[timezone IDs]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
1765+
[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)