김영한 선생님의 자바 중급을 듣고 정리한 내용입니다.
1. 자바 날짜와 시간 라이브러리 역사
- JDK 1.0 : java.util.Date
- JDK 1.1 : java.util.Calander
- Joda-Time 오픈소스 라이브러리 도입으로 사용성, 성능, 문제가 일부 해결됨
- 외부 라이브러리가 아니기 때문에 프로젝트에 별도로 추가해야 했음..
- Java 8에서 java.time 패키지를 표준 API로 도입
- 외부 라이브러리가 아니기 때문에 프로젝트에 별도로 추가해야 했음..
- Joda-Time 오픈소스 라이브러리 도입으로 사용성, 성능, 문제가 일부 해결됨
- 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 : 시간
- 날짜와 시간을 to 메서드로 분리
- 합체
- 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를 활용한다.)
'Programming > Java' 카테고리의 다른 글
[Java] 김영한의 자바 중급 1편 #8 - 중첩 클래스, 내부 클래스 - 2 (0) | 2024.10.04 |
---|---|
[Java] 김영한의 자바 중급 1편 #7 - 중첩 클래스, 내부 클래스 - 1 (0) | 2024.10.04 |
[Java] 김영한의 자바 중급 1편 #5 - ENUM (0) | 2024.09.30 |
[Java] 김영한의 자바 중급 1편 #4 - 래퍼 클래스 (0) | 2024.09.30 |
[Java] 김영한의 자바 중급 1편 #3 - String 클래스 (0) | 2024.09.10 |