🕰

process.env.TZを動的にセットする場合の注意点

3 min read

この記事は CyberAgent Developers Advent Calendar 2020 の 7 日目の記事です

こんにちは、どらです。
今回は、とあるプロジェクトでタイムゾーン依存で結果が変わるバグを起こしてしまったので、Jest で動的にタイムゾーンを変えて検証しようとした際に見つけた挙動をまとめます。

コード例

// 1. ある日時をDateオブジェクトで作る
const date = new Date("2020-06-14T12:00:00.000Z");
// 2. システムのタイムゾーンで表示される
console.log(date.toString());

// 3. 動的にタイムゾーンを変更する(ここではシンガポール標準時)
process.env.TZ = "Asia/Singapore";
// 4. シンガポール標準時で表示される…?
console.log(date.toString());

なお、今回の検証で使用したコードは以下の GitHub リポジトリにて公開しています。
GitHub Actions で Windows/macOS/Linux、Node.js v6/8/12/13 でマトリックステストした結果が見れますので、参考にしてください。

https://github.com/dora1998/process-env-tz-test

想定されるユースケース

  • システム設定に関わらず、特定のタイムゾーンで動かしたい
  • Jest 等で複数のタイムゾーンでテストしたい

問題点

v13 未満で process.env.TZ にセットしても反映されない

$ TZ=Asia/Tokyo node index.js
process.versions.node = 12.20.0
process.env.TZ = Asia/Tokyo
date.getTimezoneOffset() = -540
Sun Jun 14 2020 21:00:00 GMT+0900 (Japan Standard Time)
process.env.TZ = Asia/Singapore
date.getTimezoneOffset() = -540
Sun Jun 14 2020 21:00:00 GMT+0900 (Japan Standard Time)

$ TZ=Asia/Tokyo node index.js
process.versions.node = 13.14.0
process.env.TZ = Asia/Tokyo
date.getTimezoneOffset() = -540
Sun Jun 14 2020 21:00:00 GMT+0900 (Japan Standard Time)
process.env.TZ = Asia/Singapore
date.getTimezoneOffset() = -480
Sun Jun 14 2020 20:00:00 GMT+0800 (Singapore Standard Time)

v13 以上か未満かで結果が異なり、v13 では process.env.TZ の変更が反映される一方、v12 では一見値はセットできたように見えて実際にはシステムのタイムゾーンのままです。

実は、Node.js の V8 エンジンは内部で Date 周りのキャッシュを持っているようで、タイムゾーンを変更した際はこれをクリアしないと正しく反映されない仕様となっています(参考: nodejs/node #33805)。
そして、v13 に含まれる変更で、タイムゾーンの変更イベントを待ち受けて変更時にキャッシュを消すように修正されたのです(参考: nodejs/node #20026)。

過去にはreset-date-cacheなどを用いて手動でキャッシュを消す方法もあったようです。これを使用することでv8 時点までは期待する動作ができていました。ただ、該当ライブラリは既にメンテされておらず、node-gyp 周りが少なくとも Node.js v10 以降に対応していないのでお勧めできません。

そもそも Windows に TZ 環境変数はない

他にも、そもそも TZ 環境変数でタイムゾーンが指定できるのは macOS や Linux のみ であるという問題もあります(参考: nodejs/node #31478)。
実際に Windows 環境で Node.js を実行すると、 process.env.TZ には undefined が入ることが確認でき、当然ながらここに代入しても何も効果はありません。

参考 issue のコメントにもある通り、現時点では Windows で動的にタイムゾーンを指定する方法はないようで、tzutil コマンドで

tzutil /s UTC

と設定する方法もありますが、これでは OS 全体で使われるタイムゾーンが変更されてしまいます。

まとめ

  • システムと異なるタイムゾーンで動かそうとするのは極力避けた方が良い
  • Node v13~ && Unix 系 OS なら、 process.env.TZ を上書きすればタイムゾーンを変更可能
  • Windows では、そもそも TZ 環境変数は使えないので注意
    • 一時的に特定のタイムゾーンでプログラムを動かす方法もない