JavaScript の Date は罠が多すぎるの続き
JavaScript の Date オブジェクトは意外なところでハマることがあります。
次の記事が役に立ちました。
JavaScript の Date は罠が多すぎる - Qiita
この記事に書かれていないことで、自分がハマったことを書き足したいと思います。
new Date や Date.parse でタイムゾーン指定なし文字列をパースすると UTC で扱われたり扱われなかったり
Date.parse("2016-05-01")
=> 2016-05-01 00:00 UTC (2016-05-01 09:00 JST)
Date.parse("2016-05-01T00:00")
=> 2016-04-30 15:00 UTC (2016-05-01 00:00 JST)
Date.parse("2016/05/01")
=> 2016-04-30 15:00 UTC (2016-05-01 00:00 JST)
Date.parse("2016/05/01 00:00")
=> 2016-04-30 15:00 UTC (2016-05-01 00:00 JST)
(Firefox 46.0 で確認)
同じ日時を指定しているのに違う日時になってしまう。
詳しいことはこちら。
Date.parseとタイムゾーン - メモログ
結論:パースするときに使う文字列はタイムゾーン指定を含めること。
toString や toDateString で書式を指定できない
これは意外です。書式を指定して日付を文字列にする機能が Date オブジェクトに実装されていないんですね。
ちょっと調べると getFullYear 、getMonth 、getDate を使って自作する仕方が紹介されています。標準で欲しい機能ですね。
Date.toJSON や JSON.stringfy すると UTC で返される
var dt = new Date("2016-05-01T00:00+0900");
dt.toJSON() => 2016-04-30T15:00:00Z
タイムゾーン指定を見落とすと、指定した日時と異なる結果が返ったように見えます。
toLocaleString で使われるタイムゾーンはパースするとき指定したタイムゾーンでない
var dt = new Date("2016-05-01T00:00+0900");
dt.toLocaleString() => 2016/5/1 0:00:00 (日本標準時で実行した場合)
dt.toLocaleString() => 2016/4/30 11:00:00 (アメリカ東部夏時間で実行した場合)
dt.toLocaleString() => 2016/4/30 15:00:00 (heroku で実行した場合)
実行した環境のタイムゾーン指定が使われるようです。
ローカル機のブラウザで実行するときは問題ありませんが、クラウドサービスのサーバ機で実行するときは注意する必要ありです。
getDate や getHours はパースするとき指定したタイムゾーンで値を返すわけでない
var dt = new Date("2016-05-01T00:00+0900");
dt.getDate() => 1 (日本標準時で実行した場合)
dt.getDate() => 30 (アメリカ東部夏時間で実行した場合)
dt.getDate() => 30 (heroku で実行した場合)
前述の toLocaleString と同じ。
Moment.js を使ってみた
前述のような JavaScript の Date オブジェクトの不便なところを解消するため、Moment.js を使います。
参考 JavaScriptで日付を扱うならこれ!「moment.js」 : アシアルブログ
公式 Moment.js
Moment.js を使ってみた
- Moment.js 2.13.0
ブラウザで使用する
以下のページからダウンロードします。
Moment.js | Home
以下のファイルをワークスペースにコピーします。
lib
moment.js
使用します。
<script src="lib/moment.js" type="text/javascript"></script>
Node.js アプリで使用する
Node.js アプリのワークスペースにインストールします。
npm install --save moment
使用します。
var moment = require('moment');
パースする
代表的なパースの仕方。Date と moment と併記します。
var dt = new Date();
var dt = moment();
var dt = new Date("2016-5-1");
var dt = moment("2016-5-1");
var dt = new Date(2016, 5, 1);
var dt = moment([2016, 5, 1]);
書式を指定して日時を文字列にする
Date オブジェクトにない機能です。
var dt = moment();
var buf = dt.format("YYYY/MM/DD");
指定したタイムゾーンの日時を取得する
moment.format()
で取得される日時は、Date.toString()
と同じく、実行した環境のタイムゾーン指定が使われます。
var dt = moment("2016-05-01T00:00+0900");
dt.format() => 2016-05-01T00:00:00+09:00 (日本標準時で実行した場合)
dt.format() => 2016-04-30T11:00:00-04:00 (アメリカ東部夏時間で実行した場合)
moment
オブジェクトはタイムゾーンを指定して保持できます。ここが Date
オブジェクトと違います。
dt.utcOffset("+0900");
dt.format() => 2016-05-01T00:00:00+09:00 (日本標準時で実行した場合)
dt.format() => 2016-05-01T00:00:00+09:00 (アメリカ東部夏時間で実行した場合)
moment.utcOffset()
はタイムゾーン指定を含む日時の文字列を指定してもいい。これは意外と便利です。
var buf = "2016-05-01T00:00+0900";
var dt = moment(buf).utcOffset(buf);
同じ日時か判定する
moment
オブジェクトは便利な関数を持っています。
例えば日時を比較して同じか判定するのも簡単です。
moment("2016-05-01T00:00+0900").isSame("2016-05-01T00:00+0900", "day");