「その月の最終日」を出す、よく紹介される JavaScript の書き方があります。
const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0);
console.log(lastDay.toISOString().slice(0, 10));
「次の月の 0 日目=この月の最終日」という書き方です。これを日本時間の環境で動かすと、最終日がすべて1日前になりました。正解表6件のうち、正解は0件です。
実際に出た答え
CODE Polyglot の入門 第8回 の正解表で、初心者がよく書く形の JavaScript を動かした結果です(Node.js 24.15.0、タイムゾーンは日本時間)。
| 入力 | 正解の最終日 | JavaScript が出した最終日 |
|---|---|---|
| 2026-10-17 | 2026-10-31 | 2026-10-30 |
| 2024-02-28(うるう年) | 2024-02-29 | 2024-02-28 |
| 2100-02-27(うるう年ではない) | 2100-02-28 | 2100-02-27 |
同じ環境で、ほかの9言語の同じ版はこの行を正しく出しています(存在しない日付のケースを除く)。
なぜずれるか
1. new Date(年, 月, 日) は、動かしている場所の時刻で、その日の 0 時を作る。日本なら「日本時間の 10月31日 0時」2. toISOString() は、世界標準時(UTC)で書き出す。日本時間の 0 時は、UTC では前の日の 15 時3. だから日付の部分だけを切り出すと、前の日になる
作るときと書き出すときで、使っている時刻の基準が違うのが原因です。同じプログラムを UTC の環境(多くのクラウドのサーバー)で動かすとずれないので、「手元では壊れるのにサーバーでは通る」「その逆」が起きます。
どう直したか
作るのも、曜日を取るのも、書き出すのも、すべて UTC にそろえます。
const d = new Date(Date.UTC(y, m - 1, day));
const lastDay = new Date(Date.UTC(y, m, 0));
console.log(lastDay.toISOString().slice(0, 10));
console.log(weekdays[d.getUTCDay()]);
getDay() と getUTCDay()、new Date(…) と Date.UTC(…) を混ぜないのがポイントです。
まとめ
- 「日付だけ」を扱いたいのに、JavaScript の
Dateは時刻とタイムゾーンを必ず持っている - 書き出しに
toISOString()を使うなら、作るほうも UTC にそろえる - 同じ落とし穴は、実在の計算ツールでも見つかりました(年度の計算でも、日付がすべて1日前にずれた)
- 10言語の全プログラムは 入門 第8回 にあります
連載「その書き方、本当に通る?」(全4回)
- 第1回:「2026-02-30」を渡したら、PHP と JavaScript だけが黙って3月2日にした
- 第2回:JavaScript で「月の最終日」を出したら、日本では毎回1日前になった(この記事)
- 第3回:C# の Normalize(FormKC) が、Docker の中では何もしなかった
- 第4回:辞書の「取り出す順番」を10言語で20回ずつ試したら、5通りの性格に分かれた
Analyzegear


