티스토리 뷰

JAVA

Date, Calendar 대신 LocalDate

H!GHR 2021. 5. 3. 23:02
Date, Calendar 클래스의 문제점

Naver D2 - JAVA의 날짜와 시간 API

예전에 본 글이지만 정리할 생각은 따로 없었는데, 사내 개발 자료 공유 차원에서 wiki에 정리하다보니 블로그 생각도 나서 간만에 글을 쓰게 되었다.

(위 글의 요약에 불과하며 직접 들어가서 보는걸 추천합니다!)

1) 불변 객체가 아니다

set 메서드가 있어서 악의적으로 값 변경시 기존 사용하는 date 값의 side effect가 있을 수 있다.

2) int 상수 필드 남용

calendar.add(Calendar.SECOND, 2);

첫번째 파라미터에 Calendar.JUNE 등 엉뚱한 상수가 들어가도 컴파일 시점에서 확인이 어렵다.

3) 헷갈리는 월 지정

calendar.set(2021, 5, 3);

1월 = 0으로 표현되어 예제의 값은 Calendar.JUNE과 같다.

4) 일관성 없는 요일 상수

어떤 메서드에서는 일요일 = 0, 어떤 메서드에서는 일요일 = 1

5) Date와 Calendar의 역할 분담

날짜 계산은 Date 클래스만으로 부족해 Calendar 객체를 생성해야하는데 생성 비용도 비싸고 번거롭다.

6) java.util.Date 하위 클래스 문제

 

LocalDate의 사용

LocalDate로 년월일 구하고 파싱하고 등등은 다른 분들이 깔끔하게 정리해주신게 많다!

그래서 나는 주로 사용하였던 localdate 예시 몇가지만 올리고 마무리해야겠다.

@Test
public void 현재날짜_스트링포멧_적용() throws Exception {
    String nowYearMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));        // yyyyMMdd로 포멧 변환시 일자까지 변환 가능
    assertThat(nowYearMonth, is("202105"));                                                     // Test Pass (테스트 현재 날짜 : 20210503)
}
 
@Test
public void 스트링날짜_localdate형식으로_파싱() throws Exception {
    String dateStr = "20210503";
     
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    LocalDate parsedDate = LocalDate.parse(dateStr, formatter);
     
    assertThat(parsedDate, is(LocalDate.now()));                                                // Test Pass (테스트 현재 날짜 : 20210503)
}
 
 
@Test
public void 만나이_계산() throws Exception {
    String birthDate = "19891111";
    String standardYm = "202105";
     
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    LocalDate parsedBirthDate = LocalDate.parse(birthDate, formatter);
    LocalDate parsedStandardYm = LocalDate.parse(standardYm + "01", formatter);
     
    // parsedStandardYm.with(TemporalAdjusters.lastDayOfMonth() : 기준월의 마지막날짜 기준으로 하기 위함 (기대값 : 20210531)
    int age = parsedBirthDate.until(parsedStandardYm.with(TemporalAdjusters.lastDayOfMonth())).getYears();
     
    assertThat(age, is(31));                                                                    // Test Pass
}
 
 
@Test
public void 기간_계산() throws Exception {
    String targetDt = "20210503";                // 목표일자            
    String standardDt = "20200503";             // 기준일자
     
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    LocalDate targetLocalDt = LocalDate.parse(targetDt, formatter);
    LocalDate standardLocalDt = LocalDate.parse(standardDt, formatter);
     
    //ChronoUnit.DAYS.between(일수 차이), ChronoUnit.YEARS.between(년수 차이) 필요에 따라 변경 가능
    int workPeriod = (int) ChronoUnit.MONTHS.between(standardLocalDt, targetLocalDt);
     
    assertThat(workPeriod, is(12));                                                // Test Pass
}
 
 
@Test
public void 날짜_영문_표시() throws Exception {
    String engMonthShortName = LocalDate.of(2021, 01, 01).getMonth().getDisplayName(TextStyle.SHORT, Locale.US);
    String engDayShortName = LocalDate.of(2021, 01, 01).getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.US);
     
    String engMonthFullName = LocalDate.of(2021, 01, 01).getMonth().getDisplayName(TextStyle.FULL, Locale.US);
    String engDayFullName = LocalDate.of(2021, 01, 01).getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.US);
     
    assertThat(engMonthShortName, is("Jan"));                                                   // Test Pass
    assertThat(engDayShortName, is("Fri"));                                                     // Test Pass
     
    assertThat(engMonthFullName, is("January"));                                                // Test Pass
    assertThat(engDayFullName, is("Friday"));                                                   // Test Pass
}
댓글