java 1.8과 date
java 1.8 에서는 시간과 관련한 새로운 클래스를 제공합니다.
- LocalDate
- LocalTime
- Instant
- Duration
Date 클래스의 문제점
기존에 존재했던 Date java.util.date
는 개발을 어렵게 하는 여러 문제점들이 존재했습니다.
코드의 모호함
Date firstDayOf2002 = new Date(122, 0, 1);
System.out.println(firstDayOf2002); // Sat Jan 01 00:00:00 KST 2022
위 코드는 2022-01-01
의 인스턴스를 생성합니다.
년도의 경우 1900
년을 기준으로 차이를 의미하며, 월의 경우는 0
부터 시작하는 것을 확인할 수 있습니다.
출력시 보이는 결과값은 단순한 Date가 아닌 timestamp, timezone등을 함께 포함 하는 것을 확인할 수 있습니다.
값이 아닌 날짜로서의 기능 부족
Date now = new Date();
Date tomorrow = new Date(now.getYear(), now.getMonth(), now.getDate());
Date impossibleDate = new Date(122, 1, 40); //... (1)
Date tomorrow = new Date(impossibleDate.getYear(), impossibleDate.getMonth(), impossibleDate.getDate() + 1);
날짜를 계산하려면 위와 같은 형태로 코드를 작성합니다. 그러나 (1)
과 같이 잘못된 날짜라 할지라도 instance가 생성되며 자동으로 해당 날짜에서 연산한 값(1월 40일은 2월 10로)으로 반환하게 됩니다. 만약, 주말 또는 윤달등의 특수한 상황까지 고려한다면 계산은 더욱더 복잡해질 것 입니다.
불변성을 고려하지 못한 클래스 설계
Date now = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(impossibleDate);
calendar.add(Calendar.DATE, 1);
Date tomorrow = calendar.getTime();
이러한 상황을 고려하여 java1.1 에선 Calendar
클래스가 등장했습니다.
그러나 월의 offset이 0으로 시작한다는 기존의 문제점은 고쳐지지 않았으며, 가장 치명적인 부분은 thread-safe 하지 않는다는 것 입니다.
public final void setTime(Date date) {
setTimeInMillis(date.getTime());
}
public void setTimeInMillis(long millis) {
// If we don't need to recalculate the calendar field values,
// do nothing.
if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
&& (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
return;
}
time = millis;
isTimeSet = true;
areFieldsSet = false;
computeFields();
areAllFieldsSet = areFieldsSet = true;
}
위 코드는 Date클래스의 일부입니다. Date
객체를 반환하는 setTime()
함수는 setTimeInMillis()
를 호출합니다. 이때 time, isTimeSet 등은 내부변수로 접근 및 변경이 자유롭기에(syncronized, volatile 등이 선언되어 있지 않다) 멀티 스레드 환경에선 문제가 될 가능성이 매우 높습니다.
LocalDate, LocalDateTime 그리고 LocalTime
클래스명에 직관적으로 사용합니다. 날짜외 시간등의 정보가 추가로 필요하다면 LocalDateTime
을 사용하면 됩니다.
jdbc의 date 또는 datetime 과도 매핑이 정상적으로 처리되며 위에서 언급한 모든 문제를 해결하였습니다.
LocalDate now = LocalDate.now();
LocalDate tomorrow = now.plusDays(1);
LocalDate firstDayOf2002 = LocalDate.parse("2022-01-01");
firstDayOf2002.getDayOfYear();
firstDayOf2002.getDayOfMonth();
firstDayOf2002.getDayOfWeek();
firstDayOf2002.getDayOfMonth();
firstDayOf2002.isLeapYear();
LocalDate impossibleDay = LocalDate.parse("2022-01-40"); //...(1)
- (1): DateTimeParseException이 발생
TemporalAdjusters
달력 관련 계산을 언어 차원에서 제공하는 일종의 유틸 클래스 입니다.
LocalDate now = LocalDate.now();
System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));
System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));
System.out.println(now.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));
구체적인 내용 및 다른 함수들은 TemporalAdjusters에서 확인할 수 있습니다.
Instant, OffsetDateTime, ZonedDateTime
timezone의 정보가 추가된 클래스들이며 기본적으로 nano seconds까지 포함합니다.
Instant instant = Instant.now();
System.out.println(instant); // 2022-04-12T08:05:27.446589Z
OffsetDateTime offsetDateTime = OffsetDateTime.now();
System.out.println(offsetDateTime); //2022-04-12T17:05:27.473756+09:00
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime); // 2022-04-12T17:05:27.473351+09:00[Asia/Seoul]
- Instant: UTC를 기준으로 표현
- OffsetDateTime: UTC를 기준으로 offset을 표현
- ZonedDateTime: UTC를 기준으로 offset과 Timezone 까지 표현
댓글남기기