JavaScript で「月の最終日」を出したら、日本では毎回1日前になった ― その書き方、本当に通る?②

JavaScript
B!

「その月の最終日」を出す、よく紹介される 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-172026-10-312026-10-30
2024-02-28(うるう年)2024-02-292024-02-28
2100-02-27(うるう年ではない)2100-02-282100-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. 第1回:「2026-02-30」を渡したら、PHP と JavaScript だけが黙って3月2日にした
  2. 第2回:JavaScript で「月の最終日」を出したら、日本では毎回1日前になった(この記事)
  3. 第3回:C# の Normalize(FormKC) が、Docker の中では何もしなかった
  4. 第4回:辞書の「取り出す順番」を10言語で20回ずつ試したら、5通りの性格に分かれた
B!
← 一覧へ戻る