ユーザーが記事を投稿して、それを表示するというシンプルなシステムを作っている時の話。
ある日、IEで記事投稿の時刻表示が出ないと連絡があった。
コードを調べてもIE特有で表示できない部分がぱっと見では分からず、調べてみるとnew Date()
にちょっとした落とし穴があったのでまとめ。(どちらかというと落とし穴があったのではなく自分から引っ掛かったが正解かも...)
問題点
当初の記事の時刻表示をするコードが以下
var content = '';
var date = new Date(dateString); // APIで取得した日付の文字列をもとにDateインスタンス作成
if (date.toString() !== 'Invalid Date') {
content += '<li>';
// APIで受け取った日付を"YY.MM.DD HH:mm:ss"にする
content += date.getFullYear().toString().slice(-2) + '.' + (date.getMonth() + 1) + '.' + date.getDate() + ' ' + ('00' + date.getHours()).slice(-2) + ':' + ('00' + date.getMinutes()).slice(-2) + ':' + ('00' + date.getSeconds()).slice(-2);
content += '</li>'; // このリストタグをappendする
// コードの書き方が古風なのは許して
}
結論、何が悪かったかというと、日付文字列のフォーマット。
実はECMAScript公式は、ISO 8601 Extended FormatというYYYY-MM-DDTHH:mm:ss.sssZ
しか定義していない。
(※詳細はECMAScript公式を見てください。20.4.1.15 Date Time String Format)
今回のAPIからもらってくる日付文字列はフォーマットはYYYY-MM-DD HH:mm
今回の文字列形式がGoogle chromeなどで正常に動くのはブラウザ依存の動き方で、全てで動くとは保証できない。
結果、IEでは"Invalid Date"になって描画されなかったんですね。
納得。
解決法
解決法は、正規表現を使って、もらったフォーマットを、ISO 8601 Extended Formatにすること。
// 今回受け取るフォーマットは'YYYY-MM-DD HH:mm'なので、それから年月日時刻を抜き出す正規表現
function convertToRightFormatString(dateString) {
const regex = /([0-9]{2,4})-([0-1]?[0-9])-([0-3]?[0-9])(?: ([0-2][0-9]):([0-5][0-9]))/;
return dateString.replace(regex, function(
match, year, month, day, hour, minutes
){
return year + '-' + month + '-' + day + 'T' + hour + ':' + minutes + ':00.000Z'
});
}
var content = '';
var date = new Date(convertToRightFormatString(dateString)); // 文字列を変更
// ...以下略
これで公式に乗っ取ったフォーマットになるので、どのブラウザでもInvalid Dateにならないはず!
※ RegexでわかりやすいようにNamed capture groups使おうと思ったらIE非対応だった...。さすがIE。
参考
[ECMAScript® 2020 Language Specification] (https://www.ecma-international.org/ecma-262/#sec-date-time-string-format)