Skip to content

Commit c0deeea

Browse files
jasnelltargos
authored andcommitted
lib: support setting process.env.TZ on windows
Fixes: #4230 Signed-off-by: James M Snell <[email protected]> PR-URL: #38642 Reviewed-By: Steven R Loomis <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 524a9d6 commit c0deeea

File tree

6 files changed

+99
-4
lines changed

6 files changed

+99
-4
lines changed

doc/api/cli.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,37 @@ 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: v0.0.1
1673+
changes:
1674+
- version:
1675+
- REPLACEME
1676+
pr-url: https://github.com/nodejs/node/pull/38642
1677+
description:
1678+
Changing the TZ variable using process.env.TZ = changes the timezone
1679+
on Windows as well.
1680+
- version:
1681+
- v13.0.0
1682+
pr-url: https://github.com/nodejs/node/pull/20026
1683+
description:
1684+
Changing the TZ variable using process.env.TZ = changes the timezone
1685+
on POSIX systems.
1686+
-->
1687+
1688+
The `TZ` environment variable is used to specify the timezone configuration.
1689+
1690+
While the Node.js support for `TZ` will not handle all of the various
1691+
[ways that `TZ` is handled in other environments][], it will support basic
1692+
[timezone IDs][] (such as `'Etc/UTC'`, `'Europe/Paris'` or `'America/New_York'`.
1693+
It may support a few other abbreviations or aliases, but these are strongly
1694+
discouraged and not guaranteed.
1695+
1696+
```console
1697+
$ TZ=Europe/Dublin node -pe "new Date().toString()"
1698+
Wed May 12 2021 20:30:48 GMT+0100 (Irish Standard Time)
1699+
```
1700+
16701701
### `UV_THREADPOOL_SIZE=size`
16711702

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

src/node.cc

Lines changed: 8 additions & 0 deletions
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

Lines changed: 22 additions & 4 deletions
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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,16 @@ bool InitializeICUDirectory(const std::string& path) {
542542
return status == U_ZERO_ERROR;
543543
}
544544

545+
void SetDefaultTimeZone(const char* tzid) {
546+
size_t tzidlen = strlen(tzid) + 1;
547+
UErrorCode status = U_ZERO_ERROR;
548+
MaybeStackBuffer<UChar, 256> id(tzidlen);
549+
u_charsToUChars(tzid, id.out(), tzidlen);
550+
// This is threadsafe:
551+
ucal_setDefaultTimeZone(id.out(), &status);
552+
CHECK(U_SUCCESS(status));
553+
}
554+
545555
int32_t ToUnicode(MaybeStackBuffer<char>* buf,
546556
const char* input,
547557
size_t length) {

src/node_i18n.h

Lines changed: 2 additions & 0 deletions
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,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const { isMainThread } = require('worker_threads');
5+
6+
if (!common.hasIntl)
7+
common.skip('Intl not present.');
8+
9+
if (!isMainThread)
10+
common.skip('Test not support running within a worker');
11+
12+
const assert = require('assert');
13+
14+
process.env.TZ = 'Etc/UTC';
15+
assert.match(new Date().toString(), /GMT\+0000/);
16+
17+
process.env.TZ = 'America/New_York';
18+
assert.match(new Date().toString(), /Eastern (Standard|Daylight) Time/);
19+
20+
process.env.TZ = 'America/Los_Angeles';
21+
assert.match(new Date().toString(), /Pacific (Standard|Daylight) Time/);
22+
23+
process.env.TZ = 'Europe/Dublin';
24+
assert.match(new Date().toString(), /Irish/);

0 commit comments

Comments
 (0)