Salesforce(Apex + Visualforce)でカレンダーを作る際に、祝日判定の機能がなかったのでApexクラスでつくりました。
祝日は大きく分けて次のパターン
- 国民の休日
- 固定祝日 ex. 元旦、建国記念の日、憲法記念日など
- 春分の日
- 秋分の日
- ハッピーマンデー ex. 成人の日、海の日、敬老の日、体育の日
- 振替休日
しかしここで気をつけなれけば行けないのが、当年だけに存在する固定祝日がある
- 新天皇即位日
- 即位礼正殿の儀
そのため、固定祝日を設定できるようオブジェクト化したほうがよいが、今回はApexクラス内で管理している。
春分の日と秋分の日
これは簡単、日付の計算方法が決まっているので、それをApexにするだけ
/**
* 春分の日かどうか
*
* @params Date d 調べたい日付
* @return boolean true:春分の日 / false:春分の日ではない
**/
private static boolean isSpringHoliday(Date d) {
return d.month() == 3 && d.day() == checkFourSeasons(20.8431, d.year());
}
/**
* 秋分の日かどうか
*
* @params Date d 調べたい日付
* @return boolean true:秋分の日 / false:秋分の日ではない
**/
private static boolean isAutumnHoliday(Date d) {
return d.month() == 9 && d.day() == checkFourSeasons(23.2488, d.year());
}
ハッピーマンデー
これも簡単、日付が固定されているのでそれにあわせるだけ
/**
* ハッピーマンデーかどうか
*
* @params Date d 調べたい日付
* @return boolean true:ハッピーマンデー / false:ハッピーマンデーではない
**/
@TestVisible
private static boolean isHappyMonday(Date d) {
Integer year = d.year();
Integer month = d.month();
Integer day = d.day();
List<List<Integer> > holiday = new List<List<Integer> > {
// 月 第N曜日 開始年 終了年
new List<Integer> {1, 2, 2000, 9999}, // 成人の日
new List<Integer> {7, 3, 2003, 9999}, // 海の日
new List<Integer> {9, 3, 2003, 9999}, // 敬老の日
new List<Integer> {10, 2, 2000, 9999} // 体育の日(スポーツの日)
};
Integer dow = getDayOfWeek(Date.newInstance(year, month, 1));
Integer mondayOfWeek = 1;
Integer happyDay = mondayOfWeek - dow < 0 ? 7 + mondayOfWeek - dow : mondayOfWeek - dow;
happyDay++;
for (List<Integer> variable : holiday) {
if (month == variable[0] && year >= variable[2] && year <= variable[3]) {
happyDay = happyDay + 7 * (variable[1] - 1);
if (happyDay == day) {
return true;
}
}
}
return false;
}
固定祝日
固定もハッピーマンデーと似ており、処理を別にする必要は無いが、祝日分類のため別々のメソッドで管理している
/**
* 固定祝日かどうか
*
* @params Date d 調べたい日付
* @return boolean true:固定休日 / false:固定休日ではない
**/
@TestVisible
private static boolean isFixedHoliday(Date d) {
Integer year = d.year();
Integer month = d.month();
Integer day = d.day();
List<List<Integer> > holiday = new List<List<Integer> > {
// 月 日 開始年 終了年
new List<Integer> {1, 1, 1949, 9999}, // 元旦
new List<Integer> {2, 11, 1967, 9999}, // 建国記念の日
new List<Integer> {2, 23, 2020, 9999}, // 天皇誕生日
new List<Integer> {4, 29, 2007, 9999}, // 昭和の日
new List<Integer> {5, 3, 1949, 9999}, // 憲法記念日
new List<Integer> {5, 4, 2007, 9999}, // みどりの日
new List<Integer> {5, 5, 1949, 9999}, // こどもの日
new List<Integer> {8, 11, 2016, 9999}, // 山の日
new List<Integer> {11, 3, 1948, 9999}, // 文化の日
new List<Integer> {11, 23, 1948, 9999}, // 勤労感謝の日
// 以下、指定した年月日のみの祝日
new List<Integer> {5, 1, 2019, 2019}, // 新天皇即位日
new List<Integer> {10, 22, 2019, 2019} // 即位礼正殿の儀
};
for (List<Integer> variable : holiday) {
if (month == variable[0] && day == variable[1] && year >= variable[2] && year <= variable[3]) {
return true;
}
}
return false;
}
振替休日と国民の休日?
ここがめんどくさい、いわゆる振替系
/**
* 国民の休日かどうか
*
* @see https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
* @params Date d 調べたい日付
* @return boolean true:国民の休日 / false:国民の休日ではない
**/
@TestVisible
private static boolean isNationalHoliday(Date d) {
Date yesterday = d - 1;
Date tomorrow = d + 1;
return checkHoliday(yesterday) && checkHoliday(tomorrow) && !new List<Integer> {0, 6}.contains(getDayOfWeek(d));
}
/**
* 振替休日かどうか
*
* @params Date d 調べたい日付
* @return boolean true:振替休日 / false:振替休日ではない
**/
@TestVisible
private static boolean isTransferHoliday(Date d) {
// 当日が祝日または日曜日ならfalse
if (checkHoliday(d) || getDayOfWeek(d) == 0) {
return false;
}
Date yesterday = d - 1;
for (Integer i = getDayOfWeek(d); i >= 0; i--) {
if (!checkHoliday(yesterday)) {
return false;
} else if (getDayOfWeek(yesterday) == 0) {
return true;
}
yesterday = yesterday - 1;
}
return false;
}
祝日前後が平日か日曜日で判定をおこなう
全てをまとめたクラスはこちら
https://gist.github.com/akiyoshisamukawa/4a02a95e09522a23925376c498fc4d98
もっと効率よく処理を作成できるはずですので、あくまで参考として...