티스토리 뷰

java

[JAVA] 공휴일 계산하기

sehyeona 2022. 2. 16. 02:08

최근 spring boot 프로젝트에서 공무원 월급일자를 계산해야하는 일이 생겼습니다. 

공무원의 경우 월급날이 공휴일이거나, 주말인 경우 월급을 앞당겨서 지급받기 때문에 특정년도의 공휴일을 계산해주는 계산기가 필요했습니다.


0. 라이브러리 추가

gradle project 에 음력달력을 사용하기 위한 라이브러리를 추가해줍니다.

dependencies {
	// https://mvnrepository.com/artifact/com.ibm.icu/icu4j
	implementation group: 'com.ibm.icu', name: 'icu4j', version: '4.0.1'
}

1. 공휴일 처리 클래스

Lunar2Solar 메서드를 이용하여 음력기준의 공휴일을 양력날자로 변환해준뒤, 대체 공휴일이 적용되는 경우 대체 공휴일까지 계산합니다.

저의 경우 올해의 공휴일만 계산하면 됐기 때문에 계산 결과를 LunarCalendar 의 map 필드에 저장했습니다.

만약 자주 사용하지 않는 년도의 공휴일을 계산하는 경우 map 필드에 저장되어 가비지 컬렉션에 의해 제거되지 않고 메모리를 잡아먹게 됩니다. 이런 경우에는 holidaySet 메서드에서 계산 결과를 map 에 저장하는 부분을 제거해야합니다.

import com.ibm.icu.util.ChineseCalendar;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class LunarCalendar {
    public static final int LD_SUNDAY = 7;
    public static final int LD_SATURDAY = 6;
    public static final int LD_MONDAY = 1;
    static Map<Integer, Set<LocalDate>> map = new HashMap<>();

    private LocalDate Lunar2Solar(LocalDate lunar) {
        ChineseCalendar cc = new ChineseCalendar();

        cc.set(ChineseCalendar.EXTENDED_YEAR, lunar.getYear() + 2637);   // 년, year + 2637
        cc.set(ChineseCalendar.MONTH, lunar.getMonthValue() - 1);        // 월, month -1
        cc.set(ChineseCalendar.DAY_OF_MONTH, lunar.getDayOfMonth());     // 일

        LocalDate solar = Instant.ofEpochMilli(cc.getTimeInMillis()).atZone(ZoneId.of("UTC")).toLocalDate();

        return solar;
    }

    /**
     * Return the set of holidays of input yaer
     * 
     * <p>results of this method would be <i>saved</i> in static field {@code this.map}
     * after the method calculate holidays of input year
     * 
     * @param year target year
     * @return set of holidays of input year 
     */
    public Set<LocalDate> holidaySet(int year) {
        if (map.containsKey(year)) return map.get(year);
        Set<LocalDate> holidaysSet = new HashSet<>();

        // 양력 휴일
        holidaysSet.add(LocalDate.of(year, 1, 1));   // 신정
        holidaysSet.add(LocalDate.of(year, 3, 1));   // 삼일절
        holidaysSet.add(LocalDate.of(year, 5, 5));   // 어린이날
        holidaysSet.add(LocalDate.of(year, 6, 6));   // 현충일
        holidaysSet.add(LocalDate.of(year, 8, 15));   // 광복절
        holidaysSet.add(LocalDate.of(year, 10, 3));   // 개천절
        holidaysSet.add(LocalDate.of(year, 10, 9));   // 한글날
        holidaysSet.add(LocalDate.of(year, 12, 25));   // 성탄절

        // 음력 휴일
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 1, 1)).minusDays(1));  // ""
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 1, 1)));  // 설날
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 1, 2)));  // ""
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 4, 8)));  // 석탄일
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 8, 14)));  // ""
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 8, 15)));  // 추석
        holidaysSet.add(Lunar2Solar(LocalDate.of(year, 8, 16)));  // ""

        try {
            // 어린이날 대체공휴일 검사 : 어린이날은 토요일, 일요일인 경우 그 다음 평일을 대체공유일로 지정
            holidaysSet.add(substituteHoliday(LocalDate.of(year, 5, 5)));
            // 삼일절, 광복절, 개천절, 한글날
            holidaysSet.add(substituteHoliday(LocalDate.of(year, 3, 1)));
            holidaysSet.add(substituteHoliday(LocalDate.of(year, 8, 15)));
            holidaysSet.add(substituteHoliday(LocalDate.of(year, 10, 3)));
            holidaysSet.add(substituteHoliday(LocalDate.of(year, 10, 9)));


            // 설날 대체공휴일 검사
            if (Lunar2Solar(LocalDate.of(year, 1, 1)).getDayOfWeek().getValue() == LD_SUNDAY) {    // 일
                holidaysSet.add(Lunar2Solar(LocalDate.of(year, 1, 3)));
            }
            if (Lunar2Solar(LocalDate.of(year, 1, 1)).getDayOfWeek().getValue() == LD_MONDAY) {    // 월
                holidaysSet.add(Lunar2Solar(LocalDate.of(year, 1, 3)));
            }
            if (Lunar2Solar(LocalDate.of(year, 1, 2)).getDayOfWeek().getValue() == LD_SUNDAY) {    // 일
                holidaysSet.add(Lunar2Solar(LocalDate.of(year, 1, 3)));
            }

            // 추석 대체공휴일 검사
            if (Lunar2Solar(LocalDate.of(year, 8, 14)).getDayOfWeek().getValue() == LD_SUNDAY) {
                holidaysSet.add(Lunar2Solar(LocalDate.of(year, 8, 17)));
            }
            if (Lunar2Solar(LocalDate.of(year, 8, 15)).getDayOfWeek().getValue() == LD_SUNDAY) {
                holidaysSet.add(Lunar2Solar(LocalDate.of(year, 8, 17)));
            }
            if (Lunar2Solar(LocalDate.of(year, 8, 16)).getDayOfWeek().getValue() == LD_SUNDAY) {
                holidaysSet.add(Lunar2Solar(LocalDate.of(year, 8, 17)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        map.put(year, holidaysSet);
        return holidaysSet;
    }

    /**
     * @param h holiday target of substitute holiday
     * @return new LocalDate Object: substitute holiday of input value 
     */
    private LocalDate substituteHoliday(LocalDate h) {
        if (h.getDayOfWeek().getValue() == LD_SUNDAY) {      // 일요일
            return h.plusDays(1);
        }
        if (h.getDayOfWeek().getValue() == LD_SATURDAY) {  // 토요일
            return h.plusDays(2);
        }
        return h;
    }
}

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함