new Date()の罠
ある残り時間を表示するための以下のコードで、海外のユーザーから「残り時間がずれているよ!」と指摘をいただきました。
NG
let endDate = "2024/03/12 22:00:00"; // この値は実際にはAPIから取得した値なので変更不可 // タイムスタンプに直して、終了時間から現在時間の差分を求める let diff = new Date(endDate).getTime() - new Date().getTime(); // タイムスタンプの差分から「あと○時間○分」に直す処理は省略
endDate
は日本時間だけど、new Date()
で取得される現在時刻はユーザーが設定しているタイムゾーンになってしまうので、この処理ではまずいことが分かります。(言い訳ですがこのコードを書いたのは私ではない。)
であれば、日本時間の現在時刻を表示すればいいではないか、と思いDateのリファレンスを読んでみたのですが、どうも一筋縄ではいかなそうでした。Dateはタイムゾーンを自由に設定できるわけではないのですね…(不便)
発想を変えて、どちらも同じタイムゾーンにすればいいだろう!と思ってUTCに変換する処理を入れてみました。
NG
let endDate = "2024/03/12 22:00:00"; let diff = Date.parse(new Date(endDate).toISOString()) - Date.now();
toISOString()
でUTC時刻に変換してから、Date.parse
でタイムスタンプを取得しています。Date.now()
は常にUTCのタイムスタンプを返すようです。
が、これでもうまく動作せず。よく考えたら、endDate
にタイムゾーンの情報がないので、new Date(endDate)
の時点で、ユーザーが設定しているタイムゾーンとして解釈されてしまうようです。
であるならば、タイムゾーンを明示的に指定してから初期化しよう、ということで以下の方法で解決しました。
ISO形式(2024-03-12T22:00:00+09:00
)で記述するために、dayjsを使いました。
OK
let endDate = "2024/03/12 22:00:00"; // 実際には変更不可の値 let jstDate = dayjs(endDate).format("YYYY-MM-DDTHH:mm:ss+09:00"); let diff = Date.parse(new Date(jstDate).toISOString()) - Date.now();
めでたしめでたし。タイムゾーンは頭が混乱してしまいますね。
とても参考にさせていただきました: JavaScript における Date のタイムゾーンの挙動と対策
ブラウザでタイムゾーンを変更する方法
結論、Chrome DevToolsの「センサー(Sensors)」を使うのが一番手軽で便利でよかったです!こんな便利な機能があったとは!
センサー: デバイス センサーをエミュレートする | DevTools | Chrome for Developers
Macの システム設定>一般>日付と時刻 で端末の時間を変更するのは、タイムゾーンの問題解決には意味がなかったです。タイムゾーンではなく時間自体がずれてしまうので意図した動作にならず。最初これをやってしまって、無駄に混乱してしまいました……。