Language/Java

Java 8 - 새로운 Date & Time 정리 (Zoda-Time) [Instant, LocalDate, Duration ...]

JaeHoney 2022. 3. 16. 23:44

Joda-Time

Java 8 이전에는 Date 관련 API들이 가지는 문제들을 해결하기 위해서, Joda-Time 이라는 라이브러리를 사용했습니다.

 

그래서 Java 8부터는 Joda-Time이 자바 표준 라이브러리로 들어왔습니다.

 

Joda-Time는 다음과 같은 클래스를 지원합니다.

  • Instance
  • LocalDate & LocalTime
  • DateTime
  • Duration & Period
  • ...

 

기존 Date 관련 API의 문제점

Java 8 이전에 사용하던 Date 관련 클래스는 Date, Calander, SimpleDateFormat 등이 있습니다. 하지만 많은 문제가 있어서, 자바 8 버전 이후 부터는 새로운 날짜 관련 API들을 제공합니다.

 

기존 클래스들의 문제입니다.

 

1. 부적절한 클래스, 메소드 이름

  • Date 클래스의 경우, TimeStamp 방식으로 동작하고 시간을 내재하고 있음. 그런데 ClassName은 Date임
  • date.getTime()을 하면 1970.1.1을 기준으로 밀리세컨을 반환함. -> 메소드의 결과를 파악하기 어려움

2. Thread safety 하지 않다.

  • Date 클래스의 경우 mutable 하기 때문에 다른 Thread에서 값을 참조하고 변경할 수 있음 -> thread safe하지 않음

3. 버그가 발생할 여지가 많다.

  • Calander 클래스의 경우 입력값의 month가 0이 1월로 처리됨 -> 그래서 Calander.SETEMBER 같은 상수를 사용해야함, 그리고 DB 데이터랑 연결하면서 서로 다르게 해석됨. 문제가 발생할 여지가 너무 많음
  • Parameter Type으로 Enum이 아니라 int를 사용하므로, month에 음수가 들어올 수도 있음 (Type safety 하지 않음)

사실 언급한 것보다 훨씬 더 많은 문제가 있지만 생략하고  넘어가겠습니다.

 

Instant 클래스

Joda-Time은 기계의 시간과 사람의 시간을 분리 합니다. 예를 들어, date.getTime()은 1970.1.1을 기준으로 밀리세컨을 반환합니다. 즉, 1520350393 이런 결과 값을 받는데, 이는 사람이 읽을 수 없는 기계식 시간입니다.

 

Instant는 특정 시간을 셋팅할 수는 없습니다. 기계식 시간을 사용할 때 편리한 클래스입니다.

 

Instasnt.now()는 현재 시간을 UTC 기준으로 반환합니다.

Instant now = Instant.now();
System.out.println(now); // 2022-03-16T13:36:12.391103800Z

Instant 클래스의 메서드를 사용하면 기계식으로 시간을 연산하기 좋습니다.

System.out.println(now.getEpochSecond()); // 1647438625
System.out.println(now.toEpochMilli()); // 1647438625212

Instant instantA = now.plusMillis(1000); // +1000ms
Instant instantB = now.minusMillis(1000); // -1000ms

 

ZoneId & ZonedDateTime 클래스

ZonedId 클래스를 사용하면 타임존을 담고 사용할 수 있습니다. ZonedDateTime으로 해당 타임존을 Set할 수 있습니다.

ZoneId zone = ZoneId.systemDefault();
System.out.println(zone); // Asia/Seoul

ZonedDateTime zonedDateTime = now.atZone(zone);
System.out.println(zonedDateTime); // 2022-03-16T22:36:12.391103800+09:00[Asia/Seoul]

 

LocalDateTime 클래스

LocalDateTime은 시스템의 타임존을 참고해서 로컬 시간을 가져옵니다.

LocalDateTime now = LocalDateTime.now();
System.out.println(now); // 2022-03-16T22:42:14.080317

특정 시간을 만들 수도 있습니다.

LocalDateTime day = LocalDateTime.of(1997, Month.JULY, 17, 0, 0, 0);

 

Period & Duration 클래스

Period와 Duration 클래스는 기간을 사용하는 클래스입니다.

 

Period를 사용하면, Human식 날짜 사이의 기간을 구할 수 있습니다.

LocalDate today = LocalDate.now();
LocalDate lastDay = LocalDate.of(2022, Month.MARCH, 31);
Period period = Period.between(today, lastDay);

System.out.println(period.getMonths()); // 0
System.out.println(period.getDays()); // 15

 

Duration을 사용하면, 기계식 날짜 사이의 기간을 구할 수 있습니다.

Instant now = Instant.now();
Instant plus = now.plus(3, ChronoUnit.MINUTES);
Duration duration = Duration.between(now, plus);

System.out.println(duration.getSeconds()); // 180

 

DateTimeFormatter 클래스

DateTimeFormatter 클래스에는 다양한 날짜/시간의 포맷이 미리 정의되어 있습니다.

-> https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#predefined

 

DateTimeFormatter를 사용해서 간편하게 LocalDateTime의 형식을 변경할 수 있습니다.

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter iso = DateTimeFormatter.ISO_DATE;
        
System.out.println(now.format(iso)); // 2022-03-16

custom으로 패턴을 정의해서 쉽게 사용할 수도 있습니다.

DateTimeFormatter customFormat = DateTimeFormatter.ofPattern("MM/dd:HH");
System.out.println(now.format(customFormat)); // 03/16:11

 

parse()를 사용하면, 다시 DateTimeFormatter를 사용해서 문자열을 날짜 형태로 파싱할 수도 있습니다.

DateTimeFormatter customFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd:HH");
System.out.println(now.format(customFormat)); // 2022/03/16:11

LocalDateTime parsedTime = LocalDateTime.parse("2022/03/16:11", customFormat);
System.out.println(parsedTime); // 2022-03-16T11:00

 

<참고> Immutable

자바 8 이전에 사용하던 Date 클래스를 생각해서, Zoda-Time 라이브러리 내부 클래스도, plus, minus 등의 메서드가 객체 내부의 상태를 변경한다고 생각하시면 안됩니다.

 

다음과 같은 코드는 아무런 변화도 일어나지 않습니다.

Instant now = Instant.now();

System.out.println(now); // 2022-03-16T14:35:00.341260100Z
now.plusMillis(1000);

System.out.println(now); // 2022-03-16T14:35:00.341260100Z
now.minusMillis(1000);

System.out.println(now); // 2022-03-16T14:35:00.341260100Z

Zoda-Time 내부 클래스는 Immutable한 불변 객체이기 때문에, 새로운 객체에 값을 할당해서 사용해야 합니다.

Instant after = now.plusSeconds(30);

 

감사합니다.