Java8 in Action 정리


12. 새로운 날짜와 시간 API


기존 날짜 API 문제점
  • Date
    • 1900년을 기준으로 하는 오프셋 
    • 0으로 시작하는 달 인덱스
    • 가변 객체
  • Calendar
    • Date 와의 호환성 유지 및 개선을 위해 나온 클래스
    • 오프셋 이슈는 해결했으나 0으로 시작하는 달 인덱스는 유지됨
    • 가변 객체
  • DateFormat
    • Date 클래스에만 작동
    • 스레드 세이프하지 않음.

새로운 날짜/시간 API
  • LocalDate
    • 시간을 제외한 날짜만 표현하는 불변객체
    • LocalDate today = LocalDate.now()// 시스템시계정보를 이용한 현재 날짜정보 획득

      LocalDate date = LocalDate.of(2016313)// 2016-03-13
      int year = date.getYear()// 2016
      Month month = date.getMonth()// MARCH
      int day = date.getDayOfMonth()// 13
      DayOfWeek dow = date.getDayOfWeek()// SUNDAY
      int len = date.lengthOfMonth()// 31 (3월의 일수)
      boolean leap = date.isLeapYear()// true (윤년)

      // TemporalField를 이용한 값 조회
      int year_ = date.get(ChronoField.YEAR)// 2016
      int month_ = date.get(ChronoField.MONTH_OF_YEAR)// 3
      int day_ = date.get(ChronoField.DAY_OF_MONTH)// 13

      int dayOfYear = date.getDayOfYear()// 73

  • LocalTime
    • 시간정보만을 갖는 객체
    • LocalTime time = LocalTime.of(134520)// 13:45:20
      int hour = time.getHour()// 13
      int minute = time.getMinute()// 45
      int second = time.getSecond()// 20

      LocalTime parseTime = LocalTime.parse("13:45:20");


  • LocalDateTime
    • LocalDate와 LocalTime을 쌍으로 갖는 복합 클래스
    • LocalDate localDate = LocalDate.of(2016Month.MARCH13);
      LocalTime localTime = LocalTime.of(134520);

      LocalDateTime dt1 = LocalDateTime.of(2016Month.MARCH13134520)// 2016-03-13 13:45:20
      LocalDateTime dt2 = LocalDateTime.of(localDatelocalTime);
      LocalDateTime dt3 = localDate.atTime(134520);
      LocalDateTime dt4 = localDate.atTime(localTime);
      LocalDateTime dt5 = localTime.atDate(localDate);

      LocalDate date = dt1.toLocalDate();
      LocalTime time = dt1.toLocalTime();


  • Instant
    • 유닉스 에포크 시간(1970-01-01 00:00:00 UTC)을 기준으로 특정 지점까지의 시간을 초로 표현
    • 나노초의 정밀도를 제공
    • Instant now = Instant.now();

      Instant i1 = Instant.ofEpochSecond(3)// 1970-01-01T00:00:03Z
      Instant i2 = Instant.ofEpochSecond(30)// 1970-01-01T00:00:03Z
      Instant i3 = Instant.ofEpochSecond(21000000000L)// 2초 이후의 1억 나노초(1초), 1970-01-01T00:00:03Z
      Instant i4 = Instant.ofEpochSecond(4-1000000000L)// 4초 이전의 1억 나노초(1초), 1970-01-01T00:00:03Z


  • Duration 과 Period
    • LocalDateTime은 사람이 사용하도록, Instant 는 기계가 사용하도록 만들어진 클래스로 두 인스턴스를 혼합해서 사용할 수 없음
    • Duration 클래스는 초와 나노초로 시간단위를 표현
    • Period는 년, 월, 일로 시간을 표현할 때 사용
    • Duration과 Period 클래스가 공통 제공하는 메서드
      • beetween : 두 시간 사이의 간격을 생성
      • from : 시간 단위로 간격을 생성
      • of : 주어진 구성 요소에서 간격 인스턴스를 생성
      • parse : 문자열을 파싱해서 간격 인스턴스를 생성
      • addTo : 현재값의 복사본을 생성한 후 지정된 Temporal 객체에 추가
      • get : 현재 간격 정보값을 읽음
      • isNegative : 간격이 음수인지 확인
      • isZero : 간격이 0인지 확인
      • minus : 현재값에서 주어진 시간을 뺀 복사본을 생성
      • multipliedBy : 현재값에 주어진 값을 곱한 복사본을 생성
      • negated : 주어진 값의 부호를 반전한 복사본을 생성
      • plus : 현재값에 주어진 시간을 더한 복사본을 생성
      • subtractFrom : 지정된 Temporal 객체에서 간격을 뺌

  • 특정시점을 표현(조정)하는 날짜 시간 클래스의 공통 메서드
    • LocalDate.of(2014, 3, 18).withYear(2011).withDayOfMonth(25).with(ChronoField.MONTH_OF_YEAR, 9);
      • 2014-03-18  >  2011-03-18  >  2011-03-25  >  2011-09-25
    • 공통 메서드
      • from : 주어진 Temporal 객체를 이용해서 클래스의 인스턴스를 생성
      • now : 시스템 시계로 Temporal 객체를 생성
      • of : 주어진 구성요소에서 Temporal 객체의 인스턴스를 생성
      • parse : 문자열을 파싱해서 Temporal 객체를 생성
      • atOffset : 시간대 오프셋과 Temporal 객체를 합침
      • atZone : 시간대와 Temporal 객체를 합침
      • format : 지정된 포매터를 이용해서 Temporal 객체를 문자열로 변환 (Instant 는 지원하지 않음)
      • get : Temporal 객체의 상태를 읽음
      • minus : 특정 시간을 뺀 Temporal 객체의 복사본을 생성
      • plus : 특정시간을 더한 Temporal 객체의 복사본을 생성
      • with : 일부 상태를 바꾼 Temporal 객체의 복사본을 생성
  • TemporalAdjusters
    • 날짜 시간 API를 다양한 상황에서 사용할 수 있도록 한 클래스
      • LocalDate.of(2014, 3, 18).with(nextOrSame(DayOfWeek.SUNDAY)).with(lastDayOfMonth());
        • 2014-03-18  >  2014-03-23 (3/18 포함하여 이후로 처음 나타나는 일요일)  >  2014-03-31 (3월의 마지막 일)
    • TemporalAdjuster 함수형 인터페이스를 통해 커스텀 기능을 제공할 수 있다.
    • TemporalAdjusters 의 팩토리 메서드
      • dayOfWeekInMonth : ‘3월의 둘째 화요일’처럼 서수 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfMonth : 현재 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfNextMonth : 다음 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfNextYear : 내년의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstDayOfYear : 올해의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환
      • firstInMonth : ‘3월의 첫 번째 화요일’처럼 현재 달의 첫 번째 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환
      • lastDayOfMonth : 현재 달의 마지막 날짜를 반환하는 TemporalAdjuster를 반환
      • next : 현재 날자 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜는 포함하지 않음)
      • previous : 현재 날짜 이후로 역으로 날짜를 거슬러 올라가며 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜 미포함)
      • nextOrSame : 현재 날짜 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환 (현재날짜 포함)
      • previousOrSame : 현재 날짜 이후로 역으로 날짜를 거슬러 올라가며 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환(현재날짜 포함)
  • DateTimeFormatter
    • 기존 DateFormat과 달리 스레드 세이프한 날짜 시간 포매터 클래스
    • // 패턴을 통한 포매터 생성
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
      String formattedDate = LocalDate.of(2014318).format(formatter)// 18/03/2014
      LocalDate date = LocalDate.parse(formattedDateformatter);

      // 지역화된 DateTimeFormatter
      DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy"Locale.ITALIAN);
      String formattedDateItalian = LocalDate.of(2014318).format(italianFormatter)// 18. marzo 2014
      LocalDate italianDate = LocalDate.parse(formattedDateItalianitalianFormatter);

      // Builder를 통한 Formatter 생성
      DateTimeFormatter formatterByBuilder = new DateTimeFormatterBuilder()
              .appendText(ChronoField.DAY_OF_MONTH// d
              .appendLiteral(". ").appendText(ChronoField.MONTH_OF_YEAR)
              .appendLiteral(" ").appendText(ChronoField.YEAR)
              .parseCaseInsensitive()
              .toFormatter(Locale.GERMANY);

      String formattedDateByBuilder = LocalDate.of(2014318).format(formatterByBuilder)// 18. Marz 2014

  • ZoneId, ZoneRuls, ZonedDateTime
    • TimeZone을 대체하는 불변 클래스로 서머타임(DST) 같은 복잡한 사항이 자동으로 처리 가능
    • ZoneRuls 클래스에는 약 40개 정도의 시간대가 있으며 ZoneId의 getRules 메서드를 이용하여 해당 시간대 규정 획득 가능
    • ZonedDateTime = LocalDate + LocalTime + ZoneId 로 지정한 시간대에 상대적인 시점을 표현.
    • // 기존 TimeZone 클래스에서 ZoneId 변환
      ZoneId zoneId = TimeZone.getDefault().toZoneId()// "Asia/Seoul"

      // 지역ID는 "지역/도시" 형식으로 이루어지며 IANA Time Zone Database에서 제공하는 지역 집합정보 사용
      ZoneId romeZone = ZoneId.of("Europe/Rome");

      // 지정한 시간대에 상대적인 시점을 표현하는 ZonedDateTime
      ZonedDateTime zdt1 = LocalDate.of(2014318).atStartOfDay(romeZone)// 2014-03-18T00:00+01:00[Europe/Rome]
      ZonedDateTime zdt2 = LocalDateTime.of(20143181345).atZone(romeZone)//  2014-03-18T13:45+01:00[Europe/Rome]
      ZonedDateTime zdt3 = Instant.now().atZone(romeZone)// 2016-03-13T11:58:38.743+01:00[Europe/Rome]

      // ZoneId를 활용한 LocalDateTime과 Instnat간의 변환 (LocalDateTime은 ZoneId를 상속한 ZoneOffset을 변수로 받는다)
      LocalDateTime timeFromInstant = LocalDateTime.ofInstant(Instant.now()romeZone)// 2016-03-13T11:58:38.743
      Instant instantFromDateTime = LocalDateTime.of(20143181345).toInstant(romeZone.getRules().getOffset(timeFromInstant))// zoneOffset = "+01:00" (totalSeconds=3600)


      // ZonedDateTime은 Instnat, LocalDateTime, LocalDate, LocalTime 모두 변환 가능
      Instant instant = zdt1.toInstant()// 2014-03-17T23:00:00Z
      LocalDateTime localDateTime = zdt1.toLocalDateTime()// 2014-03-18T00:00

  • ZoneOffset, OffsetDateTime
    • ZoneOffset : ZoneId를 상속한 클래스. 서머타임을 제대로 처리할 수 없으므로 권장하진 않는다
    • OffsetDateTime : ISO-8601 캘린더 시스템에서 정의하는  UTC/GMT와 오프셋으로 날짜와 시간 표현
    • ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");
      OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(LocalDateTime.of(20143181345)newYorkOffset)// 2014-03-18T13:45-05:00

  • 대안 캘린더 시스템
    • ISO-8601 캘린더 시스템은 실질적으로 전세계에서 통용되나 자바8에서는 추가로 4개의 캘린더 시스템을 제공
      • ThaiBuddhistDate : 
      • MinguoDate : 
      • JapaneseDate : 일본력 캘린더 시스템
      • HijrahDate : 이슬람력 캘린더 시스템
    • ChronoLocalDate
      • 임의의 연대기에서 특정 날짜를 표현할 수 있는 기능을 제공하는 인터페이스
      • 자바8에서 추가된 캘린더시스템은 모두 이 인터페이스를 상속하고있어 LocalDate를 이용해 4개의 클래스 인스턴스로 변환이 가능
    • Chronology
      • 캘린더 시스템으로 ofLocale을 이용하여 Chronology의 인스턴스 획득이 가능하다.
    • JapaneseDate japaneseDate = JapaneseDate.from(LocalDate.of(2014318))// Japanese Heisei 26-03-18
      Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
      ChronoLocalDate now = japaneseChronology.dateNow()// 2016-03-13


'development > Java' 카테고리의 다른 글

[도서] Java8 in Action - Stream  (0) 2017.04.25
serialize와 serialVersionUID  (0) 2015.06.18
Posted by dreamhopp
,