12. 새로운 날짜와 시간 API

주요내용
  • 자바 8에서 새로운 날짜와 시간 라이브러리를 제공하는 이유
  • 사람, 기계가 이해할 수 있는 날짜와 시간 표현 방법
  • 시간의 양
  • 날짜 조정, 파싱, 포매팅
  • 시간대와 캘린더 다루기

자바 8에서 새로운 날짜와 시간 라이브러리를 제공하는 이유#

기존 자바에서 2021-09-21 표현하기
Date date = new Date(117, 8, 21); // 직관적이지 않음
  • 기존 Date 클래스의 한계
    • 직관적이지 않음
    • 시간대 정보를 따로 가지지 않고, 중앙 유럽 시간(+00:00)을 사용함
  • DateFormat 이 Thread Safety 를 제공하지 못함
  • Calendar 가 1.1에 새로 나왔지만, 쉽게 에러가 나는 문제, month offset 시작점이 0 이라는 문제점이 있음
  • Date Calendar 모두 mutable class
  • 결국 Joda-Time 서드파티의 많은 기능을 java.time 으로 자바 8때 추가됨

사람, 기계가 이해할 수 있는 날짜와 시간 표현 방법#

  • 아래에 살펴볼 LocalDate LocalTime LocalDateTime Instant Duration Period 는 immutable 임

LocalDate#

날짜를 표현

LocalDate 사용 예시
LocalDate now = LocalDate.now(); // 오늘 일자
LocalDate date = LocalDate.of(2021, 7, 4); // 2021-07-04
int year = date.getYear(); // 2021
Month month = date.getMonth(); // JULY
int dayOfMonth = date.getDayOfMonth(); // 4
DayOfWeek dayOfWeek = date.getDayOfWeek(); // SUNDAY
int length = date.lengthOfMonth(); // 31 (해당 달의 일 수)
boolean leapYear = date.isLeapYear(); // false (윤년 체크)
LocalDate date = LocalDate.parse("2021-07-04"); // 일반 스트링으로 LocalDate 생성
TemporalField 이용한 get
LocalDate now = LocalDate.now();
int year = now.get(ChronoField.YEAR);
int month = now.get(ChronoField.MONTH_OF_YEAR);
int day = now.get(ChronoField.DAY_OF_MONTH);

LocalTime#

시간을 표현

LocalTime time = LocalTime.of(13, 45, 20);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
LocalTime time = LocalTime.parse("15:00:32");

LocalDateTime#

날짜, 시간을 표현

LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dt0 = LocalDateTime.parse("2021-07-04T15:00:32"); // ISO-8601
LocalDateTime dt1 = LocalDateTime.of(2021, Month.JULY, 4, 15, 0, 32);
LocalDateTime dt2 = date.atTime(time);
LocalDateTime dt3 = time.atDate(date);
LocalDate localDate = dt1.toLocalDate();
LocalTime localTime = dt1.toLocalTime();

Instant#

  • 기계적인 시간 (epoch) 표현
  • unix epoch time 1970-01-01T00:00:00Z 을 기준으로 특점 지점까지 흘러간 초
// 1970-01-01T00:00:02Z
Instant epochSecond = Instant.ofEpochSecond(2);
// 1970-01-01T00:00:02.100Z
Instant epochSecond2 = Instant.ofEpochSecond(2, 100_000_000);

시간의 양#

Duration#

  • 초 간격
  • LocalTime LocalDateTime Instant 사이 간격을 지원
Duration between = Duration.between(
LocalDateTime.parse("2021-07-05T00:00:00"),
LocalDateTime.now());
System.out.println(between); // PT4H33M22.604684S
System.out.println(between.getSeconds()); // 16402

Period#

  • 날짜 간격
Period between = Period.between(
LocalDate.parse("2021-07-05"),
LocalDate.of(2021, 6, 20));
System.out.println(between); // P-15D
System.out.println(between.getDays()); // -15

날짜 조정, 파싱, 포매팅#

with*, plus*, minus*#

위의 클래스들은 immutable 하기 때문에 withAttribute를 활용하여 일부 값이 바뀐 새로운 객체를 생성할 수 있음 (절대적 방식)

절대적 방식, withAttribute
LocalDate date = LocalDate.of(2021, 7, 5); // 2021-07-05
LocalDate date1 = date.withYear(2000); // 2000-07-05
LocalDate date2 = date1.withMonth(2); // 2000-02-05
LocalDate date3 = date2.with(ChronoField.DAY_OF_MONTH, 20); // 2000-02-20

plusAttribute, minusAttribute 도 있음 (상대적 방식)

상대적 방식, plus|minusAttribute
LocalDateTime dt = LocalDateTime.parse("2021-07-05T03:05:20");
LocalDateTime dt1 = dt.plusWeeks(1); // 2021-07-12T03:05:20
LocalDateTime dt2 = dt1.minusYears(2); // 2019-07-12T03:05:20
LocalDateTime dt3 = dt2.plus(13, ChronoUnit.MONTHS); // 2020-08-12T03:05:20

TemporalAdjusters#

  • 복잡한 날짜 조정을 미리 정의해놓음
  • with* 와 함께 쓸 수 있음
LocalDate date = LocalDate.of(2021, 7, 5);
LocalDate date1 = date.with(nextOrSame(DayOfWeek.SUNDAY)); // 2021-07-11
LocalDate date2 = date1.with(lastDayOfMonth()); // 2021-07-31
  • TemporalAdjuster는 함수형 인터페이스이므로 직접 정의도 가능함
  • .with() 에 인자로 람다를 넘길 수 있음
TemporalAdjuster 정의
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}

DateTimeFormatter#

  • 날짜, 시간을 특정 형식의 문자열로 만들 수 있음
  • Thread Safety 를 제공
  • 지역에 따른 문자열이 반영될 수 있도록, Locale 포매터를 제공
  • DateTimeFormatterBuilder 를 제공
// basic
LocalDate date = LocalDate.of(2021, 7, 5);
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE)); // 2021-07-05
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE)); // 20210705
// pattern
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date = LocalDate.of(2021, 7, 5);
String formattedDate = date.format(formatter); // 05/07/2021
LocalDate parsedFormattedDate = LocalDate.parse(formattedDate, formatter);
// Locale
DateTimeFormatter koreaFormatter =
DateTimeFormatter.ofPattern("yyyy MMMM d", Locale.KOREA);
LocalDate date = LocalDate.of(2021, 7, 5);
System.out.println(date.format(koreaFormatter)); // 2021 7월 5

시간대와 캘린더 다루기#

ZoneId ZonedDateTime#

  • java.util.TimeZone 대신 java.time.ZoneId 가 등장
  • 각 지역의 표준 시간대(Timezone) 별로 규칙이 지정되어 있음
  • ZonedDateTime = LocalDateTime + ZonedId
ZoneId koreaZone = ZoneId.of("Asia/Seoul");
LocalDateTime dateTime = LocalDateTime.of(2021, Month.JULY, 5, 3, 5, 20);
ZonedDateTime zdt = dateTime.atZone(koreaZone);
// 2021-07-05T03:05:20+09:00[Asia/Seoul]

ZoneOffset OffsetDateTime#

  • ZoneId의 서브클래스, ZoneOffset
  • UTC/GMT 기준
ZoneOffset zoneOffset = ZoneOffset.of("+09:00");
LocalDateTime dateTime = LocalDateTime.of(2021, Month.JULY, 5, 3, 5, 20);
OffsetDateTime offsetDateTime = OffsetDateTime.of(dateTime, zoneOffset);
// 2021-07-05T03:05:20+09:00

대안 캘린더#

  • ISO-8601 캘린더를 기반으로 하지만, 그 밖에 4개의 대안의 캘린더를 java 8 에서 제공
  • ThaiBuddihistDate MinguoDate JapaneseDate HijrahDate
  • 위의 대안 캘린더클래스의 LocalDate는 ChronoLocalDate 인터페이스를 구현

정리#

  • java 8에서 이전의 한계를 개선하기위해 시간, 캘린더 시스템을 새로 제공
  • java 8에 새로 제공되는 날짜, 시간 객체는 immutable 함
  • immutable 하기 때문에 새로운 인스턴스로 변형해서 객체를 만드는 팩토리(with, plus, minus 등) 들을 제공
  • 사람, 기계를 위한 두 가지 표현방식을 제공
  • 복잡한 날짜 변환기능을 미리 제공하고 있음 (TemporalAdjuster)
  • 날짜를 특정 포맷으로 바꿔 제공 가능
  • 지역 Timezone이나 Offset으로 시간대를 지정할 수 있음
  • ISO-8601 외의 4개의 대안 캘린더 제공
Last updated on