준호씨의 블로그
Java - "x개월 x일 남았습니다" 구하기2. oracle months_between 본문
지난번에 Java8의 LocalDate와 Period를 이용해서 몇 개월 며칠 남았는지 구하는 코드를 만들어 보았습니다.
사실 이 코드를 만들게 된 이유는 레거시 코드의 개선작업을 하면서 였는데요. 결국 위의 방법은 사용하지 않고 코드를 새로 만들게 되었습니다.
기존에 몇개월 며칠 남은 것을 구할 때 oracle의 months_between 함수를 이용하고 있었습니다. 뭘 이런 걸 구하는데 sql로 했는지 의아하게 생각할 수도 있는데요. 예전에는 sql, procedure, trigger 등을 이용해서 DB에서 로직을 구현하는 경우도 꽤 있었습니다. 요즘은 잘 보기 힘들지만 레거시 코드에는 꽤 남아 있는 편입니다.
아무튼 Period에서 알려 주는 남은 기간은 months_between과는 좀 달랐습니다. 예를 들어 보겠습니다.
select abs(trunc(b)) rest_months,
trunc(abs(trunc(b)*(-1) + b)/(0.032258064516129))+1 rest_days
from (select months_between(sysdate, to_date('20210319','yyyymmdd')) b
from dual
);
오늘이 2020.03.20인데요. 어딘가 가입을 했는데 멤버십 기간이 1년이라고 가정하면 2021.03.19 까지가 될 것입니다. 위의 sql을 실행해 보면 REST_MONTHS 11, REST_DAYS 30이 나옵니다.
자 지난번 Java 코드를 봅시다. 똑같이 2021년 3월 19일 까지 남은 기간을 구하는 건데요.
LocalDate startDate = LocalDate.now();
LocalDate endDate = LocalDate.of(2021, 3, 19);
Period period = startDate.until(endDate);
System.out.println("REST_MONTHS: " + period.getMonths() + ", REST_DAYS: " + period.getDays());
결과는 REST_MONTHS: 11, REST_DAYS: 27 가 나옵니다.
아니 왜 남은 기간을 구하는데 11개월은 같은데 30일이 나오기도 하고 27일이 나오기도 하죠? 게다가 얼핏 보기에는 11개월 30일이 맞을 거 같습니다. 하지만 사실 11개월 27일이 맞습니다.
오늘 2020년 03월 20일로 부터 11개월 후는 2020년 02월 20일이 될 거고요. 2021년 2월은 28일까지입니다. 2월이 8일 3월이 19일 해서 27일이 남은 게 맞죠.
달력에다가 마지막 달에서 남은 일수를 직접 찍어 보았습니다.
하지만 느낌상으로는 좀 이상합니다. 한달이라함은 느낌상 30일 정도 되는데 말이죠. 추측컨대 기존에 구현한 사람은 30일로 나오는 걸 선호해서 저렇게 만들지 않았을까 생각해 봅니다.
남은 기간을 몇개월 며칠이 아니고 그냥 364일처럼 며칠만 표현하는 게 어떨까 의견도 있었습니다. 하지만 지금은 이게 중요 과제가 아니었기 때문에 표기방법을 바꾸는 건 나중에 생각해 보기로 했습니다. 일단은 기존처럼은 나오게 했어야 했지요. 그래서 Java로 Oracle의 months_between 함수를 구현해 보기로 했습니다.
빠르게 구현해야 되어서 일단 익숙한 Calendar를 이용해서 인터넷 자료들을 참고해서 만들었습니다.
public static double monthsBetween(Date baseDate, Date dateToSubstract) {
Calendar cal = Calendar.getInstance();
cal.setTime(baseDate);
int baseDayOfYear = cal.get(Calendar.DAY_OF_YEAR);
int baseYear = cal.get(Calendar.YEAR);
cal.setTime(dateToSubstract);
int subDayOfYear = cal.get(Calendar.DAY_OF_YEAR);
int subYear = cal.get(Calendar.YEAR);
return ((baseYear - subYear) * (cal.getMaximum(Calendar.MONTH) + 1)) +
(baseDayOfYear - subDayOfYear) / 31.0;
}
이제 monthsBetween 함수를 이용해서 남은 개월과 일 수를 구해봅니다.
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Date startDate = sdf.parse("20200320");
Date endDate = sdf.parse("20210319");
double v = monthsBetween(startDate, endDate);
int restMonths = Math.abs((int) v);
int restDays = (int) (Math.abs((int) v * (-1) + v) / 0.032258064516129) + 1;
System.out.println("REST_MONTHS: " + restMonths + ", REST_DAYS: " + restDays);
좀 복잡하지만 기존에 sql 로 구했던 결과처럼 REST_MONTHS: 11, REST_DAYS: 30 가 나옵니다.
여기서 0.032258064516129 라는 숫자가 무슨 숫자인가 궁금할 수 있는데요. 한 달을 1이라고 했을 때 31로 나눈 값입니다. months_between의 결과가 남은 개월 수를 소수점까지 표현해 주고 있어서 정부 부분은 개월 수, 소수 부분을 일수로 구하는 것입니다.
코드가 조금 복잡하긴 한데 좀 더 쉽고 이해하기 좋은 방법이 있는지 알아 보는거도 좋을 거 같네요.
'개발이야기' 카테고리의 다른 글
MySQL 버전 확인 방법 (0) | 2020.03.22 |
---|---|
Oracle - 버전 확인 방법. 버전 5자리 의미. (0) | 2020.03.21 |
Java - "x개월 x일 남았습니다" 구하기 (0) | 2020.03.17 |
OSX - cpan DBD::mysql (0) | 2020.03.05 |
OSX - cpan DBD::Oracle (0) | 2020.03.05 |