[ Project · Travel Road ] D-DAY 날짜 계산하기 - LocalDateTime · LocalDate · Period
LocalDateTime · LocalDate · DateTimeFormatter · Period
여행 계획을 세워서 공유하는 것이 이번 프로젝트의 주요한 로직입니다.
날짜 관련되어 기능들이 많이 필요할 것 같아서 로직을 구현 할때 사용할 수 있는 객체들을 정리하며 작업해보았습니다.
0. 적용한 로직 : DAY 날짜 계산하기
먼저 계획을 작성할때, 출발일과 종료일을 받아서 세부 일정을 입력한 날짜에 맞게 계획 할 수 있도록,
DAY 를 계산하는 로직을 추가했습니다.
※ 추후에 현제 날짜와 비교하여 노출하는 로직도 추가할 예정입니다.
1) Controller
📑 TravelController.java
@PostMapping("/category")
public String categorySave(@ModelAttribute("category") CategoryDTO categoryDTO,Model model) {
log.info(categoryDTO);
//1. d-day계산 로직
int days = categoryService.categoryDays(categoryDTO.getDateStart(), categoryDTO.getDateEnd());
int[] arr = new int[days]; // 2. 가져온 d-day 를 배열로 변경
model.addAttribute("category",categoryDTO);
model.addAttribute("days",arr); // 3. 화면에 전달
return "travel/travelMap";
}
2) Service
📑 CategoryService.java
public interface CategoryService {
boolean categorySave(CategoryDTO categoryDTO);
int categoryDays(String start, String end); // D-day 구하는 로직
}
📑 CategoryServiceImpl.java
@Override
public int categoryDays(String start, String end) {
log.info("D-day 계산");
// 1. 포맷터 생성
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
// 2. 문자열 : 날짜만 받아오고 있으므로 임의의 시간을 추가
start = start + " 00:00:00.000";
end = end + " 00:00:00.000";
// 3. 포맷터를 활용하여 문자열을 LocalDateTime 타입으로 변환
LocalDateTime startDate = LocalDateTime.parse(start, formatter);
LocalDateTime endDate = LocalDateTime.parse(end, formatter);
// 4. 출발일과 종료일을 비교
Period period = Period.between(LocalDate.from(startDate), LocalDate.from(endDate));
// 5. 일 차이가져옴 ( 출발일 - 종료일 )
//ex. 26 - 30
int days = period.getDays();
log.info(days); // ex.4
// 6. 하루 추가시켜 줌 ( ex.5 )
days = days + 1;
log.info("days : {}",days);
return days;
}
3 ) 타임리프 화면
each 를 활용해 개수만큼 <option> 태그를 생성한다.
📑navBarTravelMap.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/basic}">
<th:block th:fragment="navBarTravelMap">
<div class="navbar">
...
<h4 class="mt-4 pt-4 border-top"><i class="bi bi-calendar-check-fill"></i>
일정선택</h4>
<p class="txt_info mt-1">※ 일정을 완성하면 저장을 꼭 눌러주세요!</p>
<div class="day_select flex-column flex-lg-row mt-3" style="display: flex;">
<select class="form-select" aria-label="Default select example">
<th:block th:each="list, i : ${days}">
<option th:text="|D -${i.index + 1}|" th:value="${i.index + 1}"></option>
</th:block>
</select>
...
</th:block>
</html>
1. LocalDateTime
날짜와 시간 정보가 필요할 때 사용됩니다.
LocalDateTime 을 사용하여 시간과 날짜를 구하면, 2023-06-24T11:44:30.327959 형식으로 구해집니다.
1) 현제 날짜와 시간 구하기
LocalDateTime now = LocalDateTime.now();
2) 년 / 월 / 주 / 일 조회
LocalDateTime now = LocalDateTime.now();
System.out.println("getYear() = " + now.getYear());
System.out.println("getMonth() = " + now.getMonth());
System.out.println("getDayOfMonth() = " + now.getDayOfMonth());
System.out.println("getDayOfWeek() = " + now.getDayOfWeek());
System.out.println("getDayOfYear() = " + now.getDayOfYear());
3) 시간 / 분 / 초 조회
LocalDateTime now = LocalDateTime.now();
System.out.println("getHour() = " + now.getHour());
System.out.println("getMinute() = " + now.getMinute());
System.out.println("getSecond() = " + now.getSecond());
System.out.println("getNano() = " + now.getNano());
4) 연산하기
pluse 더하기 + / minus 빼기 - : 매개 변수 값 만큼 증가 또는 감소 합니다.
LocalDateTime now = LocalDateTime.of(2023, 6, 6, 10, 0, 0);
System.out.println("plusYears(3) = " + now.plusYears(3));
System.out.println("plusMonths(3) = " + now.plusMonths(3));
System.out.println("plusDays(3) = " + now.plusDays(3));
System.out.println("plusHours(3) = " + now.plusHours(3));
System.out.println("plusMinutes(3) = " + now.plusMinutes(3));
System.out.println("plusSeconds(3) = " + now.plusSeconds(3));
System.out.println("minusYears(3) = " + now.minusYears(3));
System.out.println("minusMonths(3) = " + now.minusMonths(3));
System.out.println("minusDays(3) = " + now.minusDays(3));
System.out.println("minusHours(3) = " + now.minusHours(3));
System.out.println("minusMinutes(3) = " + now.minusMinutes(3));
System.out.println("minusSeconds(3) = " + now.minusSeconds(3));
5) 비교하기
- isAfter() - 인자보다 미래 시간이라면 true 반환
- isBefore() - 인자보다 과거 시간이면 true 반환
- isEqual() - 인자와 같은 시간이면 true 반환
- compareTo()
- compareTo() > 0 : 인자보다 미래 시간
- compareTo() < 0 : 인자보다 과거 시간
- compareTo() == : 인자와 같은 시간
// localDateTime1: 2023년 6월 1일 0시 0분 0초
LocalDateTime localDateTime1 = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
// localDateTime2: 2023년 6월 1일 0시 0분 0초
LocalDateTime localDateTime2 = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
// localDateTime3: 2024년 6월 1일 0시 0분 0초
LocalDateTime localDateTime3 = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
6) LocalDateTime > LocalDate 로 변환
( 2023-06-24T11:44:30.327959 > 2023-06-24 )
toLocalDate() 메서드를 사용하여 객체 타입을 변환 할 수 있습니다.
LocalDateTime localDateTime = LocalDateTime.now();
localDateTime.toLocalDate()
7) LocalDateTime .pasrse( ) , 문자열 변환 확인 또는 포멧 변경
- pasrse(String)
- 한개의 매개 변수 문자열이 전달되는 경우 " yyyy-MM-dd " 형식을 사용하여 문자열을 날짜로 변환이 가능한지 분석합니다. 형식이 다른 문자열이 전달될 시 DateTimeParseException 이 발생
- pasrse(String,DataTimeFormatter)
- 문자열과 DataTimeFormatter 전달하면 포멧에서 변경한 형식으로 변환됩니다.
String str = "2023-06-05 13:47:13.248";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);
System.out.println(dateTime);
2. LocalDate
로컬 날짜 클래스로 날짜 정보만 필요할 때 사용한다. (시간은 LocalTime 클래스를 통해 따로 구할 수 있다.)
1) 현제 날짜
LocalDateTime.now()
2) LocalDate .of( ) 메서드를 사용한 날짜 개체 생성
LocalDate birthDay = LocalDate.of(1982, 02, 19);
System.out.println(birthDay );
3) 년 / 월 / 주 / 일 조회
LocalDate today = LocalDate.now();
System.out.println(today.getYear() + " : 년");
System.out.println(today.getMonth() + " : 월을 문자로 반환");
System.out.println(today.getMonthValue() + " : 월을 숫자로 반환");
System.out.println(today.getDayOfMonth() + " : 일");
System.out.println(today.getDayOfYear() + " : 객체의 일을 반환 (1~365, 윤년이면 366)";
System.out.println(today.getDayOfWeek() + " : 요일");
4) 날짜와 시간 객체의 비교
LocalDate와 LocalTime 클래스에도 객체를 비교할 수 있는 compareTo() 메소드가 오버라이딩되어 있습니다.
하지만 더욱 편리하게 날짜와 시간 객체를 서로 비교할 수 있도록 다음과 같은 메소드를 제공합니다.
- isEqual() : 날짜만 비교
- isBefore() : 두개의 날짜와 시간 객체를 비교하여 현제 객체가 명시된 객체보다 앞선 시간인지 비교
- isAfter() : 두개의 날짜와 시간 객체를 비교하여 현제 객체가 명시된 객체보다 늦은 시간인지 비교
LocalDate today = LocalDate.now(); // 2023-06-24
LocalDate otherDay = LocalDate.of(2023, 06, 29);
System.out.println(today.compareTo(otherDay)); // - 5
System.out.println(today.isBefore(otherDay)); //true
System.out.println(today.isEqual(otherDay)); //false
5) LocalDate > LocalDateTime 로 변환
( 2023-06-24 > 2023-06-24T11:44:30.327959 )
LocalDate.from(LocalDateTime.now());
3. Period
두날짜 차이를 계산할 때 사용합니다.
1) between(LocalDate date1, LocalDate date2)
메서드를 통해 두 날짜의 차이를 구할 수 있습니다. 해당 메서드는 LocalDate 타입을 받습니다.
LocalDate startDate = LocalDate.now(); //현제 날짜
LocalDate endDate = LocalDate.of(2023,06,30); // 계산하려는 날짜
Period period = Period.between(startDate, endDate);
period.getYears(); // 년도 차이
period.getMonths(); // 개월 차이
period.getDays(); // 일 차이
4. DateTimeFormatter
날짜, 시간 개채 포맷터 입니다.
이 클래스는 날짜와 시간을 구하기 위해 두가지 방법을 재공합니다.
- 형식화 : format(DateTimeFormatter formatter)
- 파싱 : parse(CharSequence text, DateTimeFormatter formatter).
1) 정의된 포맷터
Formatter | Description | Example |
ofLocalizedDate(dateStyle) | Formatter with date style from the locale | '2011-12-03' |
ofLocalizedTime(timeStyle) | Formatter with time style from the locale | '10:15:30' |
ofLocalizedDateTime(dateTimeStyle) | Formatter with a style for date and time from the locale | '3 Jun 2008 11:05:30' |
ofLocalizedDateTime(dateStyle,timeStyle) | Formatter with date and time styles from the locale | '3 Jun 2008 11:05' |
BASIC_ISO_DATE | Basic ISO date | '20111203' |
ISO_LOCAL_DATE | ISO Local Date | '2011-12-03' |
ISO_OFFSET_DATE | ISO Date with offset | '2011-12-03+01:00' |
ISO_DATE | ISO Date with or without offset | '2011-12-03+01:00'; '2011-12-03' |
ISO_LOCAL_TIME | Time without offset | '10:15:30' |
ISO_OFFSET_TIME | Time with offset | '10:15:30+01:00' |
ISO_TIME | Time with or without offset | '10:15:30+01:00'; '10:15:30' |
ISO_LOCAL_DATE_TIME | ISO Local Date and Time | '2011-12-03T10:15:30' |
ISO_OFFSET_DATE_TIME | Date Time with Offset | 2011-12-03T10:15:30+01:00' |
ISO_ZONED_DATE_TIME | Zoned Date Time | '2011-12-03T10:15:30+01:00[Europe/Paris]' |
ISO_DATE_TIME | Date and time with ZoneId | '2011-12-03T10:15:30+01:00[Europe/Paris]' |
ISO_ORDINAL_DATE | Year and day of year | '2012-337' |
ISO_WEEK_DATE | Year and Week | 2012-W48-6' |
ISO_INSTANT | Date and Time of an Instant | '2011-12-03T10:15:30Z' |
RFC_1123_DATE_TIME | RFC 1123 / RFC 822 | 'Tue, 3 Jun 2008 11:05:30 GMT' |