事の始まり
Express + Sequelizeでシステム構築中、日付が9時間ずれる現象に遭遇した。
9時間ずれるのはSequelizeで作ったテーブルに含まれるcreatedAtとupdatedAtで、MySQLの時間はJSTかつDBに格納されている時間もJSTだった。
しかし、Expressでres.json()を使って返されたcreatedAtやupdatedAtはISO 8601形式(YYYY-MM-DDThhddZ)に変換され、しかもUTCなので-9時間されたものになってしまっていた。
原因を調べると、JavaScriptのDateはJSON.stringify()したときにISO 8601形式で文字列にされることが分かったが、これが常にUTCでNode.jsの環境変数TZ(process.env.TZ)を指定しても何も変化がないので、非常に困ったことになった。
そこで、Dateの中でJSON.stringify()されたときに文字列変換を担当する関数を任意のものに書き換えて対応することにしたので、ここではそれについてメモを残しておく。
実際にやったこと
もろもろの都合で正しくはないが、今回はISO 8601形式で表示上のタイムゾーンもUTCだが、実際の表示時間はJSTにした出力を返す(本来はZを+0900にするとかあるけど今回は許して)。
Date.prototype.toJSON=function(){return this.getFullYear()+'-'+('0'+(this.getMonth()+1)).slice(-2)+'-'+('0'+this.getDate()).slice(-2)+'T'+('0'+this.getHours()).slice(-2)+':'+('0'+this.getMinutes()).slice(-2)+':'+('0'+this.getSeconds()).slice(-2)+'.000Z';}
DateはJSON.stringify()時にtoJSON()で文字列化するので、その関数を上書きする。
ミリ秒やタイムゾーン表記は前述したように正しくはないので注意。
この上書き処理を今回であればExpressの初期化処理前に仕込んでおくことで、createdAtとupdatedAtが無事9時間ずれなくなった。
まとめ
toJSON()にタイムゾーン指定効かせて。お願いだから。めっちゃ不便だから。誰だこの仕様にしたやつ。