☕️ 김영한의 실전 자바 - 중급 1편 을 듣고 작성하는 복습 블로그 입니다.
0. 배경
프로그램을 개발하다보면 📅 날짜와 🕰️ 시간 관련된 기능을 구현해야할 일이 자주 생긴다.
예를 들어서
💰 정산 프로그램을 개발할 경우 특정 시점 사이의 정산 데이터를 가져와야 하고
👆일반적인 데이터 조회에서도 사용자가 원하는 날짜를 지정해서 검색할 수 있어야 한다.
자바에는 날짜와 시간 관련된 여러 라이브러리가 존재하는데, 이번에 라이브러리에 대해서 알아보자
1. 날짜와 시간 라이브러리가 필요한 이유
💬 "굳이 날짜와 시간 라이브러리가 필요할까 ?"
💬 "그냥 내가 하나하나 계산하면 되지 않을까 ?" 라고 생각할 수 있다.
하지만, 날짜와 시간을 그냥 계산하기엔 고려해야할 요소들이 많다.
1. 🌎 윤년
지구가 태양을 한 바퀴 도는데 평균 365.425일이 걸린다.
정확히 365일이 아니기 때문에 나머지 0.425일을 더해서 약 4년마다 하루를 추가하는 윤년을 사용한다
2. ☀️ 일광 절약 시간
썸머타임이라고도 한다.
국가나 지역에 다라 적용 여부와 시작 및 종료 날짜가 다르기 때문에 나라별로 1시간 오차가 발생할 수 있다.
3. 🕰️ 타임존
나라마다 다양한 타임존으로 나뉘어져 있다.
각 타임존은 UTC 로부터의 시간 차이로 정의된다.
이런 날짜와 시간 다양한 것들을 모두 반영하기엔 혼자 개발해서는 어렵다
→ 그렇기 때문에 잘 설계된 라이브러리를 사용해야 한다.
2. 자바 날짜와 시간 라이브러리

오라클의 자바 document 에 날짜와 시간 관련 라이브러리에 대해서 정리된 표가 있다
간단하게 설명하자면 다음과 같다
| 라이브러리 명 | 설명 | 형식 |
| LocalDate | 날짜만 표현할 때 사용 | 2013-12-21 |
| LocalTime | 시간만 표현할 때 사용 | 08:20:30.213 |
| LocalDateTime | 날짜 + 시간을 표현할 때 사용 | 2013-12-21T08:20:30.213 |
| ZonedDateTime | 시간대를 고려한 날짜와 시간을 표현 (+ 타임존 포함) | 2013-11-21T08:20:30.213+9:00[Asia/Seoul] |
| OffsetDateTime | 시간대를 고려한 날짜와 시간을 표현 (+ 타임존 미포함) | 2013-11-21T08:20:30.213+9:00 |
| Instance | UTC를 기준으로 하는 시간의 한 지점 | |
| Period | 두 날짜 사이의 간격 (년 / 월 / 일) | |
| Duration | 두 시간 사이의 간격 (시 / 분 / 초) |
3. LocalDate, LocalTime, LocalDateTime
💡 시간대를 고려하지 않고, 날짜와 시간을 계산하는 경우 사용
LocalDate, LocalTime, LocalDateTime 모두 앞에 Local 이 붙는다.
🙅♀️이는 세계 시간대를 고려하지 않는다는 의미로, 타임존이 적용되지 않는다.
LocalDate : 날짜
LocalDate now = LocalDate.now();
LocalDate ofDate = LocalDate.of(2025, 7, 7);
LocalDate ofDatePlus10 = ofDate.plusDays(10);
System.out.println("now = " + now);
System.out.println("ofDate = " + ofDate);
System.out.println("ofDatePlus10 = " + ofDatePlus10);
now = 2025-07-07
ofDate = 2025-07-07
ofDatePlus10 = 2025-07-17
- now() : 현재 날짜를 기준으로 LocalDate 를 생성한다
- of(년, 월, 일) : 특정 날짜를 기준으로 LocalDate 를 생성한다.
- plusDays(일) : 일을 더한다
LocalTime : 시간
LocalTime now = LocalTime.now();
LocalTime ofTime = LocalTime.of(12, 0, 0);
LocalTime ofTimePlusHour = ofTime.plusHours(10);
System.out.println("now = " + now);
System.out.println("ofTime = " + ofTime);
System.out.println("ofTimePlusHour = " + ofTimePlusHour);
now = 17:14:28.122669
ofTime = 12:00
ofTimePlusHour = 22:00
- now() : 현재 시간을 기준으로 LocalTime 를 생성한다
- of(시간, 분, 초) : 특정 시간을 기준으로 LocalTime 를 생성한다.
- plusHours(시간) : 시간을 더한다
LocalDateTime : 날짜 + 시간
LocalDateTime now = LocalDateTime.now();
LocalDateTime ofDateTime = LocalDateTime.of(2025, 7, 7, 12, 0, 0);
LocalDateTime ofDateTimePlusDays = ofDateTime.plusDays(10);
LocalDateTime ofDateTimePlusHours = ofDateTime.plusHours(10);
System.out.println("now = " + now);
System.out.println("ofDateTime = " + ofDateTime);
System.out.println("ofDateTimePlusDays = " + ofDateTimePlusDays);
System.out.println("ofDateTimePlusHours = " + ofDateTimePlusHours);
now = 2025-07-07T17:17:33.380426
ofDateTime = 2025-07-07T12:00
ofDateTimePlusDays = 2025-07-17T12:00
ofDateTimePlusHours = 2025-07-07T22:00
- now() : 현재 날짜, 시간을 기준으로 LocalDateTime 를 생성한다
- of(년, 월, 일, 시간, 분, 초) : 특정 날짜, 시간 을 기준으로 LocalDateTime 를 생성한다.
- plusHours(시간) : 시간을 더한다
- plusDays(일) : 일을 더한다
→ LocalDateTime 은 시간과 날짜를 모두 가지기 때문에 plusHours, plusDays 를 사용할 수 있다
4. ZonedDateTime, OffsetDateTime
💡 타임존을 고려한 날짜와 시간을 사용해야 하는 경우
ZonedDateTime은 다른 나라와의 타임존을 고려한다.
자바에서 타임존은 ZoneId 로 제공된다.
ZoneId
for (String availableZoneId : ZoneId.getAvailableZoneIds()) {
System.out.println(availableZoneId);
}
System.out.println("ZoneId.systemDefault = " + ZoneId.systemDefault());
System.out.println("ZoneId.of = " + ZoneId.of("Asia/Seoul"));
- ZoneId.systemDefault() : 시스템이 사용하는 기본 ZoneId 를 반환한다
- ZoneId.of() : 입력된 타임존에 해당하는 ZoneId 를 반환한다
- ZoneId.getAvailableZoneIds() : 사용가능한 모든 ZoneId 를 반환한다
- DST (서머 타임) 정보를 포함한다.
ZonedDateTime
public final class ZonedDateTime implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable {
private final LocalDateTime dateTime;
private final ZoneOffset offset;
private final ZoneId zone;
}
- ZonedDateTime 은 내부에 LocalDateTime, ZoneOffset, ZoneId 가 있다
- 날짜와 시간을 나타내는 LocalDateTime 에 시간대를 표현하는 타임존이 포함된다.
ZonedDateTime zdt1 = ZonedDateTime.now();
System.out.println("zdt1 = " + zdt1);
ZonedDateTime zdt2 = ZonedDateTime.of(2030, 1, 1, 13, 30, 50, 0, ZoneId.of("Asia/Seoul"));
System.out.println("zdt2 = " + zdt2);
ZonedDateTime utcZdt = zdt2.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println("utcZdt = " + utcZdt);
zdt1 = 2025-07-07T17:37:36.873016+09:00[Asia/Seoul]
zdt2 = 2030-01-01T13:30:50+09:00[Asia/Seoul]
utcZdt = 2030-01-01T04:30:50Z[UTC]
- now() : 현재 날짜와 시간으로 생성, 타임존은 시스템 시간으로 들어간다
- of() : 특정 날짜와 시간을 기준으로 생성, ZoneId 를 지정할 수 있다
- withZoneSameInstance(ZoneId) : 해당 시간의 타임존을 변경할 수 있다
OffsetDateTime
public final class OffsetDateTime implements Temporal, TemporalAdjuster, Comparable<OffsetDateTime>, Serializable {
private final LocalDateTime dateTime;
private final ZoneOffset offset;
}
- LocalDateTime 에 ZoneOffset 만 추가되어 있다
- ZoneId 가 없기 떄문에 타임존을 계산할 수 없다
ZoneOffset
UTC 기준의 고정된 시간 차이를 나타낸다 ex) "+09:00", "-05:00" 등
서머타임 (DST) 를 고려하지 않는다
OffsetDateTime nowOdt = OffsetDateTime.now();
System.out.println("nowOdt = " + nowOdt);
OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.of(2030, 1, 1, 13, 30, 50), ZoneOffset.of("+01:00"));
System.out.println("odt = " + odt);
nowOdt = 2025-07-07T17:55:54.615875+09:00
odt = 2030-01-01T13:30:50+01:00
- now() : 현재 날짜와 시간으로 생성
- of() : 특정 날짜와 시간을 기준으로 생성, Zoneoffset 을 지정할 수 있다
5. Instant
💡 시간대 변환 없이 시간 계산이 필요할 때
public final class Instant implements Temporal, TemporalAdjuster, Comparable<Instant>, Serializable {
private final long seconds;
private final int nanos;
}
- 내부적으로 초와 나노초만 가지고 있다
- 1970년 1월 일 0시 0분 0초를 기준으로 경과한 시간을 가진다
- UTC 를 기준으로 하기 때문에 시간대에 영향을 주지 않는다
- 사람이 읽고 이해하기에는 직관적이지 않다
사용
- 전세계적인 시간 기준이 필요할 때
- 시간대 변환 없이 시간 계산이 필요할 때
- 데이터 저장 및 교환
에 사용된다.
Instant now = Instant.now(); // UTC 기준
System.out.println("now = " + now);
now = 2025-07-07T08:59:50.508822Z
초가 들어있지만 출력할 때는 날짜 형식에 맞춰서 출력되는데
@Override
public String toString() {
return DateTimeFormatter.ISO_INSTANT.format(this);
}
Instant 클래스 내부에서 toString() 을 오버라이딩 하기 때문이다
6. Duration, Period
💡 두 날짜 사이의 간격이나 시간 사이의 간격을 나타낼 때
Period 는 두 날짜 사이의 간격을 년 / 월 / 일 단위로 나타낸다
// 생성
Period period = Period.ofDays(10);
System.out.println("period = " + period);
// 계산에 사용
LocalDate plusDate = LocalDate.of(2030, 1, 1).plus(period);
System.out.println("plusDate = " + plusDate);
// 기간 차이
Period between = Period.between(LocalDate.of(2023, 1, 1), LocalDate.of(2023, 4, 2));
System.out.println("기간: " + between.getMonths() + "개월 " + between.getDays() + "일");
- ofXXX() : 특정 기간을 지정해서 Period 를 생성
- plus(Period period): 특정 날짜에 기간을 더할 수 있다
- between(startDate, endDate) : 특정 날짜의 차이를 구할 수 있다
Duration 은 두 시간 사이의 간격을 시 / 분 / 초 단위로 나타낸다
// 생성
Duration duration = Duration.ofMinutes(30);
System.out.println("duration = " + duration);
// 계산
LocalTime plusTime = LocalTime.of(1, 0).plus(duration);
System.out.println("더한 시간: " + plusTime);
// 시간 차이
Duration between = Duration.between(LocalTime.of(9, 0), LocalTime.of(10, 0));
System.out.println("차이 : " + between.getSeconds() + "초");
System.out.println("근무 시간: " + between.toHours() + "시간 " + between.toMinutesPart() + "분");
- ofXXX() : 특정 시간을 지정해서 Period 생성
- plus() : LocalTime 에 Period 를 더할 수 있다
- between() : LocalTime간의 차리를 구할 수 있다
- toMinutes() : 전체 분
- toMinutesPart() : 시간을 빼고 난 후의 분
7. 날짜와 시간 핵심 인터페이스
날짜와 시간은 특정 시점과 간격으로 나눌 수 있다.
- 특정 시점 : 내 생일은 2월 7일이야
- 시간의 간격 : 내 생일까지 3일 남았어
특정 시점을 나타내는 클래스는 앞에서 살펴봤던
- LocalDateTime
- ZonedDateTime
- Instance
가 있고, 각 클래스는 Temporal 인터페이스를 구현하고, Temporal 인터페이스는 TemporalAccessor 를 상속받는다
시간의 간격을 나타내는 클래스는
- Period
- Duration
이 있고, 각 클래스는 TemporalAmount 인터페이스를 구현한다.
8. 시간의 단위와 시간 필드
시간의 단위 - ChronoUnit

- 위와 같이 시간 단위 / 날짜 단위 / 기타 단위 별로 다양한 ChronoUnit 을 가진다.
- TemporalUnit 인터페이스를 구현한다
LocalTime lt1 = LocalTime.of(1, 10, 0);
LocalTime lt2 = LocalTime.of(1, 20, 0);
long secondsBetween = ChronoUnit.SECONDS.between(lt1, lt2);
System.out.println("secondsBetween = " + secondsBetween);
- 두 날짜 / 시간 차이를 원하는 단위로 쉽게 계산할 수 있다
시간 필드- ChronoField

- 날짜와 시간의 특정 부분을 나타내는 데 사용되는 열거형
- 날짜와 시간의 각 필드 중에서 원하는 데이터를 조회할 수 있다
LocalDateTime dt = LocalDateTime.of(2030, 1, 1, 13, 30, 59);
System.out.println("YEAR = " + dt.get(ChronoField.YEAR));
System.out.println("YEAR = " + dt.getYear());
LocalDateTime changedDt1 = dt.with(ChronoField.YEAR, 2020);
LocalDateTime changedDt2 = dt.withYear(2020);
LocalDateTime with = dt.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
9. 문자열 파싱과 포맷팅
포맷팅 : 날짜와 시간 데이터를 원하는 포맷의 문자열로 변경 (Date→String)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
String formattedDate = date.format(formatter);
System.out.println("날짜와 시간 포맷팅 = " + formattedDate);
파싱 : 문자열을 날짜와 시간 데이터로 변경 (String→ Date)
String input = "2030년 01월 01일";
LocalDate parseDate = LocalDate.parse(input, formatter);
System.out.println("문자열 파싱 날짜와 시간: " + parseDate);'🌱 인프런 > ☕️ 김영한의 실전 자바 - 중급 1편' 카테고리의 다른 글
| 자바 Object 클래스 (1) | 2025.09.01 |
|---|---|
| 자바 래퍼, Class 총정리 (0) | 2025.07.19 |
| 자바 예외처리 총정리 (1) | 2025.07.11 |
| 자바 열거형 ENUM 총정리 (2) | 2025.07.10 |
| 자바 중첩 클래스와 내부 클래스 (3) | 2025.07.09 |