概要
平日のみbatchを稼働させたくて、土日はcronで簡単に除外できるけど祝日はサクッと除外できないのでJava側で祝日判定した。
ちょっと検索した感じJavaのサンプルがあんまり見当たらなかったので公開します。
注意
- 振替休日はちょっと複雑なので考慮しません
- 過去の年は考慮しません
- 2020年と2021年は特殊なので考慮しません
- 春分の日と秋分の日の計算式は1980~2099年のみ有効
環境
- Java 1.8
- SpringBoot 2.2.1.RELEASE
1. 祝日の定義classを作成
- 固定日付、ハッピーマンデー、春分の日、秋分の日の4パターン
PublicHoliday.java
package com.tamorieeeen.sample.consts;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 祝日
*
* @author tamorieeeen
*
*/
@Getter
@AllArgsConstructor
public enum PublicHoliday {
NEWYEAR("元日", "fixed", 1, 1, 0),
ADULT("成人の日", "monday", 1, 0, 2),
FOUNDATION("建国記念の日", "fixed", 2, 11, 0),
BIRTHDAY("天皇誕生日", "fixed", 2, 23, 0),
SPRING("春分の日", "spring", 3, 0, 0),
SHOWA("昭和の日", "fixed", 4, 29, 0),
CONSTITUTION("憲法記念日", "fixed", 5, 3, 0),
GREEN("みどりの日", "fixed", 5, 4, 0),
CHILDREN("こどもの日", "fixed", 5, 5, 0),
SEA("海の日", "monday", 7, 0, 3),
MOUNTAIN("山の日", "fixed", 8, 11, 0),
AGED("敬老の日", "monday", 9, 0, 3),
AUTUMN("秋分の日", "autumn", 9, 0, 0),
SPORTS("スポーツの日", "monday", 10, 0, 2),
CULTURE("文化の日", "fixed", 11, 3, 0),
THANKSGIVING("勤労感謝の日", "fixed", 11, 23, 0);
private String name;
// fixed: 日付固定
// monday: 第何月曜日
// spring: 春分の日
// autumn: 秋分の日
private String type;
// 月
private int month;
// 日付型: 日
private int dayOfMonth;
// 第何月曜日型: 第何か
private int weekOfMonth;
/**
* 固定祝日を取得
*/
public static List<PublicHoliday> getFixedHoliday() {
return Stream.of(values())
.filter(v -> v.type.equals("fixed"))
.collect(Collectors.toList());
}
/**
* ハッピーマンデーを取得
*/
public static List<PublicHoliday> getHappyMonday() {
return Stream.of(values())
.filter(v -> v.type.equals("monday"))
.collect(Collectors.toList());
}
/**
* 春分の日を取得
*/
public static PublicHoliday getSpringDay() {
return SPRING;
}
/**
* 秋分の日を取得
*/
public static PublicHoliday getAutumnDay() {
return AUTUMN;
}
}
2. 祝日判定classを作成
- 第何月曜日は
Calendar.DAY_OF_WEEK_IN_MONTH
を使う - 春分の日:
int(20.8431+0.242194*(年-1980)-int((年-1980)/4))
- 秋分の日:
int(23.2488+0.242194*(年-1980)-int((年-1980)/4))
HolidayService.java
package com.tamorieeeen.sample.service;
import java.time.LocalDate;
import java.util.Calendar;
import org.springframework.stereotype.Service;
import com.tamorieeeen.sample.consts.PublicHoliday;
/**
*
* @author tamorieeeen
*
*/
@Service
public class HolidayService {
// 1年は365.242194日なので、1年あたり0.242194日進む
private static final double DIFF_OF_YEAR = 0.242194;
// うるう年であった1980年から計算
private static final int LEAP_YEAR = 1980;
// 1980年の春分の時刻「20.8431日」
private static final double SPRING_DAY_BASE = 20.8431;
// 1980年の秋分の時刻「23.2488日」
private static final double AUTMN_DAY_BASE = 23.2488;
/**
* 祝日かどうか
*/
public boolean isHoliday() {
LocalDate today = LocalDate.now();
if (this.isFixedHoliday(today) || this.isHappyMonday(today) ||
this.isSpringDay(today) || this.isAutmnDay(today)) {
return true;
}
return false;
}
/**
* 固定祝日かどうか
*/
private boolean isFixedHoliday(LocalDate today) {
return PublicHoliday.getFixedHoliday()
.stream()
.map(h -> LocalDate.of(
today.getYear(), h.getMonth(), h.getDayOfMonth()))
.anyMatch(h -> h.isEqual(today));
}
/**
* ハッピーマンデーかどうか
*/
private boolean isHappyMonday(LocalDate today) {
return PublicHoliday.getHappyMonday()
.stream()
.map(h -> LocalDate.of(
today.getYear(), h.getMonth(),
this.getDayOfMonth(h.getMonth(), h.getWeekOfMonth())))
.anyMatch(h -> h.isEqual(today));
}
/**
* ハッピーマンデーの日付を取得
*/
private int getDayOfMonth(int month, int weekOfMonth) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_WEEK, 2);
cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, weekOfMonth);
return cal.get(Calendar.DAY_OF_MONTH);
}
/**
* 春分の日かどうか
*/
private boolean isSpringDay(LocalDate today) {
int dayOfMonth =
this.getMovedDays(today.getYear(), SPRING_DAY_BASE)
- this.getLeapYearDays(today.getYear());
LocalDate springDay = LocalDate.of(
today.getYear(),
PublicHoliday.getSpringDay().getMonth(),
dayOfMonth);
return today.isEqual(springDay);
}
/**
* 秋分の日かどうか
*/
private boolean isAutmnDay(LocalDate today) {
int dayOfMonth =
this.getMovedDays(today.getYear(), AUTMN_DAY_BASE)
- this.getLeapYearDays(today.getYear());
LocalDate autmnDay = LocalDate.of(
today.getYear(),
PublicHoliday.getAutumnDay().getMonth(),
dayOfMonth);
return today.isEqual(autmnDay);
}
/**
* 進んでる分を取得
*/
private int getMovedDays(int year, double baseDay) {
return (int) Math.floor(baseDay + this.getDiffDays(year));
}
/**
* 1980年からの差分を取得
*/
private double getDiffDays(int year) {
return DIFF_OF_YEAR * (year - LEAP_YEAR);
}
/**
* うるう年の分を取得
*/
private int getLeapYearDays(int year) {
return (int) Math.floor((year - LEAP_YEAR) / 4.0);
}
}
3. 呼び出しclassを作成
-
holidayService.isHoliday()
がtrueなら祝日、falseなら平日
SampleController.java
package com.tamorieeeen.sample.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.tamorieeeen.sample.service.HolidayService;
import lombok.extern.slf4j.Slf4j;
/**
*
* @author tamorieeeen
*
*/
@Slf4j
@Controller
public class SampleController {
@Autowired
private HolidayService holidayService;
@GetMapping("/sample")
public String top() {
if (holidayService.isHoliday()) {
log.info("today is holiday.");
} else {
log.info("today is weekday.");
}
return "sample";
}
}
感想
春分の日と秋分の日の計算がめんどくさい!