Java8 in Action 정리
12. 새로운 날짜와 시간 API
기존 날짜 API 문제점
- Date
- 1900년을 기준으로 하는 오프셋
- 0으로 시작하는 달 인덱스
- 가변 객체
- Calendar
- Date 와의 호환성 유지 및 개선을 위해 나온 클래스
- 오프셋 이슈는 해결했으나 0으로 시작하는 달 인덱스는 유지됨
- 가변 객체
- DateFormat
- Date 클래스에만 작동
- 스레드 세이프하지 않음.
새로운 날짜/시간 API
- LocalDate
- 시간을 제외한 날짜만 표현하는 불변객체
- LocalDate today = LocalDate.now(); // 시스템시계정보를 이용한 현재 날짜정보 획득
LocalDate date = LocalDate.of(2016, 3, 13); // 2016-03-13
int year = date.getYear(); // 2016
Month month = date.getMonth(); // MARCH
int day = date.getDayOfMonth(); // 13
DayOfWeek dow = date.getDayOfWeek(); // SUNDAY
int len = date.lengthOfMonth(); // 31 (3월의 일수)
boolean leap = date.isLeapYear(); // true (윤년)
// TemporalField를 이용한 값 조회
int year_ = date.get(ChronoField.YEAR); // 2016
int month_ = date.get(ChronoField.MONTH_OF_YEAR); // 3
int day_ = date.get(ChronoField.DAY_OF_MONTH); // 13
int dayOfYear = date.getDayOfYear(); // 73
- LocalTime
- 시간정보만을 갖는 객체
- LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20
int hour = time.getHour(); // 13
int minute = time.getMinute(); // 45
int second = time.getSecond(); // 20
LocalTime parseTime = LocalTime.parse("13:45:20");
- LocalDateTime
- LocalDate와 LocalTime을 쌍으로 갖는 복합 클래스
- LocalDate localDate = LocalDate.of(2016, Month.MARCH, 13);
LocalTime localTime = LocalTime.of(13, 45, 20);
LocalDateTime dt1 = LocalDateTime.of(2016, Month.MARCH, 13, 13, 45, 20); // 2016-03-13 13:45:20
LocalDateTime dt2 = LocalDateTime.of(localDate, localTime);
LocalDateTime dt3 = localDate.atTime(13, 45, 20);
LocalDateTime dt4 = localDate.atTime(localTime);
LocalDateTime dt5 = localTime.atDate(localDate);
LocalDate date = dt1.toLocalDate();
LocalTime time = dt1.toLocalTime();
- Instant
- 유닉스 에포크 시간(1970-01-01 00:00:00 UTC)을 기준으로 특정 지점까지의 시간을 초로 표현
- 나노초의 정밀도를 제공
- Instant now = Instant.now();
Instant i1 = Instant.ofEpochSecond(3); // 1970-01-01T00:00:03Z
Instant i2 = Instant.ofEpochSecond(3, 0); // 1970-01-01T00:00:03Z
Instant i3 = Instant.ofEpochSecond(2, 1000000000L); // 2초 이후의 1억 나노초(1초), 1970-01-01T00:00:03Z
Instant i4 = Instant.ofEpochSecond(4, -1000000000L); // 4초 이전의 1억 나노초(1초), 1970-01-01T00:00:03Z
- Duration 과 Period
- LocalDateTime은 사람이 사용하도록, Instant 는 기계가 사용하도록 만들어진 클래스로 두 인스턴스를 혼합해서 사용할 수 없음
- Duration 클래스는 초와 나노초로 시간단위를 표현
- Period는 년, 월, 일로 시간을 표현할 때 사용
- Duration과 Period 클래스가 공통 제공하는 메서드
- beetween : 두 시간 사이의 간격을 생성
- from : 시간 단위로 간격을 생성
- of : 주어진 구성 요소에서 간격 인스턴스를 생성
- parse : 문자열을 파싱해서 간격 인스턴스를 생성
- addTo : 현재값의 복사본을 생성한 후 지정된 Temporal 객체에 추가
- get : 현재 간격 정보값을 읽음
- isNegative : 간격이 음수인지 확인
- isZero : 간격이 0인지 확인
- minus : 현재값에서 주어진 시간을 뺀 복사본을 생성
- multipliedBy : 현재값에 주어진 값을 곱한 복사본을 생성
- negated : 주어진 값의 부호를 반전한 복사본을 생성
- plus : 현재값에 주어진 시간을 더한 복사본을 생성
- subtractFrom : 지정된 Temporal 객체에서 간격을 뺌
- 특정시점을 표현(조정)하는 날짜 시간 클래스의 공통 메서드
- LocalDate.of(2014, 3, 18).withYear(2011).withDayOfMonth(25).with(ChronoField.MONTH_OF_YEAR, 9);
- 2014-03-18 > 2011-03-18 > 2011-03-25 > 2011-09-25
- 공통 메서드
- from : 주어진 Temporal 객체를 이용해서 클래스의 인스턴스를 생성
- now : 시스템 시계로 Temporal 객체를 생성
- of : 주어진 구성요소에서 Temporal 객체의 인스턴스를 생성
- parse : 문자열을 파싱해서 Temporal 객체를 생성
- atOffset : 시간대 오프셋과 Temporal 객체를 합침
- atZone : 시간대와 Temporal 객체를 합침
- format : 지정된 포매터를 이용해서 Temporal 객체를 문자열로 변환 (Instant 는 지원하지 않음)
- get : Temporal 객체의 상태를 읽음
- minus : 특정 시간을 뺀 Temporal 객체의 복사본을 생성
- plus : 특정시간을 더한 Temporal 객체의 복사본을 생성
- with : 일부 상태를 바꾼 Temporal 객체의 복사본을 생성
- TemporalAdjusters
- 날짜 시간 API를 다양한 상황에서 사용할 수 있도록 한 클래스
- LocalDate.of(2014, 3, 18).with(nextOrSame(DayOfWeek.SUNDAY)).with(lastDayOfMonth());
- 2014-03-18 > 2014-03-23 (3/18 포함하여 이후로 처음 나타나는 일요일) > 2014-03-31 (3월의 마지막 일)
- TemporalAdjuster 함수형 인터페이스를 통해 커스텀 기능을 제공할 수 있다.
- TemporalAdjusters 의 팩토리 메서드
- dayOfWeekInMonth : ‘3월의 둘째 화요일’처럼 서수 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환
- firstDayOfMonth : 현재 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
- firstDayOfNextMonth : 다음 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
- firstDayOfNextYear : 내년의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
- firstDayOfYear : 올해의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
- firstInMonth : ‘3월의 첫 번째 화요일’처럼 현재 달의 첫 번째 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환
- lastDayOfMonth : 현재 달의 마지막 날짜를 반환하는 TemporalAdjuster를 반환
- next : 현재 날자 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜는 포함하지 않음)
- previous : 현재 날짜 이후로 역으로 날짜를 거슬러 올라가며 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜 미포함)
- nextOrSame : 현재 날짜 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜 포함)
- previousOrSame : 현재 날짜 이후로 역으로 날짜를 거슬러 올라가며 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환(현재날짜 포함)
- DateTimeFormatter
- 기존 DateFormat과 달리 스레드 세이프한 날짜 시간 포매터 클래스
- // 패턴을 통한 포매터 생성
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String formattedDate = LocalDate.of(2014, 3, 18).format(formatter); // 18/03/2014
LocalDate date = LocalDate.parse(formattedDate, formatter);
// 지역화된 DateTimeFormatter
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
String formattedDateItalian = LocalDate.of(2014, 3, 18).format(italianFormatter); // 18. marzo 2014
LocalDate italianDate = LocalDate.parse(formattedDateItalian, italianFormatter);
// Builder를 통한 Formatter 생성
DateTimeFormatter formatterByBuilder = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH) // d
.appendLiteral(". ").appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ").appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.GERMANY);
String formattedDateByBuilder = LocalDate.of(2014, 3, 18).format(formatterByBuilder); // 18. Marz 2014
- ZoneId, ZoneRuls, ZonedDateTime
- TimeZone을 대체하는 불변 클래스로 서머타임(DST) 같은 복잡한 사항이 자동으로 처리 가능
- ZoneRuls 클래스에는 약 40개 정도의 시간대가 있으며 ZoneId의 getRules 메서드를 이용하여 해당 시간대 규정 획득 가능
- ZonedDateTime = LocalDate + LocalTime + ZoneId 로 지정한 시간대에 상대적인 시점을 표현.
- // 기존 TimeZone 클래스에서 ZoneId 변환
ZoneId zoneId = TimeZone.getDefault().toZoneId(); // "Asia/Seoul"
// 지역ID는 "지역/도시" 형식으로 이루어지며 IANA Time Zone Database에서 제공하는 지역 집합정보 사용
ZoneId romeZone = ZoneId.of("Europe/Rome");
// 지정한 시간대에 상대적인 시점을 표현하는 ZonedDateTime
ZonedDateTime zdt1 = LocalDate.of(2014, 3, 18).atStartOfDay(romeZone); // 2014-03-18T00:00+01:00[Europe/Rome]
ZonedDateTime zdt2 = LocalDateTime.of(2014, 3, 18, 13, 45).atZone(romeZone); // 2014-03-18T13:45+01:00[Europe/Rome]
ZonedDateTime zdt3 = Instant.now().atZone(romeZone); // 2016-03-13T11:58:38.743+01:00[Europe/Rome]
// ZoneId를 활용한 LocalDateTime과 Instnat간의 변환 (LocalDateTime은 ZoneId를 상속한 ZoneOffset을 변수로 받는다)
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(Instant.now(), romeZone); // 2016-03-13T11:58:38.743
Instant instantFromDateTime = LocalDateTime.of(2014, 3, 18, 13, 45).toInstant(romeZone.getRules().getOffset(timeFromInstant)); // zoneOffset = "+01:00" (totalSeconds=3600)
// ZonedDateTime은 Instnat, LocalDateTime, LocalDate, LocalTime 모두 변환 가능
Instant instant = zdt1.toInstant(); // 2014-03-17T23:00:00Z
LocalDateTime localDateTime = zdt1.toLocalDateTime(); // 2014-03-18T00:00
- ZoneOffset, OffsetDateTime
- ZoneOffset : ZoneId를 상속한 클래스. 서머타임을 제대로 처리할 수 없으므로 권장하진 않는다
- OffsetDateTime : ISO-8601 캘린더 시스템에서 정의하는 UTC/GMT와 오프셋으로 날짜와 시간 표현
- ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");
OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(LocalDateTime.of(2014, 3, 18, 13, 45), newYorkOffset); // 2014-03-18T13:45-05:00
- 대안 캘린더 시스템
- ISO-8601 캘린더 시스템은 실질적으로 전세계에서 통용되나 자바8에서는 추가로 4개의 캘린더 시스템을 제공
- ThaiBuddhistDate :
- MinguoDate :
- JapaneseDate : 일본력 캘린더 시스템
- HijrahDate : 이슬람력 캘린더 시스템
- ChronoLocalDate
- 임의의 연대기에서 특정 날짜를 표현할 수 있는 기능을 제공하는 인터페이스
- 자바8에서 추가된 캘린더시스템은 모두 이 인터페이스를 상속하고있어 LocalDate를 이용해 4개의 클래스 인스턴스로 변환이 가능
- Chronology
- 캘린더 시스템으로 ofLocale을 이용하여 Chronology의 인스턴스 획득이 가능하다.
- JapaneseDate japaneseDate = JapaneseDate.from(LocalDate.of(2014, 3, 18)); // Japanese Heisei 26-03-18
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow(); // 2016-03-13
'development > Java' 카테고리의 다른 글
[도서] Java8 in Action - Stream (0) | 2017.04.25 |
---|---|
serialize와 serialVersionUID (0) | 2015.06.18 |