施設予約などで使える! javaScript(jQuery)で半永久的な祝日判定をする方法

弊社で、施設予約システム「G.Open Door」を開発した際に、施設と利用日を選択した段階で、平日と土日祝で異なる料金を自動表示させる機能をつける必要がありました。

jQueryで判定していたので、曜日はすぐに取得・判定可能でしたが、
祝日は何かしらの処理をつくって判定する必要があります。

最終的に土日祝の判定をできる様になったのですが、結構大変だったので、そもそも祝日っていつ?というところから、どんなファイルになったかをシェアします。

祝日判定は来年分までしかできない

日本の祝日は、毎年2月に翌年の祝日が発表されています。
そのため、国で公表されている祝日を含め、「Holidays JP API」や「国民の祝日」などの、祝日を返すAPIも、来年の祝日までしか判定できません。

予約システム等で、2年先や3年先など、祝日が公表されていない期間も祝日判定がしたい場合に、どうにかする必要があります。

もっと祝日について調べてみた

祝日は、2023年4月現在、下記の5種類あるようです。
※もっと詳しく知りたい方は、内閣府ホームページの「国民の祝日」についてをご確認ください。

  1. 日付固定…例:元旦(1/1)など
  2. ◯月第◯曜日…例:成人の日(1月の第2月曜日)など
  3. 計算で算出されるもの…例:春分の日※後ほど計算式だします
  4. 振替休日…日曜日が祝日の場合の月曜日
  5. 国民の休日…二つの祝日に挟まれた平日

2年以上先の祝日判定をするには、日付固定と◯月第◯曜日はjsonファイルで、それ以外はjQueryで算出すれば、いけそう。

振替休日は、振替休日になる部分が祝日の場合は一番近い平日となるようなのですが、今回は簡易版として、日曜日が祝日の場合の月曜日が祝日となるようにします。

また、「国民の休日」は「敬老の日」と「秋分の日」の日付で稀に発生するようです。前回は2015年の9月22日がそれに該当するとのこと。チェックしたい日の前後が祝日かどうか調べればこちらもいけそうです。

そんな感じで、作ってみます。

jQueryで2年以上未来の祝日判定をする方法

今回は下記の様なファイル構成で、index.htmlに入力した日付が土日祝日かどうか判定するものを作成してみます。

  • index.html
  • js/holiday.js
  • js/check.js
  • js/tentative.js
  • json/tentative.json
  • css/style.css

1. 判定する用のjsファイルを仮作成

チェックに使用する関数のベースを作成します。
これに書き加えて、APIを使った祝日判定と、APIが無い期間は自作したファイルから判定する記述を追記していきます。

js/check.js

この段階では、土日のみ判定できます。

function checkHoliday(inputDate){
  // チェックしたい日付を整形
  const getDate = new Date(inputDate);
  const getYear = getDate.getFullYear();
  const getMonth = ("0" + (getDate.getMonth() + 1)).slice(-2);
  const getDay = ("0" + getDate.getDate()).slice(-2);
  const getWeek = getDate.getDay();

  // チェックできる形式にしておく
  const checkDateFull = getYear + "-" + getMonth + "-" + getDay; // API用
  const checkDate = getMonth + "-" + getDay; // 自作用

  // チェック結果を返す配列
  let checkResult = false;

  // 今日の年月日取得
  let nowDate = new Date();
  let nowYear = nowDate.getFullYear();

  // チェック
  if (getWeek == 0 || getWeek == 6) {
    // 土日ならtrueを返す
    checkResult = true;
  } else if (Number(getYear) - Number(nowYear) <= 2 && 1948 <= Number(getYear)) {
    // チェックしたい日が2年以内ならAPI使用
  } else {
    // チェックしたい日が2年以上先なら自作データにて判定
  }

  return checkResult;
}

2. jQueryで祝日ファイルを読み込む

公表されている部分に関しては、内閣府が出しているCSVファイルか、素敵な方々が作成してくださっているAPIを利用します。
今回は、「国民の祝日」のAPIを利用する想定で進めていきます。

js/holiday.js

国民の祝日APIを読み込みます。

const URL = 'https://api.national-holidays.jp/all';
const DATAS = await jQuery
  .ajax({
    type: 'GET',
    url: URL,
    datatype: 'json',
    async: false,
  })
  .done(function () {
    return;
  });

const Holidays = await DATAS.map(function (obj) {
  return obj['date'];
});

export default Holidays;

今回は、指定日が祝日か・祝日じゃないかだけを判定するために、const Holidaysで「2024-01-01」など日付だけ確認できるデータを返す様にしています。

js/check.js

判定する用のjsファイルの一行目に、作成したjsファイルをインポートしておきます。

import holidayList from './holiday.js';

check.jsのコメント「// チェックしたい日が2年以内ならAPI使用」を探しその下に下記追加します。

APIでとってきた配列中にチェックしたい日付と合致するものがあれば合致する配列番号を返す$.inArrayを使って判定します。(無い場合は-1を返す)
これで、APIがある場合は祝日判定ができます。

if ($.inArray(checkDateFull, holidayList) > -1) {
  checkResult = true;
}

3. jsonファイルで日付固定・曜日固定の祝日を作る

日付固定はそのまま入力すれば良いとして、1月の第2月曜日など用に自分がわかるルールを作っておきます。

今回は「01-w21」など、01(月・2桁の数字) w(曜日判定する識別子)2(何週目か数字)1(曜日)というルールで作成しています。
最後の曜日に使用している数字はjQueryのgetDay()で曜日をチェックした時に返される値の、0=日曜日、1=月曜日…を使用しています。

json/tentative.json

後で「やっぱり何の日かも知りたい」となった時用に、日付と何の日かを記載したjsonファイルにしました。

{
  "data": {
    "01-01": "元旦",
    "01-w21": "成人の日",
    "02-11": "建国記念の日",
    "02-23": "天皇誕生日",
    "04-29": "昭和の日",
    "05-03": "憲法記念日",
    "05-04": "こどもの日",
    "07-w31": "海の日",
    "08-11": "山の日",
    "09-w31": "敬老の日",
    "10-w21": "スポーツの日",
    "11-03": "文化の日",
    "11-23": "勤労感謝の日"
  }
}

js/tentative.js

自作jsonの方も、API同様に日付だけ返す様にしておきます。

const URLBASE = window.location.origin;
const URL = `${URLBASE}/holiday/json/tentative.json`;
const DATAS = await jQuery
  .ajax({
    type: "GET",
    url: URL,
    datatype: "json",
    async: false,
  })
  .done(function () {
    return;
  });

const Holidays = Object.keys(DATAS.data);

export default Holidays;

js/check.js

祝日APIの読み込み同様、2行目に作成したjsファイルをインポートしておきます。

import tentativeList from "./tentative.js";

◯月第◯曜日のような祝日用に、指定した日がその月の何回目の曜日か調べる用の関数を作ります。

まずは、指定した日と同月で同じ曜日の日を取得します。

// 指定日と同月で同じ曜日の日を全て取得
function getAllWeekDate(inputDate) {
  const date = new Date(inputDate);
  const day = date.getDay();
  const year = date.getFullYear();
  const month = date.getMonth();
  const days = [];

  for (let i = 1; i <= 31; i++) {
    const tmpDate = new Date(year, month, i);

    if (month !== tmpDate.getMonth()) break;
    if (tmpDate.getDay() !== day) continue;
    days.push(tmpDate);
  }
  return days;
}

作成した関数を使って、さらに指定日が何週目であるか調べる関数を作成します。

// 指定日が第何週目か
function getWeekNum(inputDate) {
  const sameWeeks = getAllWeekDate(inputDate);
  let weekNum = 0;
  for(let i=0; i < sameWeeks.length; i++){
    const date = new Date(sameWeeks[i]);
    const fullDate = date.getFullYear() + '-' + ('0' + (date.getMonth() + 1)).slice(-2) + '-' + ('0' + date.getDate()).slice(-2);
    if (fullDate == inputDate) {
      weekNum = i + 1;
    }
  }
  return weekNum;
}

check.jsのコメント「// チェックしたい日が2年以上先なら自作データにて判定」を探しその下に下記追加します。
チェックはAPI同様に$.inArrayを用いてチェックします。

let checkTentativeResult = false; // 自作判定の結果格納用

// 日付がぴったり一致する場合
if ($.inArray(checkDate, tentativeList) > -1) {
  checkTentativeResult = true;
}

// ◯月第◯曜日の場合
const chackWeek = getWeekNum(checkDateFull);
let checkDateWeek = "";
if (chackWeek > 0) {
  checkDateWeek = getMonth + "-w" + chackWeek + getWeek;
  if ($.inArray(checkDateWeek, tentativeList) > -1) {
    checkTentativeResult = true;
  }
}

// 自作チェックの結果
checkResult = checkTentativeResult;

ここまでで、2年以上先の固定日と◯月第◯曜日タイプの祝日判定ができる様になりました。

4. 計算が必要な春分の日と秋分の日を判定

春分の日と秋分の日は計算式で算出します。計算式の理由は説明できませんが、ありがたくそのまま使用させてもらいます。

  • 春分の日 → Math.floor(20.8431 + 0.242194 * (調べたい年 – 1980)) – Math.floor(調べたい年 – 1980) / 4);
  • 秋分の日 → Math.floor(23.2488 + 0.242194 * (調べたい年- 1980)) – Math.floor(調べたい年- 1980) / 4);

js/check.js

コメント「// 自作チェックの結果」の前の行あたりに下記を追加します。

// 春分の日
const spring = Math.floor(20.8431 + 0.242194 * (Number(getYear) - 1980)) - Math.floor((Number(getYear) - 1980) / 4);
let splingDate = "03-" + spring;
if (getMonth == "03" && getDay.match(/19|20|21|22/)) {
  const springWeek = new Date(`${getYear}-${getMonth}-${spring}`).getDay();
  if (getDay == spring) {
    checkTentativeResult = true;
  } else if (springWeek == 0 && Number(spring) + 1 == getDay) {
    checkTentativeResult = true;
  }
}

// 秋分の日
const autumn = Math.floor(23.2488 + 0.242194 * (Number(getYear) - 1980)) - Math.floor((Number(getYear) - 1980) / 4);
let autumnDate = "09-" + autumn;
if (getMonth == "09" && getDay.match(/22|23|24|25/)) {
  const autumnWeek = new Date(`${getYear}-${getMonth}-${autumn}`).getDay();
  if (getDay == autumn) {
    checkTentativeResult = true;
  } else if (autumnWeek == 0 && Number(autumn) + 1 == getDay) {
    checkTentativeResult = true;
  }
}

春分の日は3月の19~21日、秋分の日は9月の22〜24日とのことなので、振替休日があった場合の1日分をプラスして、チェックしたい月が3月で日付が19〜22日の場合は春分の日のチェックを、9月で22〜25日の場合は秋分の日チェックをし、春分の日や秋分の日が日曜日ならその翌日は振替休日なので祝日となる様にしています。

また、次項目の国民の休日判定等に使用するため、春分の日・秋分の日をそれぞれ何日か保存しておきます。

5. 振替休日か国民の休日か判定

チェックしたい日の前日が日曜日で祝日の場合は振替休日になります。
国民の休日は、祝日と祝日の間の平日が祝日となります。

これをチェックするために、まずは、チェックしたい日の前後の日付を取得します。

js/check.js

コメント「// 自作チェックの結果」の前の行あたりに下記を追加します。

// チェックしたい日の前日を取得
let beforeDay = ("0" + (Number(getDay) - 1)).slice(-2);
let beforeYear = getYear;
let beforeMonth = getMonth;
let beforeDate = beforeMonth + "-" + beforeDay;
if (Number(getDay) == 1) {
  // チェックしたい日が1日なら前月の月末日を入れる
  if (Number(getMonth) == 1) {
    beforeMonth = "12";
    beforeYear = Number(beforeYear) - 1;
  } else {
    beforeMonth = ("0" + (Number(getMonth) - 1)).slice(-2);
  }
  let beforeLastDay = new Date(getYear, Number(getMonth) - 1, 0).getDate();
  beforeDay = ("0" + beforeLastDay).slice(-2);
  beforeDate = beforeMonth + "-" + beforeDay;
}
const beforeWeek = new Date(`${beforeYear}-${beforeDate}`).getDay();
const beforeWeekCheck = getWeekNum(`${beforeYear}-${beforeDate}`);
const checkBeforeWeek = `${beforeMonth}-w${beforeWeekCheck}${beforeWeek}`;

// チェックしたい日の翌日を取得
let afterDay = ("0" + (Number(getDay) + 1)).slice(-2);
let afterYear = getYear;
let afterMonth = getMonth;
let afterDate = getMonth + "-" + afterDay;
let afterLastDay = new Date(getYear, getMonth, 0).getDate();
if (Number(afterLastDay) == Number(getDay)) {
  // チェックしたい日が月末なら翌月1日を入れる
  if (Number(getMonth) == 12) {
    afterMonth = "01";
    afterYear = Number(getYear) + 1;
  } else {
    afterMonth = ("0" + (Number(getMonth) + 1)).slice(-2);
  }
  afterDate = afterMonth + "-01";
}
const afterWeek = new Date(`${afterYear}-${afterDate}`).getDay();
const afterWeekCheck = getWeekNum(`${afterYear}-${afterDate}`);
const checkAfterWeek = `${afterMonth}-w${afterWeekCheck}${afterWeek}`;

まずは振替休日から判定できるようにします。

js/check.js

続いて下記を追加します。
春分の日・秋分の日は前項目でチェックしているので、こちらでは自作祝日jsonの中にある祝日と一致するかチェックします。

これまでの祝日判定同様、チェックしたい日の前日が祝日か$.inArrayで確認します。
また、今回は簡易的に、チェックしたい日が月曜日で前日が祝日の場合は、祝日判定となる様にします。

// 振替休日かチェック
if ($.inArray(beforeDate, tentativeList) > -1 && getWeek == 1) {
  checkTentativeResult = true;
}

js/check.js

続いて国民の休日をチェックします。

こちらがあり得るパターンとしては、9月の敬老の日と秋分の日で発生することがあるようですので、下記の様にチェックしています。

// 国民の休日かチェック(前日と翌日が祝日で、チェックしたい日が平日)
if (
  ($.inArray(beforeDate, tentativeList) > -1 || $.inArray(checkBeforeWeek, tentativeList) > -1 || beforeDate == splingDate || beforeDate == autumnDate) &&
  ($.inArray(afterDate, tentativeList) > -1 || $.inArray(checkAfterWeek, tentativeList) > -1 || afterDate == splingDate || afterDate == autumnDate)
) {
  checkTentativeResult = true;
}

6. 表示部分を作成

日付を入力してボタンを押すと、下に平日か土日祝か表示されるようにします。

index.html

<html lang="ja">
  <head>
    <title>祝日check</title>
    <meta name="viewport" content="width=device-width" />
  	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <section>
      <h1>祝日チェッカー</h1>
      <div>
        <input type="date" name="calender" value="">
        <button id="holiday_check" type="button">確認</button>
      </div>
      
      <div id="result">
        <p></p>
      </div>
    </section>

    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script type="module" src="js/check.js"></script>
  </body>
</html>

css/style.css

section{
  margin: 3rem auto;
  max-width: 18rem;
}
#result{
  margin:1em auto 0;
}

7. 入力された日付が祝日かどうか判定する

js/check.js

// 入力された日付が祝日かどうかチェックする
$(function () {
  let msg = "";
  $("#holiday_check").on('click',function(){
    const inputDate = $('input[name="calender"]').val();
    const result = checkHoliday(inputDate);
    if (result){
      msg = "土日祝です。"
    }else{
      msg = "平日です。";
    }
    $("#result p").text(msg);
  });
});

8. index.htmlを開いてチェック

最後に、index.htmlを開いて、日付を入力して「確認」ボタンを押すと、動作を確認できます。

まとめ

実装するとなると、APIの使用ではなく、内閣府HPにある公式のcsvを使ったり、振替休日も、振替日が祝日だったら、最も近い平日を振替休日にするなど調整が必要になります。

ですが、ある程度カバーできれば良い場合はこれでも問題ないのかな…程度にまとめてみました。

また、カスタマイズすれば、「第2水曜日は定休日」など独自の休みを追加したりすることもできますので、よろしければお試しください。

お問い合わせ

ガリレオ アンド ヴィーナス合同会社では、Wordpressを使用したコーポレートサイトなどの構築の他、施設予約サイトの構築、カート機能のついたECショップなどのWEBサイト制作、その他Webを用いたシステム開発・構築を行なっております。

今回の祝日判定の様な機能を用いて
・お店の決まった定休日を毎回手入力するのが面倒なので、自動表示できるようにしたい
・夏休み期間など特定の期間だけ、料金を10%UPで表示したい
などなど、ちょっと面倒くさい…を便利にするお手伝いもしております。

「こんなことできるかな?」や「こんなサービスをやりたい!」などありましたらお気軽にお問い合わせください。

    関連記事

    1. ブログなどの投稿記事で実際にWEBデザイナーがよく使用する HTML一…

    2. WordPress(ワードプレス)って何?ウェブ系フロントエンジニアが…

    3. Advance Custom fields(ACF)で作成した項目を検…

    4. 15分でウェブショップ完成!無料&簡単なBASEを使ったウェブショップ…

    5. 【SEO対策】HTMLと仲良しなフロントエンドエンジニアが教える検索エ…

    6. 専門知識不要でECサイト開設!決済サービスSquareアカウントだけで…