
java.time 패키지는 JDK 8부터 도입된 새로운 날짜와 시간 라이브러리로, 기존의 java.util.Date와 java.util.Calendar의 문제점을 해결하기 위해 설계되었습니다. 이번 포스팅에서는 java.time 패키지에서 날짜와 시간을 조작하거나 계산할 때 사용하는 다양한 단위와 필드를 표현하는 클래스들을 먼저 정리해보고, java.time 패키지 클래스들의 공통적인 기능들을 구현하는 Temporal계열의 인터페이스 및 주요 메서드들을 정리해볼 예정입니다.
주요 내용
ChronoUnit과 ChronoField는 날짜와 시간의 조작에 필수적인 단위와 필드를 정의합니다. Temporal 계열 인터페이스Temporal 계열 인터페이스는 java.time 패키지에서 날짜와 시간 객체들이 공통적으로 구현하는 기능을 정의합니다. Temporal, TemporalAccessor, TemporalAdjuster, TemporalAmount 등 다양한 인터페이스가 존재하며, 이들은 날짜와 시간의 조작, 계산, 비교 등을 위한 핵심 기능을 제공합니다. 이러한 인터페이스를 이해함으로써, java.time 패키지의 강력한 시간 처리 기능을 효과적으로 활용할 수 있습니다.java.time 패키지에는 날짜와 시간을 조작하거나 계산할 때 사용하는 다양한 단위와 필드를 표현하는 클래스가 있습니다. 그 중에서도 ChronoUnit과 ChronoField는 가장 핵심적인 역할을 합니다. ChronoUnit 클래스는 시간 단위를 정의하는 Enum으로, 연도, 월, 일, 시간, 분, 초와 같은 다양한 시간 단위를 표현합니다.java.time.temporal 패키지에 열거형으로 TemporalUnit 인터페이스를 구현하며, 날짜 및 시간의 계산, 차이 계산 등에 사용됩니다.| ChronoUnit | 설명 |
|---|---|
| NANOS | 나노초 |
| MICROS | 마이크로초 |
| MILLIS | 밀리초 |
| SECONDS | 초 |
| MINUTES | 분 |
| HOURS | 시간 |
| DAYS | 일 단위 |
| WEEKS | 주 단위 |
| MONTHS | 월 |
| YEARS | 연도 |
| DECADES | 10년 |
| CENTURIES | 100년 |
| MILLENNIA | 1000년 |
| ERAS | 시대(기원전, 서기) |
| FOREVER | 무한대 |
| Method | 반환타입 | 설명 |
|---|---|---|
| .getDuration() | Duration | 해당 단위의 길이를 Duration으로 반환합니다. |
| .isDurationEstimated() | boolean | 해당 단위의 길이가 추정치인지 여부를 반환합니다. |
| .isDateBased() | boolean | 해당 단위가 날짜 기반인지 여부를 반환합니다. |
| .isTimeBased() | boolean | 해당 단위가 시간 기반인지 여부를 반환합니다. |
| .isSupportedBy(Temporal) | boolean | 지정된 Temporal 객체가 해당 단위를 지원하는지 확인합니다. |
| .addTo(Temporal, long) | Temporal | 해당 단위의 값을 더한 새로운 Temporal 객체를 반환합니다. |
| .between(Temporal, Temporal) | long | 두 Temporal 객체 사이의 차이를 해당 단위로 계산하여 반환합니다. |
import java.time.temporal.ChronoUnit;
import java.time.LocalDate;
import java.time.temporal.Temporal;
LocalDate today = LocalDate.now();
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("Next Year: " + nextYear); // 출력: Next Year: 2025-08-29
long daysBetween = ChronoUnit.DAYS.between(today, nextYear);
System.out.println("Days Between: " + daysBetween); // 출력: Days Between: 365 (윤년이 아닌 경우)
Temporal nextMonth = ChronoUnit.MONTHS.addTo(today, 1);
System.out.println("Next Month: " + nextMonth); // 출력: Next Month: 2024-09-29
ChronoField 클래스는 날짜와 시간의 특정 필드를 정의하는 Enum으로, 연도, 월, 일, 시, 분, 초 등과 같은 필드를 표현합니다.
java.time.temporal 패키지에 열거형으로 TemporalField 인터페이스를 구현하며, 특정 날짜 또는 시간의 필드를 읽거나 조작할 때 사용됩니다.getFrom() 메서드를 통해 값을 가져올 수 있으며, with() 메서드를 통해 값을 설정할 수 있습니다. (참고로 Java에서 with는 새로운 불변 객체를 생성한다는 뜻이 있습니다.)주요 시간 및 날짜 필드
| ChronoField | 설명 | 범위 |
|---|---|---|
| ERA | 연대 (BC 혹은 AD) | 0(BC), 1(AD) |
| YEAR | 연도 | -999,999,999 ~ 999,999,999 |
| MONTH_OF_YEAR | 연중 월 | 1(Jan) ~ 12(Dec) |
| DAY_OF_YEAR | 연중 날짜 | 1 ~ 365(366) |
| DAY_OF_MONTH | 월중 날짜 | 1 ~ 28/29/30/31 |
| DAY_OF_WEEK | 요일 | 1(Mon) ~ 7(Sun) |
| HOUR_OF_DAY | (24시간) 시간 | 0 ~ 23 |
| CLOCK_HOUR_OF_DAY | (24시간) 시간 | 1 ~ 24 |
| AMPM_OF_DAY | 오전/오후 | 0(AM), 1(PM) |
| HOUR_OF_AMPM | (12시간) 시간 | 0 ~ 11 |
| CLOCK_HOUR_OF_AMPM | (12시간) 시간 | 1 ~ 12 |
| MINUTE_OF_HOUR | 분 | 0 ~ 59 |
| SECOND_OF_MINUTE | 초 | 0 ~ 59 |
| MILLI_OF_SECOND | 밀리초 | 0 ~ 999 |
| MICRO_OF_SECOND | 마이크로초 | 0 ~ 999,999 |
| NANO_OF_SECOND | 나노초 | 0 ~ 999,999,999 |
| INSTANT_SECONDS | 타임스탬프 | -2^63 ~ 2^63-1 (long) |
| OFFSET_SECONDS | UTC기준 오프셋 초 | -64800 ~ 64800 (±18시간) |
| Method | 반환타입 | 설명 |
|---|---|---|
| .isSupportedBy(TemporalAccessor) | boolean | 지정된 TemporalAccessor가 해당 필드를 지원하는지 확인합니다. |
| .getFrom(TemporalAccessor) | long | 해당 필드의 값을 가져옵니다. |
| .adjustInto(Temporal, long) | Temporal | 해당 필드의 값을 설정한 새로운 Temporal 객체를 반환합니다. |
| .range() | ValueRange | 해당 필드의 유효 범위를 반환합니다. |
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ValueRange;
LocalDate today = LocalDate.now();
int year = today.get(ChronoField.YEAR);
int month = today.get(ChronoField.MONTH_OF_YEAR);
int day = today.get(ChronoField.DAY_OF_MONTH);
System.out.println("Year: " + year); // 출력: Year: 2024
System.out.println("Month: " + month); // 출력: Month: 8
System.out.println("Day: " + day); // 출력: Day: 29
LocalDate adjustedDate = today.with(ChronoField.YEAR, 2025);
System.out.println("Adjusted Date: " + adjustedDate); // 출력: Adjusted Date: 2025-08-29
ValueRange range = ChronoField.DAY_OF_MONTH.range();
System.out.println("Day of Month Range: " + range); // 출력: Day of Month Range: 1 - 28/31
Temporal 계열 인터페이스java.time 패키지에서 날짜와 시간 작업을 효과적으로 수행하기 위해 설계된 여러 인터페이스들이 존재합니다. Temporal 계열의 인터페이스들입니다. java.time 패키지 내의 여러 클래스들이 이러한 인터페이스를 구현하고 있습니다.Temporal 계열 인터페이스의 상속 및 구현 관계TemporalAccessor --(상속)--> Temporal --(구현)--> LocalDateTime, ZonedDateTime, Instant, ...TemporalAmount --(구현)--> Period, DurationTemporalUnit --(구현)--> ChronoUnitTemporalField --(구현)--> ChronoFieldTemporalAccessor는 날짜와 시간 객체에서 특정 필드를 읽기 위한 인터페이스입니다.
TemporalAccessor는 날짜와 시간 객체를 읽기만 할 수 있으며, 조작은 불가능합니다.주요 메서드
.isSupported(TemporalField field): 특정 필드가 이 객체에서 지원되는지 확인합니다..getLong(TemporalField field): 지정된 필드의 값을 long 타입으로 반환합니다..get(TemporalField field): 지정된 필드의 값을 int 타입으로 반환합니다..range(TemporalField field): 지정된 필드의 유효 범위를 반환합니다.import java.time.LocalDate;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.ChronoField;
TemporalAccessor date = LocalDate.of(2024, 8, 29);
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
System.out.println("Year: " + year + ", Month: " + month); // 출력: Year: 2024, Month: 8
Temporal은 TemporalAccessor를 상속받아 날짜와 시간 객체를 읽을 뿐만 아니라 조작할 수 있는 기능을 추가로 제공하는 인터페이스입니다.
주요 메서드
.with(TemporalField field, long newValue): 지정된 필드의 값을 새로 설정한 객체를 반환합니다..plus(long amountToAdd, TemporalUnit unit): 지정된 단위의 값을 더한 객체를 반환합니다..minus(long amountToSubtract, TemporalUnit unit): 지정된 단위의 값을 뺀 객체를 반환합니다..until(Temporal endExclusive, TemporalUnit unit): 현재 객체와 종료 객체 사이의 차이를 지정된 단위로 계산하여 반환합니다.ChronoUnit.단위.between()과는 약간 차이가 있는데, .until은 더 일반적이고 다양한 단위로 차이를 계산할 수 있습니다.import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.ChronoUnit;
Temporal date = LocalDate.of(2024, 8, 29);
Temporal nextWeek = date.plus(1, ChronoUnit.WEEKS);
System.out.println("Next Week: " + nextWeek); // 출력: Next Week: 2024-09-05
TemporalAmount는 시간의 양을 나타내는 인터페이스로, Period와 Duration 클래스가 이를 구현합니다.
주요 메서드
.get(TemporalUnit unit): 지정된 단위의 값을 반환합니다..getUnits(): 이 시간 양에서 사용 가능한 단위의 목록을 반환합니다..addTo(Temporal temporal): 지정된 Temporal 객체에 이 시간 양을 더한 새로운 객체를 반환합니다..subtractFrom(Temporal temporal): 지정된 Temporal 객체에서 이 시간 양을 뺀 새로운 객체를 반환합니다.TemporalAmount로 따로 쓰이지는 않기때문에 이를 구현한 Period를 예시로 가져왔습니다.import java.time.LocalDate;
import java.time.Period;
LocalDate today = LocalDate.of(2024, 8, 29);
Period period = Period.ofDays(10);
LocalDate tenDaysLater = today.plus(period);
System.out.println("Ten Days Later: " + tenDaysLater); // 출력: Ten Days Later: 2024-09-08
TemporalAdjuster는 java.time 패키지에서 날짜와 시간 객체를 조정하는 데 사용되는 전략 인터페이스입니다. TemporalAdjuster는 Temporal 객체를 받아서 그 값을 조정한 후 새로운 Temporal 객체를 반환하는 함수형 인터페이스입니다..adjustInto(Temporal temporal): 주어진 Temporal 객체를 조정하고, 조정된 새로운 Temporal 객체를 반환합니다.TemporalAdjuster를 구현하여 커스텀 날짜 및 시간 조정을 할 수 있습니다. 이를 통해 더욱 복잡한 조정 로직을 적용할 수 있습니다.import java.time.LocalDate;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.time.temporal.ChronoField;
import java.time.DayOfWeek;
import java.time.temporal.TemporalAdjusters;
public class NextMonthFirstMondayAdjuster implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
// 다음 달의 첫 번째 날로 이동
Temporal firstDayOfNextMonth = temporal.with(TemporalAdjusters.firstDayOfNextMonth());
// 첫 번째 월요일로 이동
return firstDayOfNextMonth.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
}
}
// 사용 예시
LocalDate today = LocalDate.of(2024, 8, 29);
LocalDate nextMonthFirstMonday = today.with(new NextMonthFirstMondayAdjuster());
System.out.println("Next Month's First Monday: " + nextMonthFirstMonday); // 출력: Next Month's First Monday: 2024-09-02
java.time.temporal.TemporalAdjusters 클래스는 자주 사용되는 TemporalAdjuster를 정적(static) 메서드로 제공합니다.Temporal객체의 .with()메서드에서 쓰입니다. | TemporalAdjusters | 설명 | 예시 |
|---|---|---|
| .firstDayOfMonth() | 현재 날짜의 해당 월 첫 번째 일로 조정합니다. | .with(TemporalAdjusters.firstDayOfMonth()) |
| .lastDayOfMonth() | 현재 날짜의 해당 월 마지막 날로 조정합니다. | .with(TemporalAdjusters.lastDayOfMonth()) |
| .firstDayOfNextMonth() | 현재 날짜의 다음 달 첫 번째 날로 조정합니다. | .with(TemporalAdjusters.firstDayOfNextMonth()) |
| .next(DayOfWeek) | 현재 날짜 이후의 가장 가까운 특정 요일로 조정합니다. | .with(TemporalAdjusters.next(DayOfWeek.FRIDAY)) |
| .nextOrSame(DayOfWeek) | 현재 날짜 또는 이후의 가장 가까운 특정 요일로 조정합니다. | .with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY)) |
| .previous(DayOfWeek dayOfWeek) | 현재 날짜 이전의 가장 가까운 특정 요일로 조정합니다. | .with(TemporalAdjusters.previous(DayOfWeek.MONDAY)) |
| .previousOrSame(DayOfWeek dayOfWeek) | 현재 날짜 또는 이전의 가장 가까운 특정 요일로 조정합니다. | .with(TemporalAdjusters.previousOrSame(DayOfWeek.THURSDAY)) |
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;
LocalDate today = LocalDate.of(2024, 8, 29);
// 다음 월의 첫 번째 날로 조정
LocalDate firstDayOfNextMonth = today.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println("First Day of Next Month: " + firstDayOfNextMonth); // 출력: First Day of Next Month: 2024-09-01
// 다음 금요일로 조정
LocalDate nextFriday = today.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("Next Friday: " + nextFriday); // 출력: Next Friday: 2024-08-30
java.time 패키지에서 날짜와 시간을 조작하고 계산하는 데 필수적인 ChronoUnit과 ChronoField 클래스, 그리고 다양한 시간 관련 작업을 수행할 수 있게 하는 Temporal 계열 인터페이스에 대해 알아보았습니다. TemporalAdjuster와 같은 인터페이스는 날짜와 시간의 복잡한 조정 작업을 단순하고 명확하게 수행할 수 있도록 도와줍니다. TemporalAdjuster를 구현하여 프로젝트의 특정 요구 사항에 맞는 조정을 수행할 수도 있습니다.TemporalAdjusters를 사용하면 자주 발생하는 날짜 조정 작업을 쉽게 처리할 수 있습니다.