본문 바로가기
JAVA

[JAVA] LocalDateTime vs. Instant (feat. ZonedDateTime, OffsetDateTime)

by 민죠미 2023. 1. 17.

java.time 패키지를 공부하는 중, Instant 클래스에 대해 처음 알게되었다.
평소 프로젝트를 할 때는 LocalDateTime 을 사용했는데, Instant를 사용해야 좋은 경우는 무엇이 있는지 궁금증이 생겼다.
살펴보던 와중 LocalDateTime 을 엔티티 칼럼으로 지정한 필드는 DB에 무슨 형식으로 저장했더라... 하고 살펴보니 냅다 문자열로 저장했거나 datetime 형식이다. datetime 형식은 SQL 표준에 맞지않고 정확도가 떨어져 공식문서에서는 권장하지 않는다고 하니 다음번에 프로젝트를 할 땐 DB쪽 형식도 제대로 찾아보고 설정해야겠다 😅ㅎ


java.time 패키지의 핵심 클래스

날짜와 시간을 하나로 표현하는 Calendar 클래스와 달리, java.time 패키지에서는 날짜와 시간을 별도의 클래스로 분리해 놓았다.

LocalDate (날짜) + LocalTime (시간) → LocalDateTime (날짜&시간)
LocalDateTime + 시간대 → ZonedDateTime

Instant 클래스

Instant 는 날짜와 시간을 초단위(정확히는 나노초)로 표현한다. 날짜와 시간을 초단위로 표현한 값을 타임스탬프(time-stamp)라고 부르는데, 이 값은 날짜와 시간을 하나의 정수로 표현할 수 있으므로 날짜와 시간의 차이를 계산하거나 순서를 비교하는데 유리해서 데이터베이스에 많이 사용된다.

인간보다는 기계에 친화적이다.

Instant 는 에포크 타임(EPOCH TIME, 1970-01-01 00:00:00 UTC)부터 경과된 시간을 나노초 단위로 표현한다.
사람에겐 불편하지만, 단일 진법으로만 다루기 때문에 계산하기 쉽다. long 형태로 Unix Timestamp를 저장하기 때문에 연산이 빠르다.

Instant now = Instant.now();
Instant now2 = Instant.ofEpochSecond(now.getEpochSecond());    //나노초는 0으로 설정됨
Instant now3 = Instant.ofEpochSecond(now.getEpochSecond(), now.getNano());

System.out.println("epochSec :" + now2.getEpochSecond());    //epochSec :1673939166
System.out.println("nano 1 :" + now.getNano());        //nano 1 :722188500
System.out.println("nano 2 :" + now2.getNano());    //nano 2 :0
System.out.println("nano 3 :" + now3.getNano());    //nano 3 :722188500
System.out.println("밀리초 :" + now.toEpochMilli());    //밀리초 :1673939166722
  • long toEpochMilli()
    • 오라클 데이터베이스의 타임스탬프(timestamp)처럼 밀리초 단위의 EPOCH TIME을 필요로 하는 경우를 위한 메서드. (1밀리초 = 1000000나노초)

Instant는 항상 UTC(+00:00)를 기준으로 하기 때문에, LocalTime과 차이가 있을 수 있다. 예를 들어 한국은 시간대가 '+09:00'이므로 InstantLocalTime 간에는 9시간의 차이가 있다. 시간대를 고려해야하는 경우 OffsetDateTime 을 사용하는 것이 더 나은 선택일 수 있다.

Instant와 Date간의 변환

Instant는 기존의 java.util.Date를 대체하기 위한 것이며, JDK1.8부터 Date에 Instant로 변환할 수 있는 새로운 메서드가 추가되었다.

static Date from(Instant instant)    // Instant -> Date
Instant toInstant()        // Date -> Instant

LocalDateTime 과 ZonedDateTime

LocalDateTime날짜 + 시간 정보를 가지고 있다. Timezone이 없으며, 현재 로컬 시간에 맞춰서 시간을 표현한다.

LocalDateTime에 시간대(time-zone)를 추가하면, ZonedDateTime이 된다. java.time 패키지에서는 ZoneId라는 클래스를 사용하여 시간대를 다룬다. ZoneId는 일광 절약시간(DST, Daylight Saving Time)을 자동적으로 처리해준다.

LocalDate에 시간 정보를 추가하는 atTime()을 쓰면 LocalDateTime을 얻을 수 있는 것처럼, LocalDateTimeatZone()으로 시간대 정보를 추가하면, ZonedDateTime을 얻을 수 있다.

cf) 사용가능한 ZoneId의 목록은 ZoneId.getAvailableZoneIds() 로 얻을 수 있다.

LocalDateTime dateTime = LocalDateTime.of(2023, 01, 17, 12, 34, 56);
ZoneId zid = ZoneId.of("Asia/Seoul");
ZonedDateTime zdt = dateTime.atZone(zid);
System.out.println(zdt);    //2023-01-17T12:34:56+09:00[Asia/Seoul]

ZonedDateTime zdt2 = LocalDate.now().atStartOfDay(zid);
System.out.println(zdt2);   //2023-01-17T00:00+09:00[Asia/Seoul]

ZoneId nyId = ZoneId.of("America/New_York");
ZonedDateTime nyTime = ZonedDateTime.now().withZoneSameInstant(nyId);
System.out.println(nyTime); //2023-01-17T02:33:13.908404-05:00[America/New_York]

ZoneOffset 과 OffsetDateTime

UTC로부터 얼마만큼 떨어져 있는지를 ZoneOffSet 으로 표현한다. ZonedDateTimeZoneId로 구역을 표현하는데, ZoneId가 아닌 ZoneOffset을 사용하는 것이 OffsetDateTime이다.

ZoneId vs. ZoneOffset

ZoneId는 일광절약시간처럼 시간대와 관련된 규칙들을 포함하고 있으나 ZoneOffset은 단지 시간대를 시간의 차이로만 구분한다. 컴퓨터에게 일광절약시간처럼 계절별로 시간을 더했다 뺏다하는 것과 같은 행위는 위험하기 그지없다. 아무런 변화 없이 일관된 시간체계를 유지하는 것이 더 안전하다.

결론

같은 지역 내의 컴퓨터간에 데이터를 주고받을 때, 전송시간을 표현하기에 LocalDateTime 이면 충분하겠지만, 서로 다른 시간대에 존재하는 컴퓨터간의 통신에는 OffsetDateTime이 필요하다.


Reference

댓글