ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 구독의 보너스 날짜 계산하기
    Java 2023. 9. 19. 00:01

    개요

    구독의 주기는 1달로 일어난다고 가정해 보겠습니다.

    이때 만약 어떤 일로 인해서 미리 구독을 해지시키고 다시 1달짜리를 가입시켜 주어야 한다면 어떻게 될까요?

    예를 들어 10월 15일에 어떤 사용자가 구독을 시작했는데 10월 21일에 구독 해지가 일어나고 다시 가입이 진행된다면 이 유저는 보너스 6일을 얻어야 합니다.

     

    테스트

    class BonusPeriodTest {
    
        @Test
        @DisplayName("현재 날짜가 10월15일이고, 구독일이 10월 1일이라면 해당 구독은 11월 1일에 만기되야하므로 보너스날짜는 17일이다")
        void test1(){
         LocalDate currentDate = LocalDate.of(2023,10,15);
         LocalDate subscribedAt = LocalDate.of(2023,10,1);
    
         BonusPeriod actual = BonusPeriod.calculateBonusPeriodBy(currentDate, subscribedAt);
    
         Assertions.assertEquals(actual.getValue(), 17);
        }
    
        @Test
        @DisplayName("현재 날짜가 10월15일이고, 구독일이 9월 16일이라면 해당 구독의 만기일은 10월 16일이므로 보너스날짜는 1일이다")
        void test2(){
            LocalDate currentDate = LocalDate.of(2023,10,15);
            LocalDate subscribedAt = LocalDate.of(2023,9,16);
    
            BonusPeriod actual = BonusPeriod.calculateBonusPeriodBy(currentDate, subscribedAt);
    
            Assertions.assertEquals(actual.getValue(), 1);
        }
    
        @Test
        @DisplayName("현재 날짜가 9월15일이고, 구독일이 8월 16일이라면 해당 구독의 만기일은 9월 16일이므로 보너스날짜는 1일이다")
        void test3(){
            LocalDate currentDate = LocalDate.of(2023,9,15);
            LocalDate subscribedAt = LocalDate.of(2023,8,16);
    
            BonusPeriod actual = BonusPeriod.calculateBonusPeriodBy(currentDate, subscribedAt);
    
            Assertions.assertEquals(actual.getValue(), 1);
        }
    
        @Test
        @DisplayName("현재 날짜가 9월15일이고, 구독일이 8월 1일이라면 해당 구독의 만기일은 10월 1일이므로 보너스날짜는 16일이다")
        void test4(){
            LocalDate currentDate = LocalDate.of(2023,9,15);
            LocalDate subscribedAt = LocalDate.of(2023,8,1);
    
            BonusPeriod actual = BonusPeriod.calculateBonusPeriodBy(currentDate, subscribedAt);
    
            Assertions.assertEquals(actual.getValue(), 16);
        }
    
        @Test
        @DisplayName("현재 날짜가 9월15일이고, 구독일이 8월 15일이라면 해당 구독의 만기일은 9월 15일이므로 보너스날짜는 0일이다")
        void test5(){
            LocalDate currentDate = LocalDate.of(2023,9,15);
            LocalDate subscribedAt = LocalDate.of(2023,8,15);
    
            BonusPeriod actual = BonusPeriod.calculateBonusPeriodBy(currentDate, subscribedAt);
    
            Assertions.assertEquals(actual.getValue(), 0);
        }
    }

    이 정도가 동작한다면 보너스 날짜 계산로직이 동작한다고 생각하고 테스트를 작성해 보았습니다.

     

    코드

    @Value(staticConstructor = "bonusOf")
    public class BonusPeriod {
    
        public static BonusPeriod noBonus() {
            return BonusPeriod.bonusOf(0, DAY);
        }
    
        int value;
        BonusPeriodUnit unit;
    
        public static BonusPeriod calculateBonusPeriodBy(LocalDate currentTime, LocalDate subscribedAt){
            int currentDays = currentTime.getDayOfMonth();
            int subscribedAtDays = subscribedAt.getDayOfMonth();
            if(currentDays == subscribedAtDays){
                return BonusPeriod.noBonus();
            }
            if(currentDays < subscribedAtDays){
                return BonusPeriod.bonusOf(subscribedAtDays - currentDays, DAY);
            }
    
            int currentTimeMonthEndOfDay = currentTime.lengthOfMonth();
            int bonusDays = currentTimeMonthEndOfDay - currentDays + subscribedAtDays;
            return BonusPeriod.bonusOf(bonusDays, DAY);
        }
    }

    현재날짜와 같이 제어할 수 없는 값이 메서드의 내부로 들어오면 테스트하기가 어려워집니다.

    따라서 테스트를 위해 현재날짜와 구독일을 인자로 주입받습니다.

     

    현재날짜와 구독일이 같은 경우에는 보너스기간이 없습니다. (단, 정책에 따라 하루를 추가해 줄 수도 있을 것 같습니다.)

     

    현재날짜(예: 15일) 보다 구독일(예: 20일)이 큰 경우에는 단지 20일-15일을 하여 5일을 보너스 기간을 넣어줄 수 있습니다.

     

    현재날짜(예: 15일)보다 구독일(예: 1일)이 작은 경우에는 현재월의 말일을 알아야 합니다.

    이때 말일은 30일 31일 28일 등으로 다양하기 때문에 LocalDate 함수의 lenghOfMonth를 활용하여 구해줍니다.

    이후 말일과 현재날짜의 차이를 구하고 구독일을 더하여 계산해 줍니다.

     

    예를 들어 현재날짜가 8월 15일이고, 구독일이 8월 5일인 경우에는 원래의 구독만료일은 9월 5일입니다.

    만료일로부터 현재까지 남은 기간(보너스기간)을 계산해야 합니다.

    8월의 마지막(31일)과 현재날짜(15일)를 빼줍니다. (이러면 8월분에 대한 남은 기간이 계산됩니다.)

    그리고 구독이 만료되어야 하는 날(5일)을 더해주게 되면 만료일로부터 남은 기간을 계산할 수 있게 됩니다.

     

    lengOfMonth 메서드

    @Override
    public int lengthOfMonth() {
    	switch (month) {
    		case 2:
    			return (isLeapYear() ? 29 : 28);
    		case 4:
    		case 6:
    		case 9:
    		case 11:
    			return 30;
    		default:
    			return 31;
    	}
    }

    2월일 경우는 윤년을 계산하고 30일, 31일을 계산하여 넘겨주는 모습을 확인할 수 있습니다.

     

     

    'Java' 카테고리의 다른 글

    BigDecimal이란?  (0) 2024.01.10
    Throwable 클래스란?  (0) 2023.06.16
    Instant vs LocalDateTime  (0) 2023.05.27
    [Java] 날짜,시간과 관련된 LocalDateTime의 역사  (0) 2022.08.27
    [Java]ExecutorService란?  (0) 2022.07.31

    댓글

Designed by Tistory.