-
Date - 모던 자바 인 액션BackEnd/자바 2022. 4. 9. 23:46
자바의 날짜와 시간 API는 왜 바뀌었을까?
Date부터 LocalDate, TemporalAdjusters까지
자바에서 날짜와 시간을 다루는 건 생각보다 까다롭다. 그동안 Date, Calendar, DateFormat을 번갈아가며 써봤지만, 그만큼 많은 불편함도 감수해야 했다.
자바 8부터 새롭게 등장한 java.time 패키지는 이 모든 불편함을 한 번에 개선하려는 시도였고, 지금은 자바 날짜/시간 처리의 표준으로 자리잡았다.Date 클래스의 문제점
가장 오래된 날짜 API인 Date는 몇 가지 치명적인 단점을 갖고 있다.
new Date(117,8,21);// Thu Sep 21 00:00:00 CET 2017
이 코드만 봐도 불편한 점이 드러난다.
- 1900년을 기준으로 하는 연도 오프셋
117은 2017년을 의미한다. 0이 1900년이라 헷갈리기 쉬운 구조다. - 0부터 시작하는 월 인덱스
1월이 0, 12월이 11이다. 실수하기 딱 좋다. - 기본 시간대가 중앙유럽시간(CET)
지역화에 대한 고려가 거의 없다. - 가변 클래스(Mutable)
값이 불변이 아니라서, 여러 스레드에서 동시에 접근할 경우 문제가 발생할 수 있다.
이런 문제 때문에 자바에서는 Calendar 클래스를 대안으로 제시했다.
Calendar 클래스를 Date의 대안으로 제공
Calendar.getInstance().getTime();
Calendar.getInstance().getTime() 같은 방식으로 날짜를 생성할 수 있고, Date보다 유연한 기능들을 제공하긴 한다.
하지만…
- 여전히 0부터 시작하는 월 인덱스
- 여전히 가변 클래스
- 여전히 복잡하고 직관적이지 않은 API
- 포맷 처리를 위해 여전히 DateFormat 사용
이쯤 되면 대안이 아니라 또 다른 문제 덩어리라고 봐도 무방하다.
DateFormat과 SimpleDateFormat의 문제
DateFormat format = new SimpleDateFormat("yyyy/MM/dd");
날짜를 문자열로 변환하거나, 반대로 파싱할 때 많이 쓰는 게 SimpleDateFormat이다.
문제는 스레드 안전하지 않다는 점이다.
같은 포맷터를 두 스레드가 동시에 사용하면, 예기치 못한 결과가 발생할 수 있다.
이런 문제는 특히 웹 애플리케이션에서 심각하게 나타난다.
LocalDate의 등장 — 가볍고 강력한 날짜 객체
자바 8에서 도입된 LocalDate는 불변(immutable) 객체다.
시간 정보 없이 날짜만 표현할 수 있고, 날짜 연산과 관련된 다양한 메서드를 제공한다.불변 객체이기 때문에 새로운 날짜를 만들 땐 기존 객체를 변경하지 않고 새로운 객체를 반환한다.
getYear,getMonth 등과 같은 기본적인 기능을 물론 dayOfYear, dayOfmonth 같이 현재 며칠째인지 계산해주는 메서드와
lengthOfMonth 이 달이 며칠까지 존재하는지, isLeapYear윤달인지 평달인지 알려주는 유용한 메서드들도 있다.
날짜를 더하거나 뺄 수도 있으며, withAttribute 메서드로 기존 LocalDate 를 바꾼 버전을 간단하게 만들수 있다.
첫번째 인수로 TemporalField 를 갖는 메서드를 사용하면 좀 더 범용적으로 메서드를 활용할 수 있다.
날짜 수정도 with로 간단하게
with 메서드는 get 메서드와 쌍을 이룬다. 이들 메서드는 날짜와 시간 api의 모든 클래스가 구현하는 Temporal 인터페이스에 정의되어있다.
기존 날짜에서 일부 필드를 변경하고 싶을 땐 withXxx 메서드를 사용한다.
혹은 범용적으로 with(TemporalField, value)를 사용할 수도 있다LocalDate date = LocalDate.of(2022, 04, 9); int year = date.getYear(); int dayOfYear = date.getDayOfYear(); int dayOfMonth = date.getDayOfMonth(); DayOfWeek dayOfWeek = date.getDayOfWeek(); int len = date.lengthOfMonth(); boolean leapYear = date.isLeapYear(); LocalDate localDate1 = date.plusDays(2).minusDays(3); LocalDate localDate2 = date.plusWeeks(2); LocalDate localDate3 = date.plusYears(2); LocalDate localDate4 = date.withYear(2023); LocalDate localDate5 = date.withDayOfMonth(25); LocalDate with = date.with(ChronoField.MONTH_OF_YEAR, 2);
LocalDate date2 = LocalDate.parse("2022-04-09");
DateTimeFormatter : DateFormat 을 대체하는 클래스
LocalDate,LocalTime,Instant 클래스는 Temporal 인터페이스를 구현한다.
Temporal 인터페이스는 특정 시간을 모델링하는 객체의 값을 어떻게 읽고 조작할지 정의한다.
Duration 과 Period 는 두 시간 객체 사이의 지속시간을 만들 수 있다.
Period between = Period.between(LocalDate.of(2022, 02, 28) , LocalDate.of(2022, 04, 9));
복잡한 날짜 조정: TemporalAdjusters
다음주 일요일, 돌아오는 평일, 어떤 달의 마지막 날 등 복잡한 날짜 조정이 필요할 때,
오버로드 된 버전의 with 메서드에 좀 더 다양한 동작을 수행할 수 있도록 하는 기능을 제공
TemporalAdjusters 의 팩토리 메서드로 만들 수 있는 TemporalAdjuster 를 이용해 ㄷ다양한 기능을 제공한다.
date.with(TemporalAdjusters.firstDayOfMonth());//현재 달의 첫번째 날짜를 반환하는 TemporalAdjuster 를 반환 date.with(TemporalAdjusters.lastDayOfMonth()); date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));//현재 달의 첫번째 요일에 해당하는 날짜 반환 date.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); date.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));//현재 날짜 이후로 지정한 요일이 처음/이전으로 나타나는 날짜를 반환하는 TemporalAdjuster
커스터마이징도 가능
TemporalAdjuster를 직접 구현하면 특정 요일에 따라 다음 근무일을 구하는 등 다양한 커스텀 조정이 가능하다.
public class NextWorkingDay implements TemporalAdjuster { @Override public Temporal adjustInto(Temporal temporal) { DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); int dayToAdd=1; if(dow == DayOfWeek.FRIDAY) dayToAdd = 3; else if(dow == DayOfWeek.SATURDAY) dayToAdd = 2; else if(dow == DayOfWeek.SUNDAY) dayToAdd = 1; return temporal.plus(dayToAdd, ChronoUnit.DAYS); } }
마무리
자바 8 이전까지의 날짜/시간 API는 불편하고 직관적이지 않으며, 스레드에 취약했다.
LocalDate, DateTimeFormatter, TemporalAdjusters 같은 자바 8의 새로운 날짜 API는 불변성과 명확한 설계를 기반으로, 실용적이고 안전한 날짜 처리를 가능하게 해준다.이제 Date나 Calendar를 사용할 일은 거의 없다.
그 대신 java.time.* 패키지를 쓰는 게 기본이고, 이게 자바에서 날짜/시간을 다루는 가장 현대적이고 안전한 방식이다.반응형'BackEnd > 자바' 카테고리의 다른 글
모듈화 - 모던 자바 인 액션 (0) 2022.04.10 디폴트 메서드 - 모던 자바 인 액션 (0) 2022.04.10 Optional 에서 flatmap 과 map (0) 2022.04.07 Optional - 모던 자바 인 액션 (0) 2022.04.04 자바의 컬렉션 - 모던 자바 인 액션 (0) 2022.03.21 - 1900년을 기준으로 하는 연도 오프셋