Overview
LocalDateTime은 Java 8에서 새로 추가된 API로 java.util.Date과 java.util.Calendar의 결점을 보완했습니다. (배포 순 java.util.Date< java.util.Calendar < java.time)
이번 아티클에서는 Date, Calendar의 문제점을 알아보고 java.time 패키지는 이를 어떻게 수정했는지 알아봅시다. 그리고 LocalDateTime 뿐만 아니라 LocalTime, LocalDateTime, ZonedDateTime, Period, Duration과 같은 지원되는 다른 API들도 정리해보겠습니다.
Defection of Date & Calendar
-
Thread Safety – Date, Calendar 클래스는 thread safe하지 않습니다. 따라서 동시성 이슈와 같은 디버깅이 어려운 상황을 마주해야하고 쓰레드 세이프를 보장하기 위해 추가 코드를 사용해야 합니다. 대조적으로 새로운 java.time API는 immutable하고 thread safe합니다.
-
APIs Design and Ease of Understanding – Date, Calendar는 날짜 계산 명령를 수행하는 메서드가 어색하게 설계되었습니다. 새로운 API는 ISO에 기반을 두고 있고 date, time, duration, periods와 같은 모델과도 조화를 이룹니다.
-
ZonedDate and Time – Date, Calendar에서는 Timezone을 다루기 위해 추가 코드가 필요했습니다. 반면 새로운 API에서는 Timezone은 Local과 ZonedDate API로 처리 가능합니다.
LocalDate, LocalTime and LocalDateTime
가장 빈번히 사용되는 클래스는 LocalDate, LocalTime, LocalDateTime입니다. 이름에서 알 수 있듯이, Local의 Date/Time(날짜/시간)을 나타냅니다. 중요한 점은 이 클래스들이 Timezone이 특별히 정의될 필요가 없을때 사용된다는 점입니다.
LocalDate
LocalDate는 시간 없이 ISO(yyyy-MM-dd)형태의 날짜를 나타냅니다. 즉 순수하게 연-월-일을 표현하기 위해 사용할 수 있습니다. LocalDate 객체를 만들기 위해 "of" 메서드를 쓰거나 "parse" 메서드를 쓸 수 있습니다.
LocalDate.now()
LocalDate.of(2020, 12, 06);
LocalDate.parse("2020-12-06"); // ISO String
LocalTime
LocalTime은 날짜 없이 시간만을 나타냅니다. LocalDate과 비슷하게 LocalTime도 "of", "parse" 메서드로 간단히 생성할 수 있습니다.
LocalTime.now()
LocalTime.of(15,30,45);
LocalTime.parse("15:30:45"); // ISO String
LocalDateTime
LocalDateTime은 가장 일반적으로 쓰이는 클래스로 Date와 Time의 결합으로 날짜와 시간을 나타냅니다.
LocalDateTime.now()
LocalDateTime.of(2020, 12, 6, 15, 30, 45);
LocalDateTime.parse("2020-12-06T15:30:45"); // ISO String
ZonedDateTime, OffsetDateTime API
자바 8에서는 ZoneDateTime을 제공합니다. Local 지역이 아닌 특정 타지역의 날짜와 시간을 다루어야 할때 ZoneDateTime을 사용할 수 있습니다. 그리고 이런 특정지역의 날짜와 시간을 타임존(TimeZone)이라 합니다. 세상에는 약 40개의 Zone이 존재하며 Zone을 식별하기 위한 ZoneId가 있습니다.
ZoneId.of("Asia/Seoul"); // 서울 ZoneId
ZoneId.getAvailableZoneIds(); // 가능한 모든 ZoneId 보기
ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Asia/Seoul"));
ZonedDateTime.parse("2020-12-06T22:15:30+01:00[Asia/Seoul]");
OffsetDateTime API는 타임존을 다루는 또 다른 방법입니다. ZoneOffset은 Zone간의 시차를 의미하며 UTC 기준으로 고정된 시간 차이를 양수나 음수로 나타냅니다. 예를 들어 서울의 경우 타임존 코드는 Asia/Seoul이고 london과 시차는 +0900 입니다. ZoneId는 이 시간 차이를 단순히 식별자 코드로 나타내고 ZoneId를 이용하든 ZoneOffset을 이용하든 시간을 나타내는 표현이 다를뿐입니다.
ZoneOffset.of("+02:00");
OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.of("+02:00"));
withZoneSameInstant()
ZoneDateTime에는 withZoneSameInstant() 메소드가 있습니다. withZoneSameInsant를 사용하면 ZonedDateTime에 다른 타임존이나 시차를 적용할 수 있습니다.
ZonedDateTime seoul = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Asia/Seoul"));
// 2020-12-06T20:30+09:00[Asia/Seoul]
ZonedDateTime utc = seoul.withZoneSameInstant(ZoneOffset.UTC);
// 2020-12-06T11:30Z (KST - 9 = UTC)
ZonedDateTime london = seoul.withZoneSameInstant(ZoneId.of("Europe/London"));
// 2020-12-06T11:30+00:00[Europe/London]
ZonedDateTime london = seoul.withZoneSameInstant(ZoneOffset.of("+0000"));
// 2020-12-06T11:30+00:00
ZonedDateTime newYork = seoul.withZoneSameInstant(ZoneId.of("America/New_York"));
// 2020-12-06T10:30-01:00[America/New_York] (KST - 10 = UTC - 1)
ZonedDateTime newYork = seoul.withZoneSameInstant(ZoneOffset.of("-0100"));
// 2020-12-06T10:30-01:00