본문 바로가기

Programming/Java

[Java] 김영한의 자바 중급 1편 #6 - 날짜와 시간 라이브러리

반응형

 

김영한 선생님의 자바 중급을 듣고 정리한 내용입니다.

 

 


 

 

1. 자바 날짜와 시간 라이브러리 역사

  • JDK 1.0 : java.util.Date
  • JDK 1.1 : java.util.Calander
    • Joda-Time 오픈소스 라이브러리 도입으로 사용성, 성능, 문제가 일부 해결됨
      • 외부 라이브러리가 아니기 때문에 프로젝트에 별도로 추가해야 했음..
        • Java 8에서 java.time 패키지를 표준 API로 도입
  • JDK 8(1.8) (java.time 패키지)
    • 이전 API 문제점을 해결하면서 사용성, 성능, 스레드 안정성, 타임존 처리 등에서 크게 개선
    • 불변 객체로 설계되었으며 사이드 이펙트, 스레드 안정성 보장, 보다 직관적인 API 제공으로 날짜와 시간 연산을 단순화함
    • LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant 등의 클래스를 포함한다.
    • Joda-Time의 많은 기능을 표준 자바 플랫폼으로 가져옴

 

 

2. 자바 날짜와 시간 라이브러리 소개

 

 

2.1. LocalDate, LocalTime, LocalDateTime

  • LocalDate : 날짜만 다룰 때 사용. 예 : 2013-11-21
  • LocalTime : 시간만을 다룰 때 사용. 예 : 08:20:30.213
  • LocalDateTime : LocalDate와 LocalTime을 합한 개념. 예 : 2023-11-21T08:20:30.213

2.1.1. 사용 케이스

  • 국내 서비스만 고려할 때

2.2. ZonedDateTime, OffsetDateTime

  • ZonedDateTime : 시간대를 고려한 날짜와 시간을 표현할 때 사용한다. + 타임존이 포함된다. 예 : 2013-11-21T08:20:30.213+9.00[Asia/Seoul]
  • OffsetDateTime : 시간대를 고려한 날짜와 시간을 표현할 때 사용한다. + 타임존이 없고, UTC로부터 시간대 차이인 고정된 오프셋만 제공한다. 예 : 2013-11-21T08:20:30.213+9:00

2.3. Year, Month, YearMonth, MonthDay

자주 사용하지는 않는다.

2.4. Instant

UTC를 기준으로 하는, 시간의 한 지점을 난타낸다. 날짜의 시간을 나노초 정밀도로 표현하며, 1970년 1월 1일 0시 0분 0초(UTC)를 기준으로 경과한 시간으로 계산된다.

→ Instance 내부에는 초데이터만 들어가 있다. (나노초 포함)

2.5. Period, Duration

시간의 개념은 크게 2가지로 표현할 수 있다.

  • 특정 시점의 시간(시각)
  • 시간의 간격(기간)

Period : 두 날짜 사이의 간격을 년, 원, 인 단위로 나타낸다.

Duration : 두 시간 사이의 간격을 시, 분, 초(나노초) 단윌로 나타낸다.

 

 


 

3. 기본 날짜와 시간 : LocalDateTime

  • LocalDate : 날짜만 다룰 때 사용. 예 : 2013-11-21
  • LocalTime : 시간만을 다룰 때 사용. 예 : 08:20:30.213
  • LocalDateTime : LocalDate와 LocalTime을 합한 개념. 예 : 2023-11-21T08:20:30.213

3.1. LocalDate

 

 

3.2. LocalTime

 

 

 

3.3. LocalDateTime

 

 

  • 생성
    • now() : 현재 날짜와 시간을 기준으로 생성
    • of(…) : 특정 날짜와 시간을 기준으로 생성
  • 분리
    • 날짜와 시간을 to 메서드로 분리
      • toLocalDate : 날짜
      • toLocalTime : 시간
  • 합체
    • LocalDateTime.of(localDate, localTime) → LocalDateTime
  • plusXxx : 특정 날짜와 시간을 더함
  • isBefore() : 현재 날짜와 시간이 이전이라면 true
  • isAfter() : 현재 날짜와 시간이 이후라면 true
  • isEqual() : 시간이 같으면 true

 


 

4. 타임존 - ZonedDateTime

‘Asia/Seoul’ 같은 타임존 안에는…

  • 일광 절약 시간제에 대한 정보
  • UTC+09:00와 같은 UTC로 부터 차이인 오프셋 정보를 모두 포함

4.1. ZoneId

Java에서는 타임존을 ZoneId 클래스로 제공한다.

 

 

 

4.2. ZonedDateTime

ZoneDateTime = LocalDateTime + ZoneId

public class ZonedDateTime {
	private final LocalDateTime dateTime;
	private final ZoneOffset offset;
	private final ZoneId zone;
}

 

 

 

4.3. OffsetDateTime

LocalDateTime + ZoneOffset = OffsetDateTime

public class OffsetDateTime {
	private final LocalDateTime dateTime;
	private final ZoneOffset offset;
}

 

 

4.4. ZonedDateTime vs OffsetDateTime

 

ZonedDateTime : 구체적인 지역 시간대를 다룰때 사용하며, 일광 절약 시간을 자동으로 처리할 수 있다. (사용자 지정 시간대에 따른 시간 계산이 필요할 때 적절하다.)

OffsetDateTime : UTC와의 시간 차이만을 나타낼 때 사용하며, 지역 시간대의 복잡성을 고려하지 않는다. 시간대 변환 없이 로그를 기록하고, 데이터를 저장하고 처리할 때 적합하다.

 

 


 

5. 기계 중심의 시간 - Instant

Instant는 UTC(협정 세계시)를 기준으로 하는, 시간의 한 지점을 나타낸다.

 

Instant는 날짜와 시간을 나노초 정밀도로 표현한다. 즉 초 데이터만 들어있다. (나노초 포함)

public class Instant {
	private final long seconds;
	private final int nanos;
 ...
}

 

 

Epoch time(에포크 타임) or Unix Timestamp ?
1970년 1월 1일 00:00:00 UTC부터 현재까지 경과된 시간을 초 단위로 표현한 것이다.

즉, Unix 시간은 1970년 1월 1일 이후로 경과한 전체 초의 수로, 시간대에 영향을 받지 않는 절대적인 시간 표현 방식이다.

Instant는 바로 이 Epoch 시간을 다루는 클래스이다.

 

5.1. Instant 특징

  • 장점
    • 시간대 독립성 : 전 세계 어디서나 동일한 시점을 가리킨다.
    • 고정된 기준점 : 1970년 1월 1일 UTC를 기준으로 하기 때문에 기준이 명확하다.
  • 단점
    • 사용자 친화적이지 않다.
    • 시간대 정보 부재
  • 사용 예시
    • 전 세계적인 시간 기준이 필요할 때
    • 시간대 변환 없이 시간 계산 필요 시
    • 데이터 저장 및 교환

일반적으로 날짜와 시간을 이용할 때 LocalDateTime, ZonedDateTime 등을 사용하면 된다.
Instant는 날짜 계산이 어렵기 때문에 특별한 경우에 한정하여 사용하면 된다.

 

 

 

 


 

6. 기간, 시간의 간격 : Duration, Period

  • Period : 두 날짜 사이의 간격을 년, 월, 일 단위로 나타낸다.
    • 단위 : 년, 월, 일
    • 사용 대상 : 날짜
    • 주요 메서드 : getYears(), getMonths(), getDays()
  • Duration : 두 시간 사이의 간격을 시, 분, 초 단위로 나타낸다.
    • 단위 : 시간, 분, 초, 나노초
    • 사용 대상 : 시간
    • 주요 메서드 : toHours(), toMinutes(), getSeconds(), getNano()

6.1. Period

public class Period {
	private final int years;
	private final int months;
	private final int days;
}

 

 

 

 

6.2. Duration

두 시간 사이의 간격을 시, 분, 초(나노초)로 나타냄

public class Duration {
	private final long seconds;
	private final int nanos;
}

 

 

 

 


 

7. 날짜와 시간의 핵심 인터페이스

날짜와 시간은 특정 시점의 시간(시각)과 시간의 간격(기간)으로 나눌 수 있다.

 

 

 

[1] 특정 시점의 시간 : Temporal(TemporalAccessor 포함) 인터페이스를 구현한다.

→ 구현으로 LocalDateTime, ZonedDateTime, Instant 등이 있다.

 

[2] 시간의 간격(기간) : TemporalAmount 인터페이스를 구현한다.Period, Duration 이 있다.

 

 

TemporalAccessor 인터페이스
- 날짜와 시간을 읽기 위한 인터페이스
- 이 인터페이스는 특정 시점의 날짜와 시간 정보를 읽을 수 있는 최소한의 기능을 제공한다.

Temporal 인터페이스
- TemporalAccessor의 하위 인터페이스로, 날짜와 시간을 조작하기 위한 기능을 제공한다.
- 이를 통해 날짜와 시간을 변경하거나 조정할 수 있다.

TemporalAmount 인터페이스
- 시간의 간격을 나타내며, 날짜와 시간 객체에 적용하여 그 객체를 조정할 수 있다.
- 예를 들어, 특정 날짜에 일정 기간을 더하거나 빼는 데 사용된다.

 

 

7.1. 시간의 단위와 시간 필드

 

 

시간의 단위 : TemporalUnit(ChronoUnit)

  • TemporalUnit 인터페이스는 날짜와 시간을 측정하는 단위로 나타내며, 주로 사용되는 구현체는 java.lang.temporal.ChronoUnit 열거형으로 구현되어 있다. (열거형도 클래스이기 때문에 구현 O)
  • ChronoUnit은 다양한 시간 단위를 제공한다.

 

7.2. ChronoField

날짜 및 시간을 나타내는 데 사용되는 열거형이다.

 

열거형은 다양한 필드를 통해 날짜와 시간의 특정 부분을 나타낸다.

  • TemporalField 인터페이스는 날짜와 시간을 나타내는데 사용된다. java.time.temporal.ChronoField 열거형을 통해 구현된다.
  • ChronoField는 다양한 필드를 통해 날짜와 시간의 특정 부분을 나타낸다.

 

 

 

8. 날짜와 시간 조회하고 조작하기

8.1. 날짜와 시간 조회하기

날짜와 시간을 조회하려면 날짜와 시간 항목 중 어떤 필드를 조회할 지 선택해야 한다.

→ 이때 날짜와 시간 필드를 뜻하는 ChronoField가 사용된다.

 

 

 

TemporalAccessor.get(TemporalField field)

  • LocalDateTime을 포함한 특정 시점의 시간을 제공하는 클래스는 모두 TemporalAccessor 인터페이스를 구현한다.
  • TemporalAccessor는 특정 시점의 시간을 조회하는 기능을 제공한다.

 

편의 메서드를 제공하는 케이스

자주 사용하는 조회 필드는 간단한 편의 메서드를 제공한다.

dt.get(ChronoField.DAY_OF_MONTH) → dt.getDatOfMonth()

 

편의 메서드를 제공하지 않는 케이스

자주 사용하지 않은 특별한 기능은 편의 메서드를 제공하지 않는다.

→ get(TemporalField field)를 사용하면 된다.

 

8.2. 날짜와 시간 조작하기

어떤 시간 단위(Unit)을 변경할지 선택해야 하는데, → 이때 날짜와 시간 단위를 뜻하는 ChronoUnit이 사용된다.

 

 

Temporal plus(long amountToAdd, TemporalUnit unit)

  • LocalDateTime을 포함한 특정 시점의 시간을 제공하는 클래스는 모두 Temporal 인터페이스를 구현한다.
  • Temporal은 특정 시점의 시간을 제공하는 기능을 제공한다.
  • plus 메서드는 더하기 할 숫자와 시간의 단위(Unit)을 전달하면 된다. → 이때 TemporalUnit의 구현인 ChronoUnit을 인수로 전달하면 된다. → 불변이므로 반환값을 받아야 한다.

 

편의 메서드도 제공한다.

  • dt.plus(10, ChronoUnit.YEARS) → dt.plusYears(10)

 

8.3. 중간 정리

TemporalAccessor.get(), Temporal.plus()와 같은 인터페이스를 통해 특정 구현 클래스와 무관하게 일관성 있는 시간 조회, 조작 기능을 제공하는 것을 확인할 수 있다.

 

덕분에 LocalDateTime, LocalDate, LocalTime, ZonedDateTime, Instant와 같은 수 많은 구현에 관계없이 일관성 있는 방법으로 시간을 조회하고, 조작할 수 있다.

 

 

8.4. with() 메서드에 대해서

 

 

Temporal with(TemporalField field, long newValue)

  • 날짜와 시간의 특정 필드의 값만 변경할 수 있다.
  • 불변이기 때문에 반환값을 받아야 한다.

 

편의 메서드 또한 제공

dt.with(ChronoField.YEAR, 2020) → dt.withYear(2020)

 

TemporalAdjuster 사용 : 복잡한 날짜를 계산하고 싶다면 사용

// TemporalAdjuster 인터페이스
public interface TemporalAdjuster {
	Temporal adjustInto(Temporal temporal);
}

// TemporalAdjusters.next(DayOfWeek.FRIDAY) : 다음 금요일
// TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY) : 이번 달의 마지막 일요일

 

 

 

 

8.5. 날짜와 시간 문자열 파싱, 포맷팅

  • 포맷팅 : 날짜와 시간 데이터를 원하는 포맷의 문자열로 변경하는 것 Date → String
  • 파싱 : 문자열을 날짜와 시간 데이터로 변경하는 것 String → Date

 

 

 

8.5.1. 문자열을 날짜와 시간으로 파싱

파싱 : 문자열을 읽어서 → 날짜와 시간 객체로 만드는 것

 

(문자열의 어떤 부분이 년이고, 월이고, 일인지 각각 위치를 지정해줘야 한다.)

(DateTimeFormatter를 활용한다.)

 

 

 

 

 

 

 

 

 

 

반응형