-
[Java] SpringBoot : 기념일 날짜 계산하기(util.Date, sql.Date, Calendar 변환, 지난 일 및 남은 일 계산, list 정렬)프로젝트/기능 정리 2023. 7. 27. 22:06
목적
- 특정 날짜를 기준으로 오늘까지 일(days)수와 특정 날짜까지 남은 일수 구하기
- 기념일 계산을 위해 시작일을 기준으로 100일, 200일, ~년 ··· 단위 구하기
- 오늘까지의 일수 이상의 기념일만 4개씩 보이도록 구현
진행한 프로젝트의 마이페이지에 등록된 날짜를 기준으로 기념일을 보여주는 기능이 필요했다.
가진 정보는 회원이 등록한 날짜 하나이므로 해당 값을 통해 자동으로 이후 날짜를 계산해주는 흐름을 만들어보도록 하자.
완성된 화면은 아래와 같다.
완성 이미지 예시 만약 시간을 몇 달 돌려보면 아래와 같다.
완성 이미지 예시 보통은 DB에 등록되어 있는 날짜 데이터를 어떤 모델에 담아 가져온 후 작업을 하게되지만, 날짜를 테스트하기 위해 아래 과정을 진행해줬다.
[Java]
@RequestMapping("/mypage/main.do") public String main(HttpSession session, Model model) { //코드생략 String firstDate = "20221005"; SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); //코드생략
String으로 날짜를 입력했고 SimpleDateFormat을 통해 변환할 포맷을 지정해줬다.
보통 모델에 담아서 가져올 때 sql.Date 타입을 사용했기에 해당 과정을 맞춰주기 위해 sql.Date 타입으로 변환하는 과정을 넣었다.
// try catch 생략 Date date = format.parse(firstDate); long dateConv = date.getTime(); java.sql.Date sqlDate = new java.sql.Date(dateConv); couple.setCp_date(sqlDate); //VO에 담는 과정(날짜 계산과정이랑 상관없음) //이하 코드 생략
가장 위 Date는 java.util.Date를 사용한 것이다.
지정한 format에 String 형태로 입력한 날짜를 넣어 파싱해줬다.
이렇게 변환한 util.Date에 .getTime()을 사용하면 밀리세컨드 값을 반환하는데 이를 sql.Date에 넣어 타입을 맞춰줬다.
util.Date.getTime() 설명 만약 DB에서 날짜를 sql.Date 타입으로 날짜를 받아왔다면 위 과정 없이 바로 아래 과정을 진행하면 된다.
[차이값 구하기 - 기준일로부터 현재까지 지난 일 수]
날짜 변환하는 내용들을 찾아보면서 이것 저것 섞였는데(Calendar, util.Date, sql.Date) 기록을 남기기 위해 다 적는 것이지 이 과정이 필수적이지는 않다.
결론적으로는 어떤 방식으로든 날짜를 밀리세컨드 단위로 변환하여 두 값의 차이를 구한 후 해당 밀리세컨드 값을 일수(Days)로 변환해주면 끝이다.
Calendar firstDateC = new GregorianCalendar(); firstDateC.setTimeInMillis(couple.getCp_date().getTime()); Date firstDateD = firstDateC.getTime();
우선 Calendar 객체를 생성해줬다.
그리고 DB에서 가져온 sql.Date 타입의 데이터를 밀리세컨드 단위로 변환(.getTime())하여, 생성한 Calendar의 날짜를 세팅해줬다.
그걸 다시 util.Date 타입의 변수에 밀리세컨드 단위로 넣어주면 기준 날짜 세팅이 완료된다.
Calendar nowDateC = Calendar.getInstance(); Date nowDateD = nowDateC.getTime();
위 코드는 같은 방식으로 오늘 날짜를 구하는 방법이다.
이렇게 구한 두 날짜(밀리세컨드 단위)의 차이를 구해주면, 기준일로부터 얼마가 지났는지 알 수 있다.
long diff = nowDateD.getTime() - firstDateD.getTime();
물론 위 값은 밀리세컨드 단위이므로 이를 일 단위로 변환해주는 과정이 필요한데 아래와 같다.
since = TimeUnit.MILLISECONDS.toDays(diff) + 1; // try문 밖에 선언
1을 더해준 이유는 당일 날짜를 포함하지 않아서 그렇다. 시간 단위가 계산되지 않아서 만 1일이 되어야 1일로 계산이 되는듯 하다.
예를 들어, 오늘 날짜인 2023.07.27이 기준 날짜이면 위 과정을 거쳐 계산했을 때 since가 0이 나온다.
즉, 당일이면 0일로 표시되고 내일인 2023.07.28이 되어야 1일이 된다는 것이다.
아무튼 보통은 만난 당일부터 1일로 치니 1을 더해줬다.
여기까지 하면 기준 날짜로부터 현재까지 지난 일수를 계산할 수 있다.
[특정일까지 남은 일 계산]
다음은 기준일로부터 특정 날짜까지 남은 일을 구해보자.
여기서는 기념일을 구하는 것으로 100일, 200일 1년, 300일 등의 단위로 해당하는 날짜와 남은 일을 구했다.
먼저 기준일에서 4개의 날짜를 뽑아냈다.
예를 들어, 오늘이 75일 째 되는 날이라면, 100일, 200일, 300일, 1년을 뽑아내고,
296일 째 되는 날이라면 300일, 1년, 400일, 500일을 뽑아냈다.
단순하게 계산했는데 식은 아래와 같다.
((since.intValue()-1)/100 + 1) * 100 (((since.intValue()-1)/100 + 1) * 100) + 100 (((since.intValue()-1)/100 + 1) * 100) + 200 ((since.intValue()-1)/365 + 1) * 365
since는 위에서 구한 기준일에서 오늘까지 지난 일 수이다.
개념은 since를 100으로 나눠 그 몫을 구한 다음, 해당 값에 100을 곱해주는 것이다.
하지만 만약 값에 그냥 100을 곱하면 100 이하의 숫자일 경우 몫이 0이라 1을 더해줬다.
예를 들어, 오늘이 50일이면 100으로 나눴을 때 몫이 0이 된다.
하지만 원하는 기능은 since가 100일 이하이면 100일째 되는 날의 날짜와 D-day를 표시하는 것이므로 마지막에 1을 더해 보정해줬다.
앞에 since값에 -1을 해준 이유는 만약 딱 나누어 떨어질 경우, 즉 since가 100인 경우 100/100 + 1이 되므로 값이 2가된다.
since가 100일이라면 화면에 0일 남음 또는 오늘! 과같은 메시지를 보여줘야 하기 때문에 -1을 해서 보정해줬다.
나머지는 이렇게 구한 값에 100과 200을 더해 다음 100일 후의 날짜를 구하도록 한 것이고, 1년도 마찬가지다.
코드를 조금 단순화 하기 위해 위 값들을 처리할 메소드를 하나 만들었다.
우선 메소드를 호출하는 부분은 아래와 같다.
Map<String, Object> map1 = calcDate(((since.intValue()-1)/100 + 1) * 100, couple.getCp_date().toLocalDate()); Map<String, Object> map2 = calcDate((((since.intValue()-1)/100 + 1) * 100) + 100, couple.getCp_date().toLocalDate()); Map<String, Object> map3 = calcDate((((since.intValue()-1)/100 + 1) * 100) + 200, couple.getCp_date().toLocalDate()); Map<String, Object> map4 = calcDate(((since.intValue()-1)/365 + 1) * 365, couple.getCp_date().toLocalDate());
첫 번째 인자에는 위 식을 넣어주고, 두 번째 인자에는 기준일을 LocalDate 타입으로 변환하여 넣어줬다.
private Map<String, Object> calcDate(int since, LocalDate first) { Map<String, Object> map = new HashMap<>(); LocalDate AnniversaryDate = first.plusDays(since); //LocalDate -> Date ZoneId zoneId = ZoneId.systemDefault(); Date AnniDateConvert = Date.from(AnniversaryDate.atStartOfDay(zoneId).toInstant()); Date today = new Date(); long diff = AnniDateConvert.getTime() - today.getTime(); Long remain = ((diff / (24 * 60 * 60 * 1000L)) % 365); map.put("day", since); map.put("date", AnniversaryDate); map.put("remain", remain); return map; }
가장 먼저 기준일로부터 100일, 200일, 365일 이후의 날짜를 구하기 위해 LocalDate 객체의 plusDays 메소드를 사용했다.
이렇게하면 기준일로부터 since로 넘겨준 100 or 200 or 365 or 400 ··· 등의 값만큼 더해진 날짜를 구해준다.
다음은 LocalDate 타입의 날짜를 util.Date 타입으로 변환하기 위해 위에 복잡한 구조를 사용했는데,
이것저것 찾아보며 수정하다 저렇게 된거라 쉬운 방법이 있으면 다르게 진행해도 된다.
이렇게 변환한 100일, 200일, 400일 ···의 날짜를 밀리세컨드 단위로 변환하여 오늘 날짜와 빼준 것이 diff이다.
diff는 밀리세컨드 단위이므로 이를 일 단위로 바꾸기 위해 ((diff / (24 * 60 * 60 * 1000L)) % 365) 이 수식을 사용했다.
이렇게 계산된 값들을 map에 담고 이를 리턴하는 것으로 메소드는 종료된다.
map에 담긴 데이터를 화면으로 보자면 이렇다.
map 데이터 화면 예시 [날짜순 정렬]
마지막은 이렇게 생성한 map 객체들을 list에 담아주고, day가 낮은 순으로 정렬해주면 된다.
//코드생략 List<Map<String, Object>> list = new ArrayList<>(); list.add(map1); list.add(map2); list.add(map3); list.add(map4); Map<String, Object> tmp; for(int i = 0; i < 3; i++) { for(int j = i+1; j < 4; j++) { if((int)list.get(j).get("day") < (int)list.get(i).get("day")) { tmp = list.get(j); list.remove(j); list.add(j, list.get(i)); list.remove(i); list.add(i,tmp); } } } model.addAttribute("daylist", list); model.addAttribute("since", since); //코드 생략
루프를 돌면서 day의 값을 앞뒤로 비교하여 작은 값을 앞의 위치로 바꿔준 것이다.
[JSP]
<div id = "couple_since"> <p>오늘로</p> <p id = "couple_today"><strong>${since}일</strong></p> <span id= "couple_since"><small>since</small> <fmt:formatDate value="${couple.cp_date}" pattern = "yyyy.MM.dd"/></span> </div> <div id = couple_anniversary> <c:forEach var="list" items="${daylist}" varStatus="status"> <ul class="couple_anniversary_list"> <li class="couple_anni_date"> <span> <c:if test="${list.day % 365 == 0}"> <fmt:formatNumber value = "${list.day/365}"/>주년 </c:if> <c:if test = "${list.day % 365 != 0}"> ${list.day}일 </c:if> </span> </li> <li class="couple_anni_date_remain"> <span> <c:if test = "${list.remain == 0}"> 오늘! </c:if> <c:if test = "${list.remain != 0 }"> ${list.remain}일 남음 </c:if> </span> <br> <span>${list.date }</span> </li> </ul> </c:forEach>
JSP 태그는 위와 같이 작성했다.
이렇게 해서 기준일로부터 오늘까지 지난 일 수, 기준일로부터 특정 날짜까지 남은 일 수와 날짜를 구해봤다.
날짜 타입 변환 과정에 대해서만 찾아보고 원하는 기능에 대한 로직은 찾아보지 않아서 다른 사람들은 어떻게 구하는지 모르겠지만 일단 이대로 작동은 잘 되는 것 같다.
뭔가 자동화가 부족한 느낌이 괴장이 들고.. 코드도 뭔가 깔끔하지 않은 것 같지만 다른 방법이 딱히 떠오르지 않았다.
그래도 나중에 이와 비슷한 날짜 계산을 해야될 때가 오면 참고할 수 있도록 정리해둔 것으로 하고 마무리하겠다.
'프로젝트 > 기능 정리' 카테고리의 다른 글