はじめに
JavaScriptでは、Dateオブジェクトから、指定したその月の最終日(月末日)を取り出す際、getMonth()メソッド
を利用します。
上記の一文だけ読むとそんなに難しいことではない気がするのですが、実際にやってみて、JavaScript独特のDateオブジェクトの挙動に翻弄され、ハマりまくってしまいました。
JavaScript の Dateは、初心者がハマりがちな罠が多いそうです。
何だか面倒な挙動が多く「クセが強い!」Dateオブジェクト。
そんな同じ状況でハマってしまう方の助けになればと思いブログに書き留めておきます。
前提知識
UTC、GMT、JSTの違い
まず、前提としてDateオブジェクトはUTC(協定世界時)を基準としています。
- UTC特徴
- セシウム原子時計を基本とした時刻
- 現在、世界の基準となっている時刻
- 協定世界時 - Wikipedia
- GMTの特徴
- 英国のグリニッジ天文台(経緯0度)での地方平均時(平均太陽が南中する時を正午とする)のことで、天文観測(地球の自転)によって決められる時刻
- UTCとほとんど同じだが、現在はUTCのほうがより正確であるため、そちらを世界標準として採用している
- グリニッジ標準時 - Wikipedia
それから、UTCの時刻へ9時間プラスした時刻をJSTと呼びます。
- JST特徴
- 日本における標準時
- UTC + 9時間した時刻
- 日本標準時 - Wikipedia
ISO 8160
それから、日付と時刻の表記に関する国際規格に沿って、形式や表記が定められています。
以下、返り値として返される、2021-10-31T11:31:04.554Z
や、2021-10-30T15:00:00.000Z
という表記も、ISO 8160に準じて定められています。
ハマってしまったこと
Dateオブジェクト
まずは、Dateオブジェクトを作ってみます。
Date.now()
メソッドを使い、現在時刻(UTC)を生成することができます。
ここでは、「UTC」であることがポイントです。
> now = new Date(); 2021-10-31T11:31:04.554Z
ここから、その月の最終日を取得するために、以下の引数を渡してあげます。
第三引数に0
を渡すと、その月の最終日が返ってくるはずですが…
date = new Date(年, 月, 0); date = new Date(2021, 10, 0); 2021-10-30T15:00:00.000Z
あれ? 10月30日で最終日ではない…
10月は、31日まで存在しますが、10月30日となっています。
UTCだから、おかしなことになっているのではないかと考え、JSTに変換するメソッドがないか調べてみました。
toLocaleString()メソッド
ありました。
このメソッドを使うことでJSTへ変換できるようです。
.toLocaleString({ timeZone: 'Asia/Tokyo' })
> now = new Date().toLocaleString({ timeZone: 'Asia/Tokyo' }); '2021/10/31 21:10:38'
出来ました! 現在の日本時刻(21:10)と同じです。
このオブジェクトを使って、今回やりたかったこと(その月の最終日を取得する)試してみます。
まずは、UTCでその月の最終日を取得します。
> const date = new Date(2021, 10, 0); undefined > date 2021-10-30T15:00:00.000Z
ここから、変数date
に対してメソッドを実行してみます。
結果、無事に10月31日と表示されました。
> date.toLocaleString({ timeZone: 'Asia/Tokyo' }); '2021/10/31 0:00:00'
toLocaleStringメソッド は、文字通り String(文字列)に修正してしまうものなので注意
ここで本題のgetMonth()メソッド
を使い、指定された日時の「月」だけ取得を試みてみます。
> const date = new Date(2021, 10, 0); undefined > const endDate = date.toLocaleString({ timeZone: 'Asia/Tokyo' }); undefined > endMonth = endDate.getMonth(); Uncaught TypeError: endDate.getMonth is not a function
ん?
Uncaught TypeError: endDate.getMonth is not a function
、値を関数として呼び出そうとしたが、その値が実際には関数ではなかった場合に発生するエラーが返ってきました。
これは小見出しにもあるように、「toLocaleStringメソッド は、文字通り String(文字列)に修正してしまうもの」なので、変数endDate
に代入されている値は、Stringなので、レシーバとしては適切ではないようです。
getMonth()メソッド
に対しては、Detaオブジェクトを利用する必要があるようです。
以上、私がハマった経緯になります。次に解決方法をまとめます。
結論
ここからは、ハマったポイントを抜け出した結論になります。
正直、結論は「え?そんなことだったの…」と、拍子抜けの内容です。 しかし、上記のDateオブジェクトの挙動のクセを体感したからこその拍子抜けなので、今回は遠回りでしたがとてもよい経験となりました。
DateオブジェクトのgetMonth()メソッドは、JSTを基準とした値を返す
MDNには、以下のように書いていました。
注: Date オブジェクトの中心となる時間値は UTC ですが、日付と時刻、またはその一部を取得する基本的なメソッドは、すべて地方時 (ホストシステムなど) のタイムゾーンとオフセットで動作することを覚えておくことが重要です。
要は、DateオブジェクトのgetMonth()メソッド
はJSTとして返事をくれるようです!!
一点、注意点としては、0-11の数字で扱っているので、正確な月に修正するには +1 が必要です。
変数date
へ、10月の最終日(31日)のオブジェクトを生成して代入します。
確認してみると、UTCでは、2021-10-30T15:00:00.000Z
と表記されています。
> const date = new Date(2021, 10, 0); undefined > date 2021-10-30T15:00:00.000Z
では、ここで「getMonth()メソッド
はJSTとして返事をくれる」が正しいか試してみます。
> date.getMonth(); 9
「9」!
無事に9が表示されました!!!
上述しましたが、0-11の数字で扱っているので、9は、10月のことになります。
ちゃんとJSTとして変換して返してくれています。
おまけ
諸々が解決したあと、改めてMDNを読み直してみました。
すると、以下のように「地方時に基づき」と書いてありました…汗
地方時 == 日本の時刻 ですね。
getMonth() メソッドは、地方時に基づき、指定された日付の「月」を表す 0 を基点とした値 (すなわち 0 が年の最初の月を示す) を返します。
何だか今日は、JavaScriptのトリックに翻弄された一日でした…(JavaScriptにそんなつもりは無いと思いますが…)
引き続きドキュメントをしっかり読み解き、ひとつひとつやっていきたいと思います。
ハッピーハロウィーーン!!