【JavaScript】日付が正しいかチェックする方法

入力された日付が正しいかをチェックするとき、「①形式が正しいか」と「②実在する日付か」は別物です。たとえば 2024-02-31 は形式は正しいですが、存在しない日付です。

注意したいのは、new Date() だけでは実在チェックにならない点です。2024-02-31 はエラーにならず 3月2日繰り越されてしまうため、「有効な日付」と誤判定されます。この記事では、この罠を避けた正しいチェック方法を解説します。

この記事の結論:日付チェックは2段階で行います。①正規表現で形式(YYYY-MM-DD)を確認し、②new Date(年, 月-1, 日) で組み立てて年月日が入力と一致するか比較します。!isNaN(date.getTime()) だけでは、2024-02-31 のような繰り越される無効日付を見逃します
スポンサーリンク

まず形式をチェックする(正規表現)

まずは入力が想定どおりの形式(ここでは YYYY-MM-DD)かを正規表現で確認します。これは「形式」だけのチェックで、日付が実在するかは見ていません。

JavaScript:形式(YYYY-MM-DD)の確認
function isDateFormat(str) {
  return /^\d{4}-\d{2}-\d{2}$/.test(str);
}

isDateFormat("2024-07-13"); // true
isDateFormat("2024/07/13"); // false(区切りが違う)
isDateFormat("24-07-13");   // false(桁数が違う)
isDateFormat("2024-02-31"); // true ← 形式は正しい(実在は別問題)

最後の例のように、2024-02-31形式チェックは通過します。ここから「実在する日付か」を確かめる必要があります。

new Date() だけでは実在チェックにならない(繰り越しの罠)

new Date() でパースして isNaN を見ればいい」と思いがちですが、これは不十分です。存在しない日付は、エラーにならず近い実在日へ繰り越されてしまうからです。

NG:isNaNだけの判定(無効日付を見逃す)
function isValidDateNG(str) {
  return !isNaN(new Date(str).getTime());
}

// 実際の挙動(繰り越されてしまう)
new Date("2024-02-31"); // → 2024-03-02(true になる!)
new Date("2023-02-30"); // → 2023-03-02(true になる!)
new Date("2024-04-31"); // → 2024-05-01(true になる!)
new Date("2024-13-01"); // → Invalid Date(月の桁溢れだけは弾く)
ここが落とし穴:2024-02-312023-02-30Invalid Date にならず、翌月へ繰り越されて「有効」と判定されます。そのため isNaN によるチェックだけでは、存在しない日付がすり抜けます。文字列パースとタイムゾーンの詳細は文字列をDate型に変換する方法でも解説しています。

実在する日付かチェックする(年月日の一致比較)

確実なのは、入力の年月日からDateを組み立て、組み立てた結果が入力と一致するかを比べる方法です。繰り越しが起きると年月日がズレるので、それを検出できます。

JavaScript:形式+実在をまとめて検証(堅牢版)
function isValidDate(str) {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(str);
  if (!m) return false; // 形式が違えば不正

  const y = Number(m[1]);
  const mo = Number(m[2]);
  const d = Number(m[3]);

  // ローカルのDateで組み立てる(月は0始まりなので -1)
  const date = new Date(y, mo - 1, d);

  // 組み立て後の年月日が入力と一致すれば「実在する日付」
  return (
    date.getFullYear() === y &&
    date.getMonth() === mo - 1 &&
    date.getDate() === d
  );
}

isValidDate("2024-07-13"); // true
isValidDate("2024-02-29"); // true(うるう年)
isValidDate("2023-02-29"); // false(平年に2/29は無い)
isValidDate("2024-02-31"); // false(繰り越しを検出)
isValidDate("2024-13-01"); // false

この方法なら、うるう年(2024-02-29)は通し、平年の 2023-02-29 や存在しない 2024-02-31 はきちんと false にできます(すべてNodeで動作確認済み)。

タイムゾーンのズレに注意

上のコードで new Date(y, mo - 1, d)(数値を渡すローカルのコンストラクタ)を使っているのには理由があります。文字列を new Date("2024-07-13") のように渡すと、ハイフン区切りはUTCとして解釈され、UTCより遅れた地域では getDate()1日ズレることがあります。

年月日の一致比較をするときは、文字列パースに頼らず数値からローカルのDateを組み立てるのが安全です。なお 2024/07/13(スラッシュ区切り)はローカル時間として解釈されるなど、区切り文字でも挙動が変わります。詳細は文字列をDate型に変換する方法を参照してください。

ライブラリや新しいAPIを使う手も

厳密な日付検証を簡潔に書きたい場合は、ライブラリや新しいAPIも選択肢です。

  • date-fnsparse でフォーマット指定して読み込み、isValid で妥当性を確認できます。
  • Temporal(新しい日付API):Temporal.PlainDate.from(str, { overflow: "reject" }) は不正な日付で例外を投げます(既定の "constrain" は丸めてしまうため reject を指定)。

現在の日付を取得・表示する方法は現在の日付をYYYY/MM/DD形式で取得・表示する方法にまとめています。

よくある質問(FAQ)

QJavaScriptで日付の形式が正しいか確認するには?
A/^\d{4}-\d{2}-\d{2}$/.test(str)YYYY-MM-DD 形式を確認できます。ただしこれは形式のみのチェックで、2024-02-31 のような実在しない日付も通過します。実在チェックは別途必要です。
Q2月31日のような無効な日付をチェックするには?
Anew Date()2024-02-313/2 に繰り越すため、isNaN だけでは見逃します。new Date(年, 月-1, 日) で組み立てて、getFullYear() / getMonth() / getDate() が入力と一致するかを比較すると、確実に判定できます。
Qnew Date() でうるう年を正しく判定できますか?
A組み立て後の一致比較なら正しく判定できます。2024-02-29(うるう年)は true2023-02-29(平年)は 3/1 に繰り越されるため一致比較で false になります。
Q日付チェックでタイムゾーンに注意すべき点は?
Aハイフン区切りの "2024-07-13" はUTCとして解釈され、UTCより遅れた地域では getDate() が1日ズレることがあります。年月日の一致比較では、文字列パースではなく new Date(年, 月-1, 日) でローカルのDateを組み立てるのが安全です。

まとめ

日付のチェックは、①正規表現で形式を確認 → ②年月日の一致比較で実在を確認、の2段階で行うのが確実です。

とくに注意すべきは、new Date()2024-02-313/2 に繰り越して「有効」と誤判定する点です。isNaN だけに頼らず、new Date(年, 月-1, 日) で組み立てた結果が入力と一致するかを比べることで、存在しない日付を確実に弾けます。タイムゾーンのズレを避けるため、比較にはローカルのコンストラクタを使いましょう。