본문 바로가기
그룹 스터디 공부(IT 서적)/모던 자바 인 액션

chapter 12 새로운 날짜와 시간 API

by hanyugyeong 2023. 8. 24.
반응형
SMALL

자바 8 이전의 날짜와 시간 API의 문제들

  • Date 클래스는 직관적이지 못하며 자체적으로 시간대 정보를 알고 있지 않다
  • Date를 deprecated 시키고 등장한 Calendar 클래스 또한 쉽게 에러를 일으키는 설계 문제를 갖고 있다
  • Date와 Calendar 두 가지 클래스가 등장하면서 개발자들에게 혼란만 가중되었다.
  • 날짜와 시간을 파싱하는데 등장한 DateFormat은 Date에만 지원되었으며, 스레드에 안전하지 못했다.
  • Date와 Calendar는 모두 가변 클래스이므로 유지보수가 아주 어렵다.

12.1 LocalDate, LocalTime, Instant, Duration, Period 클래스 

java.time 패키지는 LocalDate, LocalTime, LocalDateTime, Instant, Duration, Period 등 새로운 날짜와 시간에 관련된 클래스를 제공한다.

12.1.1 LocalDate와 LocalTime 사용

LocalDate 인스턴스는 시간을 제외한 날짜를 표현하는 불변 객체다. LocalDate 객체는 어떤 시간대 정보도 포함하지 않는다. 정적 팩토리 메서드 of으로 LocalDate 인스턴스를 만들 수 있다.

LocalDate date = LocalDate.of(2017, 9, 21); // 2017-09-21
int year = date.getYear(); // 2017
Month month = date.getMonth(); //SEPTEMBER
int day = date.getDayOfMonth(); //21
DayOfWeek dow = date.getDayOfWeek(); //THURSDAY
int len = date.lengthOfMonth(); // 31(3월의 일 수)
boolean leap = date.isLeapYear(); //false(윤년이 아님)

팩토리 메서드 now는 시스템 시계의 정보를 이용해서 현재 날짜 정보를 얻는다. 

LocalDate today = LocalDate.now();

 

열거자 ChronoField는 TemporalField 인터페이스를 정의하므로 다음 코드에서 보여주는 것처럼 ChronoField의 열거자 요소를 이용해서 정보를 쉽게 얻을 수 있다. 

날짜와 시간 문자열로 LocalDate와 LocalTime의 인스턴스를 만드는 방법도 있다. 다음처럼 parse 정적 메서드를 사용할 수 있다. 

LocalDate date = LocalDate.parse("2017-09-21");
LocalTime time = LocalTime.parse("13:45:21");

parse메서드에 DateTimeFormatter를 전달할 수도 있다. DateTimeFormatter의 인스턴스는 날짜, 시간 객체의 형식을 지정한다. 문자열을 LocalDate나 LocalTime으로 파싱할 수 없을 때 parse 메서드는 DateTimeParseException을 일으킨다.

12.1.2 날짜와 시간 조합 

LocalDateTime은 LocalDate와 LocalTime을 쌍으로 갖는 복합 클래스다. LocalDateTime을 직접 만들 수도 있고 날짜와 시간을 조합할 수도 있다.

12.1.3 Instant 클래스 : 기계의 날짜와 시간 

기계 관점에서 연속된 시간에서 특정지점을 하나의 큰수로 표현하는 것이 자연스러운 시간 표현 방법이다.

java.time.Instant 클래스에서는 유닉스 에포크 시간(1970년 1월 1일 0시 0분 0초 UTC)을 기준으로 특정지점까지의 시간을 초로 표현한다. 

Instant 클래스도 사람이 확인할 수 있도록 시간을 표시해주는 정적 팩토리 메서드 now를 제공한다.

하지만 기계전용 유틸리티로 초와 나노초 정보를 포함한다. 

그러므로 사람이 읽을 수 있는 시간 정보를 제공하지 않는다.

Duration과 Period 클래스와 함께 활용 가능하다.

12.1.4 Duration과 Period 정의 

Duration 클래스의 정적 팩토리 메서드 between으로 두 시간 객체 사이의 지속 시간을 만들 수 있다.

Duration d1 = Duration.between(time1, time2);
Duration d2 = Duration.between(dateTime1, dateTime2);
Duration d3 = Duration.between(instant1, instant2);

LocalDateTime은 사람이 사용, Instant는 기계가 사용하도록 만들어진 클래스로 두 인스턴스는 혼합 ❌

Duration클래스는 초와 나노초 시간 단위를 표현하기 때문에 between메서드에 LocalDate를 전달할 수 없다.

 

Period 클래스의 팩토리 메서드 between을 이용하면 두 LocalDate의 차이를 확인할 수 있다. 

Period tenDays = Period.between(LocalDate.of(2017, 9, 11), LocalDate.of(2017, 9, 21));

[표 12-1] 은 Duration과 Period 클래스가 공통으로 제공하는 메서드를 보여준다.

12.2 날짜 조정, 파싱, 포매팅

withAttribute메소드는 새로운 객체를 반환하는 메서드를 만들 수 있다.

12.2.1 TemporalAdjusters 사용하기 

다음 주 일요일, 돌아오는 평일, 어떤 달의 마짐작 날 등 복잡한 날짜 조정이 필요할 때 사용한다.

TemporalAdjuster를 이용하면 좀 더 복잡한 날짜 조정 기능을 직관적으로 해결할 수 있다.

12.2.2 날짜와 시간 객체 출력과 파싱

포매팅과 파싱 전용패키지 java.time.format이 새로 추가 되었다.

DateTimeFormatter가 가장 중요한 클래스인데 정적 팩터리 메서드와 상수를 이용해서 쉽게 포매터를 만들 수 있다.

반대로 날짜나 시간을 표현하는 문자열을 파싱해서 날짜 객체를 다시 만들 수 있다. 날짜와 시간 API에서 특정 시점이나 간격을 표현하는 모든 클래스의 팩토리 메서드 parse를 이용해서 문자열을 날짜 객체로 만들 수 있다. 

DateTimeFormatter는 스레드에서 안전하게 사용할 수 있는 클래스다. DateTimeFormatter 클래스는 특정 패턴으로 포메터를 만들 수 있는 정적 팩토리 메서드도 제공한다. 

특정 패턴으로 포매터를 만들 수 있는 정적 팩토리 메서드도 제공한다.

DateTimeFormatterBuilder 클래스로 복합적인 포매터를 정의해서 좀 더 세부적인 포매터를 제어할 수 있다. 

즉, DateTimeFormatterBuilder 클래스로 대소문자를 구분하는 파싱, 관대한 규칙을 적용하는 파싱,패딩,포매터의 선택사항 등을 활용할 수 있다. 

12.3.1 시간대 사용하기 

표준시간이 같은 지역을 묶어서 시간대 규칙 집합을 정의한다.

ZoneRules클래스에는 40개 정도의 시간대가 있다.

ZoneId의 getRules를 이용해서 해당 시간대의 규정을 획득할 수 있다. 

ZoneId romeZone = ZoneId.of("Europe/Rome");

지역ID는 {지역}/{도시} 형식으로 이루어진다.

IANA Time Zone Database에서 제공하는 지역 집합 정보를 사용한다.

toZoneId로 기존 TimeZone객체를 ZoneId객체로 변환할 수 있다. 

ZoneId zoneId = TimeZone.getDefault().toZoneId();

ZoneId 객체를 얻은 다음에는 LocalDate, LocalDateTime,Instant를 이용해서 ZoneDateTime 인스턴스로 변환할 수 있다. ZoneDateTime은 지정한 시간대에 상대적인 시점을 표현한다. 

ZoneDateTime의 컴포넌트를 보면 LocaleDate, LocalTime, LocalDateTime,  ZoneId의 차이를 쉽게 이해할 수 있다. 

ZoneId를 이용해서 LocalDateTime을 Instant로 바꿀 수도 있다.

Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);

 

 

반응형
LIST