LoginSignup
7
7

More than 3 years have passed since last update.

【Java】祝日判定サンプル

Last updated at Posted at 2020-07-06

概要

平日のみ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";
    }
}

感想

春分の日と秋分の日の計算がめんどくさい!

参考

内閣府「国民の祝日」について
「今月の第3月曜日」の日付を求める
PHPで春分の日、秋分の日を計算

7
7
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7