[Ch12] 새로운 날짜와 시간 API

java.util.Date 클래스로 날짜와 시간 관련 기능을 쓸 수 있었다. 하지만 Date 클래스는 불변 클래스가 아니라서 안전하지 않다 (이펙티브 자바 CH10 참고) 또한 DateFormat도 불변 클래스가 아니라 스레드 safe 하지 않다 실제로 마주쳤던 에러

따라서 java8 에는 java.time 패키지에 Joda-Time 의 많은 기능으로 추가해서 이와 같은 문제를 해결했다.

이 장에서는 새로운 날짜와 시간 API가 제공하는 새로운 기능을 살펴본다.

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

  • LocalDate : 시간을 제외한 날짜를 표현하는 불변 객체
    LocalDate date = LocalDate.of(2021, 10, 25);
    System.out.println(date); // 2021-10-25
    
    int year = date.getYear();
    Month mongth = date.getMonth();
    int day = date.getDayOfMonth();
    
    • get 메서드에 TemporalField 를 전달해서 정보를 얻을 수도 있다.
      // LocalDate 의 get 메서드
      @Override  // override for Javadoc and performance
      public int get(TemporalField field) {
          if (field instanceof ChronoField) {
              return get0(field);
          }
          return ChronoLocalDate.super.get(field);
      }
      
      // 사용법
      int year = date.get(ChronoField.YEAR);
      int month = date.get(ChronoField.MONTH_OF_YEAR);
      
  • LocalTime : 날짜를 제외한 시간 을 표현하는 불변 객체
  • LocalDateTime : 날짜와 시간을 모두 표현할 수 있다.
  • Instant : 기계의 날짜 시간
    • 나노초(10억분의 1초)의 정밀도를 제공한다
      Instant.ofEpochSecond(3);
      Instant.ofEpochSecond(2, 1_000_000_000); // 2초 이후의 1억 나노초
      
    • Instant 는 기계 전용의 유틸리티이기 때문에, 사람이 읽을 수 있는 시간정보를 제공하지 않는다.
  • Duration : 두 시간 객체 사이의 지속시간. 초와 나노초로 시간단위를 표현하므로 between 메서드에 LocalDate 를 전달할 수 없다 (Period 클래스를 사용한다.)
    LocalTime time1 = LocalTime.of(12, 00, 00);
    LocalTime time2 = LocalTime.of(12, 30, 00);
    Duration duration = Duration.between(time1, time2);
    System.out.println(duration); // PT30M
    System.out.println(duration.getSeconds()); // 1,800
    

지금까지 살펴본 TemporalField 인터페이스를 구현한 객체들은 모두 불변이다. 불변 클래스는 함수형 프로그래밍 그리고 스레드 안전성과 도메인 모델의 일관성을 유지하는 데 좋은 특징이다. 하지만 날짜/시간에 연산을 해야할 경우 객체의 값을 바꿔야 할 수도 있다. 자바8의 새로운 API 는 그런 기능도 제공한다.

날짜 조정, 파싱, 포매팅

withAttribute 메서드로 기존의 LocalDate 를 바꾼 버전을 간단하게 만들 수 있다. 바뀐 속성을 포함하는 새로운 객체를 반환하므로 여전히 불변 클래스이다.

public static void changeLocalDateTime() {
    LocalDate date = LocalDate.of(2021, 10, 25);
    LocalDate date1 = date.withYear(2020); // 2020-10-25
    LocalDate date2 = date.withDayOfMonth(2); // 2021-10-02
    LocalDate date3 = date.with(ChronoField.YEAR, 2019); // 2019-10-25
}

plusWeeks, minusYears 등 명시적으로 연산을 하는 방법도 있다. 이들 메서드를 살펴보면 return new LocalDate 로 항상 새로운 객체를 반환해서, 기존의 객체는 수정/변경 하지 않는 것을 확인할 수 있다.

TemporalAdjusters 사용하기

조금 더 복잡한 날짜 조정이 필요하다면 with 메서드에 TemporalAdjusters 를 전달하는 방법으로 문제를 해결할 수 있다.

댓글남기기