JavaScriptのgetMonthメソッドはインデックス値を返す
Table of Contents
はじめに
今回はJavaScriptのDateオブジェクトでのハマりやすい罠に関する記事です。
正直誰でもハマったことがあるとは思いますが、個人的な備忘録として残しておきます。
なぜこのような仕様になっているのかを理解することで、今後は間違いにくくなると思います。
getMonthメソッドの罠
今回罠にハマったのはJavaScriptのDateのgetMonthメソッドです。
DateオブジェクトはJavaScriptで時刻を扱うときに利用するものです。
例えば、以下のように記述することで現在時刻のDateオブジェクトを作れます。
const now = new Date();
console.log(now);
// Thu Oct 14 2021 22:18:27 GMT+0900 (日本標準時)
Dateオブジェクトには年や月、日などを取り出すためのメソッドが用意されています。
例えば、西暦を取得するためのgetFullYear
や日付を取得するgetDate
、時・分を取得するためのgetHours
、getMinutes
などがあります。
使えるメソッドについてはこちらのドキュメントで確認できます。
console.log(now.getFullYear());
// 2021
console.log(now.getDate());
// 14
console.log(now.getHours());
// 22
ちゃんと取得できているのがわかるかと思います。
では、ここからが本題です。
Dateオブジェクトから「月」を取り出すにはどうすれば良いでしょうか?
先ほどのドキュメントを確認すると、getMonth
というメソッドがあることがわかります。
使ってみましょう。
console.log(now.getMonth());
// 9
あれ?
今は10月なのに、"9"という数値が返ってきました。
これが私が引っかかった罠です。
実は全く気づかず、つい先日まで本ブログの記事は全て1ヶ月分ズレた日付が表示されていましたw
ちなみに私が普段よく使っているPythonの日付時刻ライブラリであるdatetime
では、月は1~12で表現されています。
(そのため、この仕様は完全に油断していました…w)
なぜこの仕様なのか?
ドキュメントを確認すると、以下のように0を基準とした値とちゃんと書いてあります。
getMonth() メソッドは、地方時に基づき、指定された日付の「月」を表す 0 を基点とした値 (すなわち 0 が年の最初の月を示す) を返します。
ちなみに、日付を取得するgetDate
メソッドは1-31の数値を返すので、getMonth
メソッドの挙動とは異なります。
なぜこのような挙動になっているのか調べてみたところ、(明確な理由はわかりませんでしたが)以下の説が存在することがわかりました。
- 欧米の言語では、月は数値ではなく単語で表現するため
- Cなど古くから存在する言語のライブラリの仕様を踏襲したため
この1つ目の理由が根幹にある気がしました。
日本では、1月、2月、…と数字を使って月を表しますが、英語ではJanuary, February, …と単語で表します。
そのため、以下のように「月」を表す配列を用意し、インデックスとしてgetMonth
で取得した数値を渡すだけで簡単にその月の単語を取得できます。
const now = new Date();
// 英語の月名配列
const eng = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
console.log(eng[now.getMonth()]);
// October
かなり自然にコードが書けますね。
配列を変えれば他の言語などへの対応なども簡単にできます。
// イタリア語の月名配列
const italy = [
'Gennaio',
'Febbraio',
'Marzo',
'Aprile',
'Maggio',
'Giugno',
'Luglio',
'Agosto',
'Settembre',
'Ottobre',
'Novembre',
'Dicembre',
];
console.log(italy[now.getMonth()]);
// Ottobre
要するに、getMonth
メソッドは月名の配列のインデックス値を返してくれると考えると、0から始まることへの違和感はなくなるかと思います。
また、日付に関しては英語などでも数値で表現します (14th October, 2021のように)。
そのため、getDate
メソッドはインデックス値ではなく、そのまま日付の数値を返してくれるのかな、と思いました。
そう考えると、日本人が疑問に思うこの仕様も、国際的にはあまり疑問を感じないのかもしれません。
そういえば、日本も昔は異なる月の呼び方をしていました。
(和風月名というみたいですね。)
const jpnOld = ['睦月', '如月', '弥生', '卯月', '皐月', '水無月', '文月', '葉月', '長月', '神無月', '霜月', '師走'];
console.log(jpnOld[now.getMonth()]);
// 神無月
和風月名を使っていた時代の人たちがJavaScriptを触ったら (謎の世界観)、getMonth
メソッドの仕様の罠にハマることもなかったかもしれないですね。
まとめ
今回は、JavaScriptのDateオブジェクトにおけるgetMonth
メソッドに潜む罠 (勝手にそう呼んでいるだけ…)について説明しました。
月名配列に入力するためのインデックス値を返してくれると考えると、自然に受け入れられる仕様だと感じられるのではないでしょうか?
getMonth
メソッドを使うときは、この話を思い出していただけると、注意してコーディングできるかもしれません。