営業日の計算のために、日本の祝日判定を JavaScript で自作しました。振替休日も国民の休日も入れたし大丈夫だろう、と思いつつ、内閣府が公開している祝日一覧の CSV と全件を突き合わせてみたところ、1日だけズレが見つかりました。その原因と、突き合わせの方法を紹介します。
この記事は、生成AI(Claude)を使って書いた下書きを、実際に動かして確認してから投稿しています。
突き合わせの方法
内閣府は祝日の一覧を CSV で公開しています(Shift_JIS、国民の祝日について からリンク)。行は 2026/5/6,休日 のような形式です。
ブラウザで内閣府のページを開いたまま開発者ツールで実行すると、同じサイトなので CSV をそのまま fetch できます。holidays(y) は自作の関数で、その年の祝日・休日を 'YYYY-MM-DD' をキーにした Map で返すものです。
const pad = n => String(n).padStart(2, '0');
const buf = await (await fetch('/chosei/shukujitsu/syukujitsu.csv')).arrayBuffer();
const text = new TextDecoder('shift_jis').decode(buf);
const official = new Set(text.trim().split(/\r?\n/).slice(1)
.map(line => line.split(',')[0].split('/').map(Number))
.filter(([y]) => y >= 2000)
.map(([y, m, d]) => `${y}-${pad(m)}-${pad(d)}`));
const mine = new Set();
for (let y = 2000; y <= 2027; y++) for (const k of holidays(y).keys()) mine.add(k);
console.log({
missing: [...official].filter(k => !mine.has(k)), // 公式にあって自分にない
extra: [...mine].filter(k => !official.has(k)), // 自分にあって公式にない
});
結果: extra: ['2003-05-04']
公式にない日が1日だけ、自分の結果に入っていました。2003年5月4日です。
2006年までは、5月4日は祝日ではなく「国民の休日」でした(憲法記念日とこどもの日に挟まれるため)。ところが 2003年5月4日は日曜日です。当時の条文は、国民の休日から日曜日を除いていたため、内閣府の一覧には載っていません。
自作の関数は「前日と翌日が祝日なら休日」とだけ判定していたので、日曜日も休日として数えていました。
// 国民の休日: 前日と翌日が本来の祝日である、祝日でない日(日曜日は除く)
for (let d = new Date(y, 0, 2); d.getFullYear() === y; d.setDate(d.getDate() + 1)) {
if (d.getDay() === 0) continue; // ← これが抜けていた
const prev = new Date(d.getFullYear(), d.getMonth(), d.getDate() - 1);
const next = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1);
if (!all.has(ymd(d)) && law.has(ymd(prev)) && law.has(ymd(next))) all.set(ymd(d), '国民の休日');
}
どうせ休みの日なので営業日の計算には影響しませんが、祝日一覧を画面に出すと1日多くなります。直したあとは、2000〜2027年の 486日すべてが一致しました(2026年10月22日に実行)。
ほかに気をつけたところ
突き合わせで問題は出ませんでしたが、書く段階で気をつけた点です。
- 振替休日と国民の休日の判定には「本来の祝日」だけを使う。 振替休日や国民の休日を祝日として数えると、休日が1日多くなる年が出うる
- 法改正と特例。 2019年の即位関連の休日、2020・2021年のオリンピックに伴う海の日・スポーツの日・山の日の移動、天皇誕生日の 12/23 → 2/23
- 春分の日・秋分の日は予測値。 正式な日付は前年2月の官報で決まる。計算式(1980〜2099年向けの近似式)は公表前の年には予測でしかない
-
new Date('2026-05-06')はタイムゾーンでずれる。 日付だけの ISO 文字列は UTC の0時として解釈されるため、UTC より西のサーバーでは前日になる。new Date(2026, 4, 6)のように数値で作る
new Date('2026-05-06').getDate(); // アメリカ時間のサーバーでは 5
new Date(2026, 4, 6).getDate(); // どこでも 6
まとめ
- 祝日判定を自作したら、公式の一覧と全件突き合わせる。目で見て「合ってそう」では、今回のような日曜日の例外に気づけない
- 内閣府のページ上なら、
fetchとTextDecoder('shift_jis')だけで突き合わせられる
この記事の祝日判定の全コード(振替休日・国民の休日・特例を含む)と、それを使った「○営業日後」「期間の営業日数」「銀行休業日(12/31〜1/3)」の計算を、Zenn の本の1章にまとめています。振込カナ・和暦・消費税の端数・CSV の文字化けなど、日本の業務データの落とし穴を、公式資料の出典とテストつきで扱った本です(最初の2章は無料)。