はじめに
Javascriptのnew Date
関数で生成した日付はタイムゾーンの影響を受けてしまいます。
タイトルの通り「日時を常にJST日本時間で表示したい」場合は、タイムゾーンを考慮したコードを書く必要があります。
具体的にどういうこと?
下記のようなコードを書いてみるとわかりやすいです。
const timestamp = 1632322799;
const date = new Date(timestamp * 1000).toLocaleString();
const formattedDate = //【省略】dateをyyyy/mm/dd hh:mm:ssに整形
console.log(formattedDate);
console.log 結果の日時に注目してください。
タイムゾーン名称 | タイムゾーンID | 略称 | UTCからのオフセット | console.log 結果 |
---|---|---|---|---|
ハワイ・アリューシャン標準時 | Pacific/Honolulu | HAST | -10:00 | 2021/09/22 4:59:59 |
ニューファンドランド標準時 | America/St_Johns | NST | -03:30 | 2021/09/22 12:29:59 |
グリニッジ標準時 | Europe/London | GMT | +00:00 | 2021/09/22 14:59:59 |
日本標準時 | Asia/Tokyo | JST | +09:00 | 2021/09/22 23:59:59 |
ロードハウ標準時 | Australia/Lord_Howe | LHST | +10:30 | 2021/09/23 1:29:59 |
上記のようにPCのタイムゾーン設定により出力される日時が変化してしまっています。
ちなみにPCのタイムゾーン設定はWindows/Mac共に簡単に変更できるので
誰でもブラウザで表示確認できることです
今回やりたいことまとめ
- Unixタイムスタンプを日時に変換して出力
- 日時は常にJST日本時間で表示
- モダンブラウザ+IE11にも対応
- できればIE11対応のためにポリフィルは入れたくない
今回はタイムスタンプを日時に変換したいのでnew Date(timestamp * 1000)
としています。
もちろんnew Date()
やnew Date('2021-09-25')
といった形でも同じ対応が可能です。
【結論】date-fns + date-fns-tzで対応
import { format } from 'date-fns';
import { utcToZonedTime } from 'date-fns-tz';
const timestamp = 1632322799;
const formattedDate = format(utcToZonedTime(timestamp * 1000, '+09:00'), 'yyyy/MM/dd HH:mm:ss');
console.log(formattedDate);
ポイントは'+09:00'
の部分。この書き方のおかげでIE11ポリフィルなしで実行できています。
'Asia/Tokyo'
とも書けますが、IE11で動かすには'+09:00'
のように、UTCからのオフセットを記述する必要があります。
参考
If you do not wish to use a polyfill the time zone option can still be used, but only with time zone offsets such as '-0200' or '+04:00' and not IANA time zone names.
Google翻訳した日本語はこちら。
ポリフィルを使用したくない場合でも、タイムゾーンオプションを使用できますが、IANAタイムゾーン名ではなく、「-0200」や「+04:00」などのタイムゾーンオフセットでのみ使用できます。
結論に至るまでに失敗した方法を紹介
JavaScript(timeZone)
ユーザー環境のlocaleを指定+タイムゾーンをAsia/Tokyo
と指定するパターンです。
ダメだった理由
-
timeZone
プロパティがIE11未対応のためエラーが発生
const timestamp = 1632322799;
const userLocale = (window.navigator.languages && window.navigator.languages[0]) || window.navigator.language || window.navigator.userLanguage || window.navigator.browserLanguage;
const date = new Date(timestamp * 1000).toLocaleString(userLocale, { timeZone: "Asia/Tokyo" });
const formattedDate = //【省略】dateをyyyy/mm/dd hh:mm:ssに整形
console.log(formattedDate);
JavaScript(getTimezoneOffset)
getTimezoneOffset
を使って固定する日時を計算するパターンです。
日本標準時(JST)に固定したいため、UTCからのオフセットを分単位で表すために(9 * 60)
をしています。
ダメだった理由
- サマータイム導入地域の場合、一部の日時がズレて表示される
const timestamp = 1641308399;
const date = new Date(timestamp * 1000 + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000));
const formattedDate = //【省略】dateをyyyy/mm/dd hh:mm:ssに整形
console.log(formattedDate);
サマータイム導入地域の場合、一部の日時がズレて表示される
Unixタイムスタンプが1632322799
(2021/09/22 23:59:59)では問題ありませんが
サマータイム(夏時間・DST)が導入されている「ニューファンドランド」「ロードハウ」では、例えばタイムスタンプが1641308399
(2022/01/04 23:59:59)だと時間がズレてしまいます。
タイムゾーン名称 | タイムゾーンID | 略称 | UTCからのオフセット | console.log 結果 |
---|---|---|---|---|
ハワイ・アリューシャン標準時 | Pacific/Honolulu | HAST | -10:00 | 2022/01/04 23:59:59 |
ニューファンドランド標準時 | America/St_Johns | NST | -03:30 | 2022/01/04 22:59:59 |
グリニッジ標準時 | Europe/London | GMT | +00:00 | 2022/01/04 23:59:59 |
日本標準時 | Asia/Tokyo | JST | +09:00 | 2022/01/04 23:59:59 |
ロードハウ標準時 | Australia/Lord_Howe | LHST | +10:30 | 2022/01/05 00:29:59 |
Day.js
ダメだった理由
- IE11でエラーが発生
- 一部Safariでエラーが発生
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
// タイムゾーンを扱うために必要なプラグインを使用
dayjs.extend(utc);
dayjs.extend(timezone);
// タイムゾーンを日本に設定
dayjs.tz.setDefault('Asia/Tokyo');
const timestamp = 1632322799;
const formattedDate = dayjs.tz(timestamp * 1000).format('YYYY/MM/DD HH:mm:ss');
console.log(formattedDate);
date-fns
よりも軽量かつMoment.js
に近い記述ができるDay.js
。
日時の処理が多いサイトかつ、サポートすべきブラウザで動くのであればこちらが最適解かと思います
ひとこと
IE11対応は大変だなあ(132937289328回目)