본문으로 건너뛰기

10.2 날짜 연산과 Period, Duration

날짜를 가져오는 법을 배웠으니 이제 날짜끼리 더하거나 빼거나, 두 날짜 사이의 차이를 계산하는 법 을 알아봅니다. java.time 패키지에는 이를 위한 강력한 도구들이 마련되어 있습니다.


1. 날짜/시간 더하고 빼기 (plus, minus 메서드)

LocalDate, LocalDateTime 객체는 불변(Immutable) 이므로, 날짜를 바꿀 때마다 새 객체를 반환 합니다. this.date = date + 5일 같은 개념이 아니라는 것을 기억하세요!

import java.time.LocalDate;
import java.time.LocalDateTime;

LocalDate today = LocalDate.of(2026, 3, 23);

// 더하기
LocalDate after30Days = today.plusDays(30);
LocalDate after2Months = today.plusMonths(2);
LocalDate nextYear = today.plusYears(1);
LocalDate after3Weeks = today.plusWeeks(3);

System.out.println("30일 뒤: " + after30Days); // 2026-04-22
System.out.println("2개월 뒤: " + after2Months); // 2026-05-23
System.out.println("1년 뒤: " + nextYear); // 2027-03-23
System.out.println("3주 뒤: " + after3Weeks); // 2026-04-13

// 빼기
LocalDate before7Days = today.minusDays(7);
LocalDate before1Month = today.minusMonths(1);
LocalDate twoYearsAgo = today.minusYears(2);

System.out.println("7일 전: " + before7Days); // 2026-03-16
System.out.println("1개월 전: " + before1Month); // 2026-02-23
System.out.println("2년 전: " + twoYearsAgo); // 2024-03-23

// LocalDateTime에서 시간 단위 조작
LocalDateTime now = LocalDateTime.of(2026, 3, 23, 14, 30, 0);
System.out.println("3시간 뒤: " + now.plusHours(3)); // ...T17:30
System.out.println("45분 뒤: " + now.plusMinutes(45)); // ...T15:15
System.out.println("30초 전: " + now.minusSeconds(30)); // ...T14:29:30

주요 plus/minus 메서드:

메서드대상설명
plusDays(n) / minusDays(n)LocalDate, LocalDateTime일 단위
plusWeeks(n) / minusWeeks(n)LocalDate, LocalDateTime주 단위
plusMonths(n) / minusMonths(n)LocalDate, LocalDateTime월 단위
plusYears(n) / minusYears(n)LocalDate, LocalDateTime연 단위
plusHours(n) / minusHours(n)LocalDateTime, LocalTime시 단위
plusMinutes(n) / minusMinutes(n)LocalDateTime, LocalTime분 단위
plusSeconds(n) / minusSeconds(n)LocalDateTime, LocalTime초 단위

2. 두 날짜 사이의 간격: Period

두 날짜 사이에 "몇 년, 몇 달, 며칠이 차이나는지"를 계산할 때는 Period 클래스를 씁니다. 날짜(Date) 간격 전용입니다.

Period.between()

import java.time.LocalDate;
import java.time.Period;

LocalDate birthDate = LocalDate.of(1995, 6, 15);
LocalDate today = LocalDate.of(2026, 3, 23);

// Period.between(시작날짜, 끝날짜)
Period p = Period.between(birthDate, today);

System.out.println("전체 기간: " + p); // P30Y9M8D (ISO 8601)
System.out.println("년수: " + p.getYears() + "년"); // 30년
System.out.println("개월수: " + p.getMonths() + "개월"); // 9개월
System.out.println("일수: " + p.getDays() + "일"); // 8일
Period의 getYears/getMonths/getDays

각 메서드는 전체 기간의 해당 부분 만 반환합니다. 예를 들어 30년 9개월 8일이면:

  • getYears() → 30 (30년)
  • getMonths() → 9 (9개월, 30년을 빼고 남은 개월)
  • getDays() → 8 (8일, 30년 9개월을 빼고 남은 일수)

"총 몇 개월"을 구하려면 ChronoUnit.MONTHS.between()을 사용하세요.

Period 직접 생성

import java.time.Period;

// 직접 생성
Period threeMonths = Period.ofMonths(3);
Period oneYear = Period.ofYears(1);
Period twoWeeks = Period.ofWeeks(2);
Period custom = Period.of(1, 6, 15); // 1년 6개월 15일

LocalDate date = LocalDate.of(2026, 1, 1);
System.out.println(date.plus(threeMonths)); // 2026-04-01
System.out.println(date.plus(custom)); // 2027-07-16

3. 두 시간 사이의 간격: Duration

두 시각 사이에 "몇 초, 몇 분이 차이나는지"를 초(second) 또는 나노초 단위로 계산할 때는 Duration 클래스를 씁니다. 시간(Time) 간격 전용입니다.

Duration.between()

import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Duration;

// LocalTime 간격
LocalTime start = LocalTime.of(9, 0, 0); // 오전 09:00:00
LocalTime end = LocalTime.of(18, 30, 45); // 오후 06:30:45

Duration d = Duration.between(start, end);

System.out.println("전체 Duration: " + d); // PT9H30M45S
System.out.println("총 시간: " + d.toHours() + "시간"); // 9시간
System.out.println("총 분: " + d.toMinutes() + "분"); // 570분
System.out.println("총 초: " + d.getSeconds() + "초"); // 34245초

// 시/분/초 분리 출력
long hours = d.toHours();
long minutes = d.toMinutesPart(); // Java 9+: 시간을 제외한 분만
long seconds = d.toSecondsPart(); // Java 9+: 분을 제외한 초만
System.out.printf("근무 시간: %d시간 %d분 %d초%n", hours, minutes, seconds);
// 9시간 30분 45초

// LocalDateTime 간격
LocalDateTime from = LocalDateTime.of(2026, 3, 23, 10, 0);
LocalDateTime to = LocalDateTime.of(2026, 3, 25, 14, 30);

Duration between = Duration.between(from, to);
System.out.println("총 시간 차이: " + between.toHours() + "시간"); // 52시간
System.out.println("총 분 차이: " + between.toMinutes() + "분"); // 3150분

Duration 직접 생성

import java.time.Duration;

Duration twoHours = Duration.ofHours(2);
Duration thirtyMins = Duration.ofMinutes(30);
Duration fiveSeconds = Duration.ofSeconds(5);
Duration halfDay = Duration.ofHours(12);

LocalTime start = LocalTime.of(8, 0);
System.out.println(start.plus(twoHours)); // 10:00
System.out.println(start.plus(thirtyMins)); // 08:30

4. ChronoUnit으로 특정 단위 차이 계산

PeriodDuration으로 구하기 어려운 "두 날짜 사이에 총 며칠?", "두 시간 사이에 총 몇 분?" 같은 단일 단위 차이 계산에는 ChronoUnit이 더 편리합니다.

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

LocalDate date1 = LocalDate.of(2026, 1, 1);
LocalDate date2 = LocalDate.of(2026, 12, 31);

// 총 일수 차이
long totalDays = ChronoUnit.DAYS.between(date1, date2);
System.out.println("총 일수: " + totalDays + "일"); // 364일

// 총 개월 수 차이
long totalMonths = ChronoUnit.MONTHS.between(date1, date2);
System.out.println("총 개월: " + totalMonths + "개월"); // 11개월

// 총 연수 차이
LocalDate birthDate = LocalDate.of(1995, 6, 15);
LocalDate today = LocalDate.of(2026, 3, 23);
long totalYears = ChronoUnit.YEARS.between(birthDate, today);
System.out.println("만 나이: " + totalYears + "세"); // 30세

// 주 단위
long totalWeeks = ChronoUnit.WEEKS.between(date1, date2);
System.out.println("총 주수: " + totalWeeks + "주"); // 52주

// 시간/분/초 단위 (LocalDateTime 사용)
LocalDateTime start = LocalDateTime.of(2026, 3, 23, 9, 0);
LocalDateTime end = LocalDateTime.of(2026, 3, 23, 17, 30);
long totalHours = ChronoUnit.HOURS.between(start, end);
long totalMinutes = ChronoUnit.MINUTES.between(start, end);
System.out.println("총 시간: " + totalHours + "시간"); // 8시간
System.out.println("총 분: " + totalMinutes + "분"); // 480분

5. TemporalAdjusters: 다음 월요일, 이번 달 마지막 날

TemporalAdjusters는 "다음 월요일", "이번 달 첫 번째 금요일" 같은 복잡한 날짜 조정을 간단하게 처리해 줍니다.

import java.time.LocalDate;
import java.time.DayOfWeek;
import java.time.temporal.TemporalAdjusters;

LocalDate today = LocalDate.of(2026, 3, 23); // 월요일

// 이번 달 첫 번째 날
LocalDate firstDay = today.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("이번 달 첫 날: " + firstDay); // 2026-03-01

// 이번 달 마지막 날
LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("이번 달 마지막 날: " + lastDay); // 2026-03-31

// 다음 월요일
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println("다음 월요일: " + nextMonday); // 2026-03-30

// 다음 금요일
LocalDate nextFriday = today.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("다음 금요일: " + nextFriday); // 2026-03-27

// 이번 달 첫 번째 금요일
LocalDate firstFriday = today.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY));
System.out.println("이번 달 첫 번째 금요일: " + firstFriday); // 2026-03-06

// 다음 해 첫 번째 날
LocalDate nextYearFirst = today.with(TemporalAdjusters.firstDayOfNextYear());
System.out.println("내년 첫 날: " + nextYearFirst); // 2027-01-01

// 이번 달의 세 번째 화요일
LocalDate thirdTuesday = today.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.TUESDAY));
System.out.println("이번 달 세 번째 화요일: " + thirdTuesday);

6. 실전 예제: D-Day 계산기 & 근무 시간 계산

D-Day 계산기

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.format.DateTimeFormatter;

public class DDayCalculator {

public static void printDDay(String eventName, LocalDate targetDate) {
LocalDate today = LocalDate.now();
long days = ChronoUnit.DAYS.between(today, targetDate);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");

if (days > 0) {
System.out.printf("%-15s (%s) → D-%d%n",
eventName, targetDate.format(fmt), days);
} else if (days == 0) {
System.out.printf("%-15s (%s) → D-DAY! 오늘입니다!%n",
eventName, targetDate.format(fmt));
} else {
System.out.printf("%-15s (%s) → D+%d (이미 지났습니다)%n",
eventName, targetDate.format(fmt), Math.abs(days));
}
}

public static void main(String[] args) {
System.out.println("=== D-Day 계산기 ===");
printDDay("크리스마스", LocalDate.of(2026, 12, 25));
printDDay("새해", LocalDate.of(2027, 1, 1));
printDDay("수능", LocalDate.of(2026, 11, 19));
printDDay("졸업식", LocalDate.of(2025, 2, 28));

// 생일까지 남은 날
LocalDate birthday = LocalDate.of(1995, 8, 20);
LocalDate today = LocalDate.now();
LocalDate thisYearBirthday = birthday.withYear(today.getYear());

// 이미 지난 경우 다음 해 생일 계산
if (thisYearBirthday.isBefore(today) || thisYearBirthday.isEqual(today)) {
thisYearBirthday = thisYearBirthday.plusYears(1);
}
printDDay("내 생일", thisYearBirthday);
}
}

[실행 결과 예시]

=== D-Day 계산기 ===
크리스마스 (2026년 12월 25일) → D-276
새해 (2027년 01월 01일) → D-283
수능 (2026년 11월 19일) → D-241
졸업식 (2025년 02월 28일) → D+388 (이미 지났습니다)
내 생일 (2026년 08월 20일) → D-150

근무 시간 계산기

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.DayOfWeek;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

public class WorkTimeCalculator {

record WorkRecord(LocalDate date, LocalTime start, LocalTime end) {
Duration workDuration() {
Duration d = Duration.between(start, end);
// 점심시간 1시간 제외
return d.minusHours(1);
}

boolean isWeekend() {
DayOfWeek dow = date.getDayOfWeek();
return dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY;
}
}

public static void main(String[] args) {
List<WorkRecord> records = new ArrayList<>();
records.add(new WorkRecord(LocalDate.of(2026, 3, 16),
LocalTime.of(9, 0), LocalTime.of(18, 30)));
records.add(new WorkRecord(LocalDate.of(2026, 3, 17),
LocalTime.of(9, 0), LocalTime.of(19, 0)));
records.add(new WorkRecord(LocalDate.of(2026, 3, 18),
LocalTime.of(8, 30), LocalTime.of(18, 0)));
records.add(new WorkRecord(LocalDate.of(2026, 3, 19),
LocalTime.of(9, 0), LocalTime.of(17, 30)));
records.add(new WorkRecord(LocalDate.of(2026, 3, 20),
LocalTime.of(9, 0), LocalTime.of(20, 0)));

DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("MM월 dd일(E)");
System.out.println("=== 주간 근무 시간 보고서 ===");
System.out.printf("%-20s %-10s %-10s %-15s%n",
"날짜", "출근", "퇴근", "근무 시간");
System.out.println("-".repeat(60));

Duration totalWork = Duration.ZERO;

for (WorkRecord r : records) {
Duration work = r.workDuration();
totalWork = totalWork.plus(work);

long h = work.toHours();
long m = work.toMinutesPart();

System.out.printf("%-20s %-10s %-10s %d시간 %d분%n",
r.date().format(dateFmt),
r.start().toString(),
r.end().toString(),
h, m);
}

System.out.println("-".repeat(60));
System.out.printf("총 근무 시간: %d시간 %d분%n",
totalWork.toHours(), totalWork.toMinutesPart());

double totalHoursDouble = totalWork.toMinutes() / 60.0;
System.out.printf("법정 근무 시간(40h) 대비: %.1f시간 %s%n",
totalHoursDouble,
totalHoursDouble > 40 ? "(+" + String.format("%.1f", totalHoursDouble - 40) + "h 초과)" : "(정상)");
}
}

[실행 결과]

=== 주간 근무 시간 보고서 ===
날짜 출근 퇴근 근무 시간
------------------------------------------------------------
03월 16일(월) 09:00 18:30 8시간 30분
03월 17일(화) 09:00 19:00 9시간 0분
03월 18일(수) 08:30 18:00 8시간 30분
03월 19일(목) 09:00 17:30 7시간 30분
03월 20일(금) 09:00 20:00 10시간 0분
------------------------------------------------------------
총 근무 시간: 43시간 30분
법정 근무 시간(40h) 대비: 43.5시간 (+3.5h 초과)

마치며

날짜/시간 간격 계산을 정리하면 다음과 같습니다.

상황사용 도구예시
날짜 간격 (년/월/일 분리)Period.between()만 나이 계산
시간 간격 (초/분/시간)Duration.between()근무 시간 계산
특정 단위 총 차이ChronoUnit.XXX.between()총 며칠인지
복잡한 날짜 조정TemporalAdjusters다음 월요일, 월말
날짜 더하기/빼기plus/minus 메서드30일 후, 3개월 전
  • Period 는 날짜(Date) 전용, Duration 은 시간(Time) 전용입니다.
  • ChronoUnit 은 "총 며칠 차이?"처럼 단일 단위 계산에 가장 직관적입니다.
  • TemporalAdjusters 를 활용하면 "이번 달 마지막 날" 같은 복잡한 날짜 계산도 한 줄로 해결됩니다.